Skip to content

Commit 8adbf85

Browse files
committed
Candidate sectional sourcemap emit implementation
1 parent 2cb4640 commit 8adbf85

9 files changed

Lines changed: 133 additions & 20 deletions

File tree

src/compiler/factory.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2585,16 +2585,19 @@ namespace ts {
25852585
return node;
25862586
}
25872587

2588-
export function createUnparsedSourceFile(text: string): UnparsedSource {
2588+
export function createUnparsedSourceFile(text: string, map?: string): UnparsedSource {
25892589
const node = <UnparsedSource>createNode(SyntaxKind.UnparsedSource);
25902590
node.text = text;
2591+
node.sourceMapText = map;
25912592
return node;
25922593
}
25932594

2594-
export function createInputFiles(javascript: string, declaration: string): InputFiles {
2595+
export function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles {
25952596
const node = <InputFiles>createNode(SyntaxKind.InputFiles);
25962597
node.javascriptText = javascript;
2598+
node.javascriptMapText = javascriptMapText;
25972599
node.declarationText = declaration;
2600+
node.declarationMapText = declarationMapText;
25982601
return node;
25992602
}
26002603

src/compiler/program.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,8 +1192,10 @@ namespace ts {
11921192

11931193
const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts");
11941194
const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`;
1195+
const jsMap = host.readFile(resolvedRefOpts.options.outFile + ".map"); // TODO: try to read sourceMappingUrl comment from the js file
11951196
const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`;
1196-
const node = createInputFiles(js, dts);
1197+
const dtsMap = host.readFile(dtsFilename + ".map");
1198+
const node = createInputFiles(js, dts, jsMap, dtsMap);
11971199
nodes.push(node);
11981200
}
11991201
}

src/compiler/sourcemap.ts

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ 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+
102106
return {
103107
initialize,
104108
reset,
@@ -146,6 +150,9 @@ namespace ts {
146150
lastEncodedNameIndex = 0;
147151

148152
// Initialize source map data
153+
completedSections = [];
154+
sectionStartLine = 0;
155+
sectionStartColumn = 0;
149156
sourceMapData = {
150157
sourceMapFilePath,
151158
jsSourceMappingURL: !compilerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined!, // TODO: GH#18217
@@ -214,6 +221,68 @@ namespace ts {
214221
lastEncodedNameIndex = undefined;
215222
sourceMapData = undefined!;
216223
sourceMapDataList = undefined!;
224+
completedSections = undefined!;
225+
sectionStartLine = undefined!;
226+
sectionStartColumn = undefined!;
227+
}
228+
229+
interface SourceMapSection {
230+
version: 3;
231+
file: string;
232+
sourceRoot?: string;
233+
sources: string[];
234+
names?: string[];
235+
mappings: string;
236+
sourcesContent?: string[];
237+
sections?: undefined;
238+
}
239+
240+
type SourceMapSectionDefinition =
241+
| { offset: { line: number, column: number }, url: string } // Included for completeness
242+
| { offset: { line: number, column: number }, map: SourceMap };
243+
244+
interface SectionalSourceMap {
245+
version: 3;
246+
file: string;
247+
sections: SourceMapSectionDefinition[];
248+
}
249+
250+
type SourceMap = SectionalSourceMap | SourceMapSection;
251+
252+
function captureSection(): SourceMapSection {
253+
return {
254+
version: 3,
255+
file: sourceMapData.sourceMapFile,
256+
sourceRoot: sourceMapData.sourceMapSourceRoot,
257+
sources: sourceMapData.sourceMapSources,
258+
names: sourceMapData.sourceMapNames,
259+
mappings: sourceMapData.sourceMapMappings,
260+
sourcesContent: sourceMapData.sourceMapSourcesContent,
261+
};
262+
}
263+
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+
const last = {
274+
offset: { line: sectionStartLine, column: sectionStartColumn },
275+
map: captureSection()
276+
};
277+
return {
278+
version: 3,
279+
file: last.map.file,
280+
sections: [...completedSections, last]
281+
};
282+
}
283+
else {
284+
return captureSection();
285+
}
217286
}
218287

219288
// Encoding for sourcemap span
@@ -284,8 +353,8 @@ namespace ts {
284353
sourceLinePos.line++;
285354
sourceLinePos.character++;
286355

287-
const emittedLine = writer.getLine();
288-
const emittedColumn = writer.getColumn();
356+
const emittedLine = writer.getLine() - sectionStartLine;
357+
const emittedColumn = emittedLine === 0 ? writer.getColumn() - sectionStartColumn : writer.getColumn();
289358

290359
// If this location wasn't recorded or the location in source is going backwards, record the span
291360
if (!lastRecordedSourceMapSpan ||
@@ -333,6 +402,38 @@ namespace ts {
333402
}
334403

335404
if (node) {
405+
if (isUnparsedSource(node) && node.sourceMapText !== undefined) {
406+
if (lastRecordedSourceMapSpan && lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) { // If we've recorded some spans, save them
407+
completedSections.push({ offset: { line: sectionStartLine, column: sectionStartColumn }, map: captureSection() });
408+
resetSectionalData();
409+
}
410+
const text = node.sourceMapText;
411+
let parsed: {} | undefined;
412+
try {
413+
parsed = JSON.parse(text);
414+
}
415+
catch {
416+
// empty
417+
}
418+
const offset = { line: writer.getLine(), column: writer.getColumn() };
419+
completedSections.push(parsed
420+
? {
421+
offset,
422+
map: parsed as SourceMap
423+
}
424+
: {
425+
offset,
426+
// This is just passes the buck on sourcemaps we don't really understand, instead of issuing an error (which would be difficult this late)
427+
url: `data:application/json;charset=utf-8;base64,${base64encode(sys, text)}`
428+
}
429+
);
430+
const emitResult = emitCallback(hint, node);
431+
sectionStartLine = writer.getLine();
432+
sectionStartColumn = writer.getColumn();
433+
lastRecordedSourceMapSpan = undefined!;
434+
lastEncodedSourceMapSpan = undefined!;
435+
return emitResult;
436+
}
336437
const emitNode = node.emitNode;
337438
const emitFlags = emitNode && emitNode.flags || EmitFlags.None;
338439
const range = emitNode && emitNode.sourceMapRange;
@@ -452,15 +553,7 @@ namespace ts {
452553

453554
encodeLastRecordedSourceMapSpan();
454555

455-
return JSON.stringify({
456-
version: 3,
457-
file: sourceMapData.sourceMapFile,
458-
sourceRoot: sourceMapData.sourceMapSourceRoot,
459-
sources: sourceMapData.sourceMapSources,
460-
names: sourceMapData.sourceMapNames,
461-
mappings: sourceMapData.sourceMapMappings,
462-
sourcesContent: sourceMapData.sourceMapSourcesContent,
463-
});
556+
return JSON.stringify(generateMap());
464557
}
465558

466559
/**

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);
183+
return createUnparsedSourceFile(prepend.declarationText, 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);
103+
return createUnparsedSourceFile(prepend.javascriptText, prepend.javascriptMapText);
104104
}
105105
return prepend;
106106
}));

src/compiler/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,12 +2652,15 @@ namespace ts {
26522652
export interface InputFiles extends Node {
26532653
kind: SyntaxKind.InputFiles;
26542654
javascriptText: string;
2655+
javascriptMapText?: string;
26552656
declarationText: string;
2657+
declarationMapText?: string;
26562658
}
26572659

26582660
export interface UnparsedSource extends Node {
26592661
kind: SyntaxKind.UnparsedSource;
26602662
text: string;
2663+
sourceMapText?: string;
26612664
}
26622665

26632666
export interface JsonSourceFile extends SourceFile {

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5454,6 +5454,10 @@ namespace ts {
54545454
return node.kind === SyntaxKind.Bundle;
54555455
}
54565456

5457+
export function isUnparsedSource(node: Node): node is UnparsedSource {
5458+
return node.kind === SyntaxKind.UnparsedSource;
5459+
}
5460+
54575461
// JSDoc
54585462

54595463
export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,11 +1669,14 @@ declare namespace ts {
16691669
interface InputFiles extends Node {
16701670
kind: SyntaxKind.InputFiles;
16711671
javascriptText: string;
1672+
javascriptMapText?: string;
16721673
declarationText: string;
1674+
declarationMapText?: string;
16731675
}
16741676
interface UnparsedSource extends Node {
16751677
kind: SyntaxKind.UnparsedSource;
16761678
text: string;
1679+
sourceMapText?: string;
16771680
}
16781681
interface JsonSourceFile extends SourceFile {
16791682
statements: NodeArray<JsonObjectExpressionStatement>;
@@ -3371,6 +3374,7 @@ declare namespace ts {
33713374
function isEnumMember(node: Node): node is EnumMember;
33723375
function isSourceFile(node: Node): node is SourceFile;
33733376
function isBundle(node: Node): node is Bundle;
3377+
function isUnparsedSource(node: Node): node is UnparsedSource;
33743378
function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression;
33753379
function isJSDocAllType(node: JSDocAllType): node is JSDocAllType;
33763380
function isJSDocUnknownType(node: Node): node is JSDocUnknownType;
@@ -3826,8 +3830,8 @@ declare namespace ts {
38263830
function createCommaList(elements: ReadonlyArray<Expression>): CommaListExpression;
38273831
function updateCommaList(node: CommaListExpression, elements: ReadonlyArray<Expression>): CommaListExpression;
38283832
function createBundle(sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource | InputFiles>): Bundle;
3829-
function createUnparsedSourceFile(text: string): UnparsedSource;
3830-
function createInputFiles(javascript: string, declaration: string): InputFiles;
3833+
function createUnparsedSourceFile(text: string, map?: string): UnparsedSource;
3834+
function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles;
38313835
function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource>): Bundle;
38323836
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>): CallExpression;
38333837
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>, param: ParameterDeclaration, paramValue: Expression): CallExpression;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,11 +1669,14 @@ declare namespace ts {
16691669
interface InputFiles extends Node {
16701670
kind: SyntaxKind.InputFiles;
16711671
javascriptText: string;
1672+
javascriptMapText?: string;
16721673
declarationText: string;
1674+
declarationMapText?: string;
16731675
}
16741676
interface UnparsedSource extends Node {
16751677
kind: SyntaxKind.UnparsedSource;
16761678
text: string;
1679+
sourceMapText?: string;
16771680
}
16781681
interface JsonSourceFile extends SourceFile {
16791682
statements: NodeArray<JsonObjectExpressionStatement>;
@@ -3371,6 +3374,7 @@ declare namespace ts {
33713374
function isEnumMember(node: Node): node is EnumMember;
33723375
function isSourceFile(node: Node): node is SourceFile;
33733376
function isBundle(node: Node): node is Bundle;
3377+
function isUnparsedSource(node: Node): node is UnparsedSource;
33743378
function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression;
33753379
function isJSDocAllType(node: JSDocAllType): node is JSDocAllType;
33763380
function isJSDocUnknownType(node: Node): node is JSDocUnknownType;
@@ -3826,8 +3830,8 @@ declare namespace ts {
38263830
function createCommaList(elements: ReadonlyArray<Expression>): CommaListExpression;
38273831
function updateCommaList(node: CommaListExpression, elements: ReadonlyArray<Expression>): CommaListExpression;
38283832
function createBundle(sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource | InputFiles>): Bundle;
3829-
function createUnparsedSourceFile(text: string): UnparsedSource;
3830-
function createInputFiles(javascript: string, declaration: string): InputFiles;
3833+
function createUnparsedSourceFile(text: string, map?: string): UnparsedSource;
3834+
function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles;
38313835
function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource>): Bundle;
38323836
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>): CallExpression;
38333837
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>, param: ParameterDeclaration, paramValue: Expression): CallExpression;

0 commit comments

Comments
 (0)