Candidate sectional sourcemap emit implementation

This commit is contained in:
Wesley Wigham 2018-05-30 17:53:26 -07:00
parent 2cb46407b1
commit 8adbf85e0e
No known key found for this signature in database
GPG Key ID: D59F87F60C5400C9
9 changed files with 133 additions and 20 deletions

View File

@ -2585,16 +2585,19 @@ namespace ts {
return node;
}
export function createUnparsedSourceFile(text: string): UnparsedSource {
export function createUnparsedSourceFile(text: string, map?: string): UnparsedSource {
const node = <UnparsedSource>createNode(SyntaxKind.UnparsedSource);
node.text = text;
node.sourceMapText = map;
return node;
}
export function createInputFiles(javascript: string, declaration: string): InputFiles {
export function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles {
const node = <InputFiles>createNode(SyntaxKind.InputFiles);
node.javascriptText = javascript;
node.javascriptMapText = javascriptMapText;
node.declarationText = declaration;
node.declarationMapText = declarationMapText;
return node;
}

View File

@ -1192,8 +1192,10 @@ namespace ts {
const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts");
const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`;
const jsMap = host.readFile(resolvedRefOpts.options.outFile + ".map"); // TODO: try to read sourceMappingUrl comment from the js file
const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`;
const node = createInputFiles(js, dts);
const dtsMap = host.readFile(dtsFilename + ".map");
const node = createInputFiles(js, dts, jsMap, dtsMap);
nodes.push(node);
}
}

View File

@ -99,6 +99,10 @@ namespace ts {
let sourceMapDataList: SourceMapData[] | undefined;
let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap);
let completedSections: SourceMapSectionDefinition[];
let sectionStartLine: number;
let sectionStartColumn: number;
return {
initialize,
reset,
@ -146,6 +150,9 @@ namespace ts {
lastEncodedNameIndex = 0;
// Initialize source map data
completedSections = [];
sectionStartLine = 0;
sectionStartColumn = 0;
sourceMapData = {
sourceMapFilePath,
jsSourceMappingURL: !compilerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined!, // TODO: GH#18217
@ -214,6 +221,68 @@ namespace ts {
lastEncodedNameIndex = undefined;
sourceMapData = undefined!;
sourceMapDataList = undefined!;
completedSections = undefined!;
sectionStartLine = undefined!;
sectionStartColumn = undefined!;
}
interface SourceMapSection {
version: 3;
file: string;
sourceRoot?: string;
sources: string[];
names?: string[];
mappings: string;
sourcesContent?: string[];
sections?: undefined;
}
type SourceMapSectionDefinition =
| { offset: { line: number, column: number }, url: string } // Included for completeness
| { offset: { line: number, column: number }, map: SourceMap };
interface SectionalSourceMap {
version: 3;
file: string;
sections: SourceMapSectionDefinition[];
}
type SourceMap = SectionalSourceMap | SourceMapSection;
function captureSection(): SourceMapSection {
return {
version: 3,
file: sourceMapData.sourceMapFile,
sourceRoot: sourceMapData.sourceMapSourceRoot,
sources: sourceMapData.sourceMapSources,
names: sourceMapData.sourceMapNames,
mappings: sourceMapData.sourceMapMappings,
sourcesContent: sourceMapData.sourceMapSourcesContent,
};
}
function resetSectionalData(): void {
sourceMapData.sourceMapSources = [];
sourceMapData.sourceMapNames = [];
sourceMapData.sourceMapMappings = "";
sourceMapData.sourceMapSourcesContent = compilerOptions.inlineSources ? [] : undefined;
}
function generateMap(): SourceMap {
if (completedSections.length) {
const last = {
offset: { line: sectionStartLine, column: sectionStartColumn },
map: captureSection()
};
return {
version: 3,
file: last.map.file,
sections: [...completedSections, last]
};
}
else {
return captureSection();
}
}
// Encoding for sourcemap span
@ -284,8 +353,8 @@ namespace ts {
sourceLinePos.line++;
sourceLinePos.character++;
const emittedLine = writer.getLine();
const emittedColumn = writer.getColumn();
const emittedLine = writer.getLine() - sectionStartLine;
const emittedColumn = emittedLine === 0 ? writer.getColumn() - sectionStartColumn : writer.getColumn();
// If this location wasn't recorded or the location in source is going backwards, record the span
if (!lastRecordedSourceMapSpan ||
@ -333,6 +402,38 @@ namespace ts {
}
if (node) {
if (isUnparsedSource(node) && node.sourceMapText !== undefined) {
if (lastRecordedSourceMapSpan && lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) { // If we've recorded some spans, save them
completedSections.push({ offset: { line: sectionStartLine, column: sectionStartColumn }, map: captureSection() });
resetSectionalData();
}
const text = node.sourceMapText;
let parsed: {} | undefined;
try {
parsed = JSON.parse(text);
}
catch {
// empty
}
const offset = { line: writer.getLine(), column: writer.getColumn() };
completedSections.push(parsed
? {
offset,
map: parsed as SourceMap
}
: {
offset,
// This is just passes the buck on sourcemaps we don't really understand, instead of issuing an error (which would be difficult this late)
url: `data:application/json;charset=utf-8;base64,${base64encode(sys, text)}`
}
);
const emitResult = emitCallback(hint, node);
sectionStartLine = writer.getLine();
sectionStartColumn = writer.getColumn();
lastRecordedSourceMapSpan = undefined!;
lastEncodedSourceMapSpan = undefined!;
return emitResult;
}
const emitNode = node.emitNode;
const emitFlags = emitNode && emitNode.flags || EmitFlags.None;
const range = emitNode && emitNode.sourceMapRange;
@ -452,15 +553,7 @@ namespace ts {
encodeLastRecordedSourceMapSpan();
return JSON.stringify({
version: 3,
file: sourceMapData.sourceMapFile,
sourceRoot: sourceMapData.sourceMapSourceRoot,
sources: sourceMapData.sourceMapSources,
names: sourceMapData.sourceMapNames,
mappings: sourceMapData.sourceMapMappings,
sourcesContent: sourceMapData.sourceMapSourcesContent,
});
return JSON.stringify(generateMap());
}
/**

View File

@ -180,7 +180,7 @@ namespace ts {
}
), mapDefined(node.prepends, prepend => {
if (prepend.kind === SyntaxKind.InputFiles) {
return createUnparsedSourceFile(prepend.declarationText);
return createUnparsedSourceFile(prepend.declarationText, prepend.declarationMapText);
}
}));
bundle.syntheticFileReferences = [];

View File

@ -100,7 +100,7 @@ namespace ts {
function transformBundle(node: Bundle) {
return createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => {
if (prepend.kind === SyntaxKind.InputFiles) {
return createUnparsedSourceFile(prepend.javascriptText);
return createUnparsedSourceFile(prepend.javascriptText, prepend.javascriptMapText);
}
return prepend;
}));

View File

@ -2652,12 +2652,15 @@ namespace ts {
export interface InputFiles extends Node {
kind: SyntaxKind.InputFiles;
javascriptText: string;
javascriptMapText?: string;
declarationText: string;
declarationMapText?: string;
}
export interface UnparsedSource extends Node {
kind: SyntaxKind.UnparsedSource;
text: string;
sourceMapText?: string;
}
export interface JsonSourceFile extends SourceFile {

View File

@ -5454,6 +5454,10 @@ namespace ts {
return node.kind === SyntaxKind.Bundle;
}
export function isUnparsedSource(node: Node): node is UnparsedSource {
return node.kind === SyntaxKind.UnparsedSource;
}
// JSDoc
export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression {

View File

@ -1669,11 +1669,14 @@ declare namespace ts {
interface InputFiles extends Node {
kind: SyntaxKind.InputFiles;
javascriptText: string;
javascriptMapText?: string;
declarationText: string;
declarationMapText?: string;
}
interface UnparsedSource extends Node {
kind: SyntaxKind.UnparsedSource;
text: string;
sourceMapText?: string;
}
interface JsonSourceFile extends SourceFile {
statements: NodeArray<JsonObjectExpressionStatement>;
@ -3371,6 +3374,7 @@ declare namespace ts {
function isEnumMember(node: Node): node is EnumMember;
function isSourceFile(node: Node): node is SourceFile;
function isBundle(node: Node): node is Bundle;
function isUnparsedSource(node: Node): node is UnparsedSource;
function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression;
function isJSDocAllType(node: JSDocAllType): node is JSDocAllType;
function isJSDocUnknownType(node: Node): node is JSDocUnknownType;
@ -3826,8 +3830,8 @@ declare namespace ts {
function createCommaList(elements: ReadonlyArray<Expression>): CommaListExpression;
function updateCommaList(node: CommaListExpression, elements: ReadonlyArray<Expression>): CommaListExpression;
function createBundle(sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource | InputFiles>): Bundle;
function createUnparsedSourceFile(text: string): UnparsedSource;
function createInputFiles(javascript: string, declaration: string): InputFiles;
function createUnparsedSourceFile(text: string, map?: string): UnparsedSource;
function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles;
function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource>): Bundle;
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>): CallExpression;
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>, param: ParameterDeclaration, paramValue: Expression): CallExpression;

View File

@ -1669,11 +1669,14 @@ declare namespace ts {
interface InputFiles extends Node {
kind: SyntaxKind.InputFiles;
javascriptText: string;
javascriptMapText?: string;
declarationText: string;
declarationMapText?: string;
}
interface UnparsedSource extends Node {
kind: SyntaxKind.UnparsedSource;
text: string;
sourceMapText?: string;
}
interface JsonSourceFile extends SourceFile {
statements: NodeArray<JsonObjectExpressionStatement>;
@ -3371,6 +3374,7 @@ declare namespace ts {
function isEnumMember(node: Node): node is EnumMember;
function isSourceFile(node: Node): node is SourceFile;
function isBundle(node: Node): node is Bundle;
function isUnparsedSource(node: Node): node is UnparsedSource;
function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression;
function isJSDocAllType(node: JSDocAllType): node is JSDocAllType;
function isJSDocUnknownType(node: Node): node is JSDocUnknownType;
@ -3826,8 +3830,8 @@ declare namespace ts {
function createCommaList(elements: ReadonlyArray<Expression>): CommaListExpression;
function updateCommaList(node: CommaListExpression, elements: ReadonlyArray<Expression>): CommaListExpression;
function createBundle(sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource | InputFiles>): Bundle;
function createUnparsedSourceFile(text: string): UnparsedSource;
function createInputFiles(javascript: string, declaration: string): InputFiles;
function createUnparsedSourceFile(text: string, map?: string): UnparsedSource;
function createInputFiles(javascript: string, declaration: string, javascriptMapText?: string, declarationMapText?: string): InputFiles;
function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends?: ReadonlyArray<UnparsedSource>): Bundle;
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>): CallExpression;
function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray<Statement>, param: ParameterDeclaration, paramValue: Expression): CallExpression;