Skip to content

Commit 2a15036

Browse files
authored
Stop generating sectional sourcemaps (microsoft#24917)
* Generate normal 1-part sourcemaps (at increased processing cost), since tools dislike sectional ones * Add semicolon * Accept sad baselines] * Forward along sourcesContent if available * Supress lint since the API actually calls for null here * Fix concatenated sourcemap paths * Accept bad baselines :( * Add overloads * Accept api update * Fix lint
1 parent bb9e059 commit 2a15036

File tree

14 files changed

+263
-210
lines changed

14 files changed

+263
-210
lines changed

src/compiler/factory.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2587,18 +2587,41 @@ namespace ts {
25872587
return node;
25882588
}
25892589

2590-
export function createUnparsedSourceFile(text: string, map?: string): UnparsedSource {
2590+
export function createUnparsedSourceFile(text: string): UnparsedSource;
2591+
export function createUnparsedSourceFile(text: string, mapPath: string | undefined, map: string | undefined): UnparsedSource;
2592+
export function createUnparsedSourceFile(text: string, mapPath?: string, map?: string): UnparsedSource {
25912593
const node = <UnparsedSource>createNode(SyntaxKind.UnparsedSource);
25922594
node.text = text;
2595+
node.sourceMapPath = mapPath;
25932596
node.sourceMapText = map;
25942597
return node;
25952598
}
2596-
2597-
export function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles {
2599+
export function createInputFiles(
2600+
javascript: string,
2601+
declaration: string
2602+
): InputFiles;
2603+
export function createInputFiles(
2604+
javascript: string,
2605+
declaration: string,
2606+
javascriptMapPath: string | undefined,
2607+
javascriptMapText: string | undefined,
2608+
declarationMapPath: string | undefined,
2609+
declarationMapText: string | undefined
2610+
): InputFiles;
2611+
export function createInputFiles(
2612+
javascript: string,
2613+
declaration: string,
2614+
javascriptMapPath?: string,
2615+
javascriptMapText?: string,
2616+
declarationMapPath?: string,
2617+
declarationMapText?: string
2618+
): InputFiles {
25982619
const node = <InputFiles>createNode(SyntaxKind.InputFiles);
25992620
node.javascriptText = javascript;
2621+
node.javascriptMapPath = javascriptMapPath;
26002622
node.javascriptMapText = javascriptMapText;
26012623
node.declarationText = declaration;
2624+
node.declarationMapPath = declarationMapPath;
26022625
node.declarationMapText = declarationMapText;
26032626
return node;
26042627
}

src/compiler/program.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,10 +1240,12 @@ namespace ts {
12401240

12411241
const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts");
12421242
const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`;
1243-
const jsMap = host.readFile(resolvedRefOpts.options.outFile + ".map"); // TODO: try to read sourceMappingUrl comment from the js file
1243+
const jsMapPath = resolvedRefOpts.options.outFile + ".map"; // TODO: try to read sourceMappingUrl comment from the file
1244+
const jsMap = host.readFile(jsMapPath);
12441245
const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`;
1245-
const dtsMap = host.readFile(dtsFilename + ".map");
1246-
const node = createInputFiles(js, dts, jsMap, dtsMap);
1246+
const dtsMapPath = dtsFilename + ".map";
1247+
const dtsMap = host.readFile(dtsMapPath);
1248+
const node = createInputFiles(js, dts, jsMap && jsMapPath, jsMap, dtsMap && dtsMapPath, dtsMap);
12471249
nodes.push(node);
12481250
}
12491251
}

src/compiler/sourcemap.ts

Lines changed: 48 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,6 @@ namespace ts {
9999
let sourceMapDataList: SourceMapData[] | undefined;
100100
let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap);
101101

102-
let completedSections: SourceMapSectionDefinition[];
103-
let sectionStartLine: number;
104-
let sectionStartColumn: number;
105-
106102
return {
107103
initialize,
108104
reset,
@@ -150,9 +146,6 @@ namespace ts {
150146
lastEncodedNameIndex = 0;
151147

152148
// Initialize source map data
153-
completedSections = [];
154-
sectionStartLine = 1;
155-
sectionStartColumn = 1;
156149
sourceMapData = {
157150
sourceMapFilePath,
158151
jsSourceMappingURL: !compilerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined!, // TODO: GH#18217
@@ -221,9 +214,6 @@ namespace ts {
221214
lastEncodedNameIndex = undefined;
222215
sourceMapData = undefined!;
223216
sourceMapDataList = undefined!;
224-
completedSections = undefined!;
225-
sectionStartLine = undefined!;
226-
sectionStartColumn = undefined!;
227217
}
228218

229219
interface SourceMapSection {
@@ -233,7 +223,7 @@ namespace ts {
233223
sources: string[];
234224
names?: string[];
235225
mappings: string;
236-
sourcesContent?: string[];
226+
sourcesContent?: (string | null)[];
237227
sections?: undefined;
238228
}
239229

@@ -261,26 +251,6 @@ namespace ts {
261251
};
262252
}
263253

264-
function resetSectionalData(): void {
265-
sourceMapData.sourceMapSources = [];
266-
sourceMapData.sourceMapNames = [];
267-
sourceMapData.sourceMapMappings = "";
268-
sourceMapData.sourceMapSourcesContent = compilerOptions.inlineSources ? [] : undefined;
269-
}
270-
271-
function generateMap(): SourceMap {
272-
if (completedSections.length) {
273-
captureSectionalSpanIfNeeded(/*reset*/ false);
274-
return {
275-
version: 3,
276-
file: sourceMapData.sourceMapFile,
277-
sections: completedSections
278-
};
279-
}
280-
else {
281-
return captureSection();
282-
}
283-
}
284254

285255
// Encoding for sourcemap span
286256
function encodeLastRecordedSourceMapSpan() {
@@ -350,8 +320,8 @@ namespace ts {
350320
sourceLinePos.line++;
351321
sourceLinePos.character++;
352322

353-
const emittedLine = writer.getLine() - sectionStartLine + 1;
354-
const emittedColumn = emittedLine === 0 ? (writer.getColumn() - sectionStartColumn + 1) : writer.getColumn();
323+
const emittedLine = writer.getLine();
324+
const emittedColumn = writer.getColumn();
355325

356326
// If this location wasn't recorded or the location in source is going backwards, record the span
357327
if (!lastRecordedSourceMapSpan ||
@@ -386,13 +356,8 @@ namespace ts {
386356
}
387357
}
388358

389-
function captureSectionalSpanIfNeeded(reset: boolean) {
390-
if (lastRecordedSourceMapSpan && lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) { // If we've recorded some spans, save them
391-
completedSections.push({ offset: { line: sectionStartLine - 1, column: sectionStartColumn - 1 }, map: captureSection() });
392-
if (reset) {
393-
resetSectionalData();
394-
}
395-
}
359+
function isPossiblySourceMap(x: {}): x is SourceMapSection {
360+
return typeof x === "object" && !!(x as any).mappings && typeof (x as any).mappings === "string" && !!(x as any).sources;
396361
}
397362

398363
/**
@@ -409,7 +374,6 @@ namespace ts {
409374

410375
if (node) {
411376
if (isUnparsedSource(node) && node.sourceMapText !== undefined) {
412-
captureSectionalSpanIfNeeded(/*reset*/ true);
413377
const text = node.sourceMapText;
414378
let parsed: {} | undefined;
415379
try {
@@ -418,24 +382,41 @@ namespace ts {
418382
catch {
419383
// empty
420384
}
421-
const offset = { line: writer.getLine() - 1, column: writer.getColumn() - 1 };
422-
completedSections.push(parsed
423-
? {
424-
offset,
425-
map: parsed as SourceMap
426-
}
427-
: {
428-
offset,
429-
// This is just passes the buck on sourcemaps we don't really understand, instead of issuing an error (which would be difficult this late)
430-
url: `data:application/json;charset=utf-8;base64,${base64encode(sys, text)}`
431-
}
432-
);
433-
const emitResult = emitCallback(hint, node);
434-
sectionStartLine = writer.getLine();
435-
sectionStartColumn = writer.getColumn();
436-
lastRecordedSourceMapSpan = undefined!;
437-
lastEncodedSourceMapSpan = defaultLastEncodedSourceMapSpan;
438-
return emitResult;
385+
if (!parsed || !isPossiblySourceMap(parsed)) {
386+
return emitCallback(hint, node);
387+
}
388+
const offsetLine = writer.getLine();
389+
const firstLineColumnOffset = writer.getColumn();
390+
// First, decode the old component sourcemap
391+
const originalMap = parsed;
392+
sourcemaps.calculateDecodedMappings(originalMap, (raw): void => {
393+
// Apply offsets to each position and fixup source entries
394+
const rawPath = originalMap.sources[raw.sourceIndex];
395+
const relativePath = originalMap.sourceRoot ? combinePaths(originalMap.sourceRoot, rawPath) : rawPath;
396+
const combinedPath = combinePaths(getDirectoryPath(node.sourceMapPath!), relativePath);
397+
const sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir;
398+
const resolvedPath = getRelativePathToDirectoryOrUrl(
399+
sourcesDirectoryPath,
400+
combinedPath,
401+
host.getCurrentDirectory(),
402+
host.getCanonicalFileName,
403+
/*isAbsolutePathAnUrl*/ true
404+
);
405+
const absolutePath = toPath(resolvedPath, sourcesDirectoryPath, host.getCanonicalFileName);
406+
// tslint:disable-next-line:no-null-keyword
407+
setupSourceEntry(absolutePath, originalMap.sourcesContent ? originalMap.sourcesContent[raw.sourceIndex] : null); // TODO: Lookup content for inlining?
408+
const newIndex = sourceMapData.sourceMapSources.indexOf(resolvedPath);
409+
// Then reencode all the updated spans into the overall map
410+
encodeLastRecordedSourceMapSpan();
411+
lastRecordedSourceMapSpan = {
412+
...raw,
413+
emittedLine: raw.emittedLine + offsetLine - 1,
414+
emittedColumn: raw.emittedLine === 0 ? (raw.emittedColumn + firstLineColumnOffset - 1) : raw.emittedColumn,
415+
sourceIndex: newIndex,
416+
};
417+
});
418+
// And actually emit the text these sourcemaps are for
419+
return emitCallback(hint, node);
439420
}
440421
const emitNode = node.emitNode;
441422
const emitFlags = emitNode && emitNode.flags || EmitFlags.None;
@@ -529,13 +510,17 @@ namespace ts {
529510
return;
530511
}
531512

513+
setupSourceEntry(sourceFile.fileName, sourceFile.text);
514+
}
515+
516+
function setupSourceEntry(fileName: string, content: string | null) {
532517
// Add the file to tsFilePaths
533518
// If sourceroot option: Use the relative path corresponding to the common directory path
534519
// otherwise source locations relative to map file location
535520
const sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir;
536521

537522
const source = getRelativePathToDirectoryOrUrl(sourcesDirectoryPath,
538-
currentSource.fileName,
523+
fileName,
539524
host.getCurrentDirectory(),
540525
host.getCanonicalFileName,
541526
/*isAbsolutePathAnUrl*/ true);
@@ -546,10 +531,10 @@ namespace ts {
546531
sourceMapData.sourceMapSources.push(source);
547532

548533
// The one that can be used from program to get the actual source file
549-
sourceMapData.inputSourceFileNames.push(currentSource.fileName);
534+
sourceMapData.inputSourceFileNames.push(fileName);
550535

551536
if (compilerOptions.inlineSources) {
552-
sourceMapData.sourceMapSourcesContent!.push(currentSource.text);
537+
sourceMapData.sourceMapSourcesContent!.push(content);
553538
}
554539
}
555540
}
@@ -564,7 +549,7 @@ namespace ts {
564549

565550
encodeLastRecordedSourceMapSpan();
566551

567-
return JSON.stringify(generateMap());
552+
return JSON.stringify(captureSection());
568553
}
569554

570555
/**

src/services/sourcemaps.ts renamed to src/compiler/sourcemapDecoder.ts

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
1+
/* @internal */
2+
namespace ts {
3+
export interface SourceFileLikeCache {
4+
get(path: Path): SourceFileLike | undefined;
5+
}
6+
7+
export function createSourceFileLikeCache(host: { readFile?: (path: string) => string | undefined, fileExists?: (path: string) => boolean }): SourceFileLikeCache {
8+
const cached = createMap<SourceFileLike>();
9+
return {
10+
get(path: Path) {
11+
if (cached.has(path)) {
12+
return cached.get(path);
13+
}
14+
if (!host.fileExists || !host.readFile || !host.fileExists(path)) return;
15+
// And failing that, check the disk
16+
const text = host.readFile(path)!; // TODO: GH#18217
17+
const file = {
18+
text,
19+
lineMap: undefined,
20+
getLineAndCharacterOfPosition(pos: number) {
21+
return computeLineAndCharacterOfPosition(getLineStarts(this), pos);
22+
}
23+
} as SourceFileLike;
24+
cached.set(path, file);
25+
return file;
26+
}
27+
};
28+
}
29+
}
30+
131
/* @internal */
232
namespace ts.sourcemaps {
333
export interface SourceMapData {
434
version?: number;
535
file?: string;
636
sourceRoot?: string;
737
sources: string[];
8-
sourcesContent?: string[];
38+
sourcesContent?: (string | null)[];
939
names?: string[];
1040
mappings: string;
1141
}
@@ -86,7 +116,7 @@ namespace ts.sourcemaps {
86116
}
87117

88118
function getDecodedMappings() {
89-
return decodedMappings || (decodedMappings = calculateDecodedMappings());
119+
return decodedMappings || (decodedMappings = calculateDecodedMappings(map, processPosition, host));
90120
}
91121

92122
function getSourceOrderedMappings() {
@@ -97,30 +127,6 @@ namespace ts.sourcemaps {
97127
return generatedOrderedMappings || (generatedOrderedMappings = getDecodedMappings().slice().sort(compareProcessedPositionEmittedPositions));
98128
}
99129

100-
function calculateDecodedMappings(): ProcessedSourceMapPosition[] {
101-
const state: DecoderState<ProcessedSourceMapPosition> = {
102-
encodedText: map.mappings,
103-
currentNameIndex: undefined,
104-
sourceMapNamesLength: map.names ? map.names.length : undefined,
105-
currentEmittedColumn: 0,
106-
currentEmittedLine: 0,
107-
currentSourceColumn: 0,
108-
currentSourceLine: 0,
109-
currentSourceIndex: 0,
110-
positions: [],
111-
decodingIndex: 0,
112-
processPosition,
113-
};
114-
while (!hasCompletedDecoding(state)) {
115-
decodeSinglePosition(state);
116-
if (state.error) {
117-
host.log(`Encountered error while decoding sourcemap found at ${mapPath}: ${state.error}`);
118-
return [];
119-
}
120-
}
121-
return state.positions;
122-
}
123-
124130
function compareProcessedPositionSourcePositions(a: ProcessedSourceMapPosition, b: ProcessedSourceMapPosition) {
125131
return comparePaths(a.sourcePath, b.sourcePath, sourceRoot) ||
126132
compareValues(a.sourcePosition, b.sourcePosition);
@@ -142,6 +148,32 @@ namespace ts.sourcemaps {
142148
}
143149
}
144150

151+
export function calculateDecodedMappings<T>(map: SourceMapData, processPosition: (position: RawSourceMapPosition) => T, host?: { log?(s: string): void }): T[] {
152+
const state: DecoderState<T> = {
153+
encodedText: map.mappings,
154+
currentNameIndex: undefined,
155+
sourceMapNamesLength: map.names ? map.names.length : undefined,
156+
currentEmittedColumn: 0,
157+
currentEmittedLine: 0,
158+
currentSourceColumn: 0,
159+
currentSourceLine: 0,
160+
currentSourceIndex: 0,
161+
positions: [],
162+
decodingIndex: 0,
163+
processPosition,
164+
};
165+
while (!hasCompletedDecoding(state)) {
166+
decodeSinglePosition(state);
167+
if (state.error) {
168+
if (host && host.log) {
169+
host.log(`Encountered error while decoding sourcemap: ${state.error}`);
170+
}
171+
return [];
172+
}
173+
}
174+
return state.positions;
175+
}
176+
145177
interface ProcessedSourceMapPosition {
146178
emittedPosition: number;
147179
sourcePosition: number;

src/compiler/transformers/declarations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ namespace ts {
180180
}
181181
), mapDefined(node.prepends, prepend => {
182182
if (prepend.kind === SyntaxKind.InputFiles) {
183-
return createUnparsedSourceFile(prepend.declarationText, prepend.declarationMapText);
183+
return createUnparsedSourceFile(prepend.declarationText, prepend.declarationMapPath, prepend.declarationMapText);
184184
}
185185
}));
186186
bundle.syntheticFileReferences = [];

src/compiler/transformers/ts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ namespace ts {
100100
function transformBundle(node: Bundle) {
101101
return createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => {
102102
if (prepend.kind === SyntaxKind.InputFiles) {
103-
return createUnparsedSourceFile(prepend.javascriptText, prepend.javascriptMapText);
103+
return createUnparsedSourceFile(prepend.javascriptText, prepend.javascriptMapPath, prepend.javascriptMapText);
104104
}
105105
return prepend;
106106
}));

0 commit comments

Comments
 (0)