diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ca8f265130..f678cf4ffc1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3016,7 +3016,7 @@ namespace ts { const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true }); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); - printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, writer); // TODO: GH#18217 + printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonOmittingWriter(writer)); // TODO: GH#18217 return writer; } } diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 7dfda66c965..92423ac785c 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -155,13 +155,13 @@ namespace ts { writer.writeLine(); } else { - writer.write(" "); + writer.writeSpace(" "); } } function emitTrailingSynthesizedComment(comment: SynthesizedComment) { if (!writer.isAtStartOfLine()) { - writer.write(" "); + writer.writeSpace(" "); } writeSynthesizedComment(comment); if (comment.hasTrailingNewLine) { @@ -283,7 +283,7 @@ namespace ts { writer.writeLine(); } else if (kind === SyntaxKind.MultiLineCommentTrivia) { - writer.write(" "); + writer.writeSpace(" "); } } @@ -303,7 +303,7 @@ namespace ts { if (!shouldWriteComment(currentText, commentPos)) return; // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/ if (!writer.isAtStartOfLine()) { - writer.write(" "); + writer.writeSpace(" "); } if (emitPos) emitPos(commentPos); @@ -342,7 +342,7 @@ namespace ts { writer.writeLine(); } else { - writer.write(" "); + writer.writeSpace(" "); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 613fe7f5c84..e8e35f65f64 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -100,7 +100,7 @@ namespace ts { const sourceMapDataList: SourceMapData[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined; const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); - const newLine = host.getNewLine(); + const newLine = getNewLineCharacter(compilerOptions, () => host.getNewLine()); const writer = createTextWriter(newLine); const sourceMap = createSourceMapWriter(host, writer); const declarationSourceMap = createSourceMapWriter(host, writer, { @@ -161,8 +161,19 @@ namespace ts { // Transform the source files const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], transformers!, /*allowDtsFiles*/ false); + const printerOptions: PrinterOptions = { + removeComments: compilerOptions.removeComments, + newLine: compilerOptions.newLine, + noEmitHelpers: compilerOptions.noEmitHelpers, + module: compilerOptions.module, + target: compilerOptions.target, + sourceMap: compilerOptions.sourceMap, + inlineSourceMap: compilerOptions.inlineSourceMap, + extendedDiagnostics: compilerOptions.extendedDiagnostics, + }; + // Create a printer to print the nodes - const printer = createPrinter({ ...compilerOptions, noEmitHelpers: compilerOptions.noEmitHelpers } as PrinterOptions, { + const printer = createPrinter(printerOptions, { // resolver hooks hasGlobalName: resolver.hasGlobalName, @@ -205,7 +216,20 @@ namespace ts { emitterDiagnostics.add(diagnostic); } } - const declarationPrinter = createPrinter({ ...compilerOptions, onlyPrintJsDocStyle: true, noEmitHelpers: true } as PrinterOptions, { + + const printerOptions: PrinterOptions = { + removeComments: compilerOptions.removeComments, + newLine: compilerOptions.newLine, + noEmitHelpers: true, + module: compilerOptions.module, + target: compilerOptions.target, + sourceMap: compilerOptions.sourceMap, + inlineSourceMap: compilerOptions.inlineSourceMap, + extendedDiagnostics: compilerOptions.extendedDiagnostics, + onlyPrintJsDocStyle: true, + }; + + const declarationPrinter = createPrinter(printerOptions, { // resolver hooks hasGlobalName: resolver.hasGlobalName, @@ -259,16 +283,21 @@ namespace ts { printer.writeFile(sourceFile!, writer); } - writer.writeLine(); - const sourceMappingURL = mapRecorder.getSourceMappingURL(); if (sourceMappingURL) { - writer.write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment + if (!writer.isAtStartOfLine()) writer.rawWrite(newLine); + writer.writeComment(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Tools can sometimes see this line as a source mapping url comment + } + else { + writer.writeLine(); } // Write the source map if (sourceMapFilePath) { - writeFile(host, emitterDiagnostics, sourceMapFilePath, mapRecorder.getText(), /*writeByteOrderMark*/ false, sourceFiles); + const sourceMap = mapRecorder.getText(); + if (sourceMap) { + writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap, /*writeByteOrderMark*/ false, sourceFiles); + } } // Write the output file @@ -339,13 +368,6 @@ namespace ts { let writer: EmitTextWriter; let ownWriter: EmitTextWriter; let write = writeBase; - let commitPendingSemicolon: typeof commitPendingSemicolonInternal = noop; - let writeSemicolon: typeof writeSemicolonInternal = writeSemicolonInternal; - let pendingSemicolon = false; - if (printerOptions.omitTrailingSemicolon) { - commitPendingSemicolon = commitPendingSemicolonInternal; - writeSemicolon = deferWriteSemicolon; - } const syntheticParent: TextRange = { pos: -1, end: -1 }; const moduleKind = getEmitModuleKind(printerOptions); const bundledHelpers = createMap(); @@ -441,8 +463,8 @@ namespace ts { emitSyntheticTripleSlashReferencesIfNeeded(bundle); for (const prepend of bundle.prepends) { - print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); writeLine(); + print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); } if (bundleInfo) { @@ -503,6 +525,9 @@ namespace ts { } function setWriter(output: EmitTextWriter | undefined) { + if (output && printerOptions.omitTrailingSemicolon) { + output = getTrailingSemicolonOmittingWriter(output); + } writer = output!; // TODO: GH#18217 comments.setWriter(output!); } @@ -592,6 +617,10 @@ namespace ts { if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile)); if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier)); if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); + if (hint === EmitHint.EmbeddedStatement) { + Debug.assertNode(node, isEmptyStatement); + return emitEmptyStatement(/*isEmbeddedStatement*/ true); + } if (hint === EmitHint.Unspecified) { if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword); @@ -692,10 +721,10 @@ namespace ts { case SyntaxKind.ImportType: return emitImportTypeNode(node); case SyntaxKind.JSDocAllType: - write("*"); + writePunctuation("*"); return; case SyntaxKind.JSDocUnknownType: - write("?"); + writePunctuation("?"); return; case SyntaxKind.JSDocNullableType: return emitJSDocNullableType(node as JSDocNullableType); @@ -727,7 +756,7 @@ namespace ts { case SyntaxKind.VariableStatement: return emitVariableStatement(node); case SyntaxKind.EmptyStatement: - return emitEmptyStatement(); + return emitEmptyStatement(/*isEmbeddedStatement*/ false); case SyntaxKind.ExpressionStatement: return emitExpressionStatement(node); case SyntaxKind.IfStatement: @@ -1143,7 +1172,7 @@ namespace ts { emitNodeWithWriter(node.name, writeProperty); emit(node.questionToken); emitTypeAnnotation(node.type); - writeSemicolon(); + writeTrailingSemicolon(); } function emitPropertyDeclaration(node: PropertyDeclaration) { @@ -1154,7 +1183,7 @@ namespace ts { emit(node.exclamationToken); emitTypeAnnotation(node.type); emitInitializer(node.initializer, node.type ? node.type.end : node.questionToken ? node.questionToken.end : node.name.end, node); - writeSemicolon(); + writeTrailingSemicolon(); } function emitMethodSignature(node: MethodSignature) { @@ -1166,7 +1195,7 @@ namespace ts { emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitTypeAnnotation(node.type); - writeSemicolon(); + writeTrailingSemicolon(); popNameGenerationScope(node); } @@ -1201,7 +1230,7 @@ namespace ts { emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitTypeAnnotation(node.type); - writeSemicolon(); + writeTrailingSemicolon(); popNameGenerationScope(node); } @@ -1214,7 +1243,7 @@ namespace ts { emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitTypeAnnotation(node.type); - writeSemicolon(); + writeTrailingSemicolon(); popNameGenerationScope(node); } @@ -1223,11 +1252,11 @@ namespace ts { emitModifiers(node, node.modifiers); emitParametersForIndexSignature(node, node.parameters); emitTypeAnnotation(node.type); - writeSemicolon(); + writeTrailingSemicolon(); } function emitSemicolonClassElement() { - writeSemicolon(); + writeTrailingSemicolon(); } // @@ -1259,26 +1288,26 @@ namespace ts { } function emitJSDocFunctionType(node: JSDocFunctionType) { - write("function"); + writeKeyword("function"); emitParameters(node, node.parameters); - write(":"); + writePunctuation(":"); emit(node.type); } function emitJSDocNullableType(node: JSDocNullableType) { - write("?"); + writePunctuation("?"); emit(node.type); } function emitJSDocNonNullableType(node: JSDocNonNullableType) { - write("!"); + writePunctuation("!"); emit(node.type); } function emitJSDocOptionalType(node: JSDocOptionalType) { emit(node.type); - write("="); + writePunctuation("="); } function emitConstructorType(node: ConstructorTypeNode) { @@ -1314,7 +1343,7 @@ namespace ts { } function emitRestOrJSDocVariadicType(node: RestTypeNode | JSDocVariadicType) { - write("..."); + writePunctuation("..."); emit(node.type); } @@ -1326,7 +1355,7 @@ namespace ts { function emitOptionalType(node: OptionalTypeNode) { emit(node.type); - write("?"); + writePunctuation("?"); } function emitUnionType(node: UnionTypeNode) { @@ -1414,7 +1443,7 @@ namespace ts { writePunctuation(":"); writeSpace(); emit(node.type); - writeSemicolon(); + writeTrailingSemicolon(); if (emitFlags & EmitFlags.SingleLine) { writeSpace(); } @@ -1513,7 +1542,7 @@ namespace ts { } emitExpression(node.expression); - increaseIndentIf(indentBeforeDot); + increaseIndentIf(indentBeforeDot, /*writeSpaceIfNotIndenting*/ false); const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); if (shouldEmitDotDot) { @@ -1521,7 +1550,7 @@ namespace ts { } emitTokenWithComment(SyntaxKind.DotToken, node.expression.end, writePunctuation, node); - increaseIndentIf(indentAfterDot); + increaseIndentIf(indentAfterDot, /*writeSpaceIfNotIndenting*/ false); emit(node.name); decreaseIndentIf(indentBeforeDot, indentAfterDot); } @@ -1668,11 +1697,11 @@ namespace ts { const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); emitExpression(node.left); - increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); + increaseIndentIf(indentBeforeOperator, isCommaOperator); emitLeadingCommentsOfPosition(node.operatorToken.pos); - writeTokenNode(node.operatorToken, writeOperator); + writeTokenNode(node.operatorToken, node.operatorToken.kind === SyntaxKind.InKeyword ? writeKeyword : writeOperator); emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts - increaseIndentIf(indentAfterOperator, " "); + increaseIndentIf(indentAfterOperator, /*writeSpaceIfNotIndenting*/ true); emitExpression(node.right); decreaseIndentIf(indentBeforeOperator, indentAfterOperator); } @@ -1684,15 +1713,15 @@ namespace ts { const indentAfterColon = needsIndentation(node, node.colonToken, node.whenFalse); emitExpression(node.condition); - increaseIndentIf(indentBeforeQuestion, " "); + increaseIndentIf(indentBeforeQuestion, /*writeSpaceIfNotIndenting*/ true); emit(node.questionToken); - increaseIndentIf(indentAfterQuestion, " "); + increaseIndentIf(indentAfterQuestion, /*writeSpaceIfNotIndenting*/ true); emitExpression(node.whenTrue); decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion); - increaseIndentIf(indentBeforeColon, " "); + increaseIndentIf(indentBeforeColon, /*writeSpaceIfNotIndenting*/ true); emit(node.colonToken); - increaseIndentIf(indentAfterColon, " "); + increaseIndentIf(indentAfterColon, /*writeSpaceIfNotIndenting*/ true); emitExpression(node.whenFalse); decreaseIndentIf(indentBeforeColon, indentAfterColon); } @@ -1771,17 +1800,25 @@ namespace ts { function emitVariableStatement(node: VariableStatement) { emitModifiers(node, node.modifiers); emit(node.declarationList); - writeSemicolon(); + writeTrailingSemicolon(); } - function emitEmptyStatement() { - writeSemicolon(); + function emitEmptyStatement(isEmbeddedStatement: boolean) { + // While most trailing semicolons are possibly insignificant, an embedded "empty" + // statement is significant and cannot be elided by a trailing-semicolon-omitting writer. + if (isEmbeddedStatement) { + writePunctuation(";"); + } + else { + writeTrailingSemicolon(); + } } + function emitExpressionStatement(node: ExpressionStatement) { emitExpression(node.expression); if (!isJsonSourceFile(currentSourceFile)) { - writeSemicolon(); + writeTrailingSemicolon(); } } @@ -1837,9 +1874,9 @@ namespace ts { writeSpace(); let pos = emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, /*contextNode*/ node); emitForBinding(node.initializer); - pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.initializer ? node.initializer.end : pos, writeSemicolon, node); + pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.initializer ? node.initializer.end : pos, writePunctuation, node); emitExpressionWithLeadingSpace(node.condition); - pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.condition ? node.condition.end : pos, writeSemicolon, node); + pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.condition ? node.condition.end : pos, writePunctuation, node); emitExpressionWithLeadingSpace(node.incrementor); emitTokenWithComment(SyntaxKind.CloseParenToken, node.incrementor ? node.incrementor.end : pos, writePunctuation, node); emitEmbeddedStatement(node, node.statement); @@ -1886,13 +1923,13 @@ namespace ts { function emitContinueStatement(node: ContinueStatement) { emitTokenWithComment(SyntaxKind.ContinueKeyword, node.pos, writeKeyword, node); emitWithLeadingSpace(node.label); - writeSemicolon(); + writeTrailingSemicolon(); } function emitBreakStatement(node: BreakStatement) { emitTokenWithComment(SyntaxKind.BreakKeyword, node.pos, writeKeyword, node); emitWithLeadingSpace(node.label); - writeSemicolon(); + writeTrailingSemicolon(); } function emitTokenWithComment(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode: Node, indentLeading?: boolean) { @@ -1922,7 +1959,7 @@ namespace ts { function emitReturnStatement(node: ReturnStatement) { emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, writeKeyword, /*contextNode*/ node); emitExpressionWithLeadingSpace(node.expression); - writeSemicolon(); + writeTrailingSemicolon(); } function emitWithStatement(node: WithStatement) { @@ -1954,7 +1991,7 @@ namespace ts { function emitThrowStatement(node: ThrowStatement) { emitTokenWithComment(SyntaxKind.ThrowKeyword, node.pos, writeKeyword, node); emitExpressionWithLeadingSpace(node.expression); - writeSemicolon(); + writeTrailingSemicolon(); } function emitTryStatement(node: TryStatement) { @@ -1975,7 +2012,7 @@ namespace ts { function emitDebuggerStatement(node: DebuggerStatement) { writeToken(SyntaxKind.DebuggerKeyword, node.pos, writeKeyword); - writeSemicolon(); + writeTrailingSemicolon(); } // @@ -2046,7 +2083,7 @@ namespace ts { } else { emitSignatureHead(node); - writeSemicolon(); + writeTrailingSemicolon(); } } @@ -2192,7 +2229,7 @@ namespace ts { writePunctuation("="); writeSpace(); emit(node.type); - writeSemicolon(); + writeTrailingSemicolon(); } function emitEnumDeclaration(node: EnumDeclaration) { @@ -2216,7 +2253,7 @@ namespace ts { emit(node.name); let body = node.body; - if (!body) return writeSemicolon(); + if (!body) return writeTrailingSemicolon(); while (body.kind === SyntaxKind.ModuleDeclaration) { writePunctuation("."); emit((body).name); @@ -2249,7 +2286,7 @@ namespace ts { emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node); writeSpace(); emitModuleReference(node.moduleReference); - writeSemicolon(); + writeTrailingSemicolon(); } function emitModuleReference(node: ModuleReference) { @@ -2272,7 +2309,7 @@ namespace ts { writeSpace(); } emitExpression(node.moduleSpecifier); - writeSemicolon(); + writeTrailingSemicolon(); } function emitImportClause(node: ImportClause) { @@ -2311,7 +2348,7 @@ namespace ts { } writeSpace(); emitExpression(node.expression); - writeSemicolon(); + writeTrailingSemicolon(); } function emitExportDeclaration(node: ExportDeclaration) { @@ -2330,7 +2367,7 @@ namespace ts { writeSpace(); emitExpression(node.moduleSpecifier); } - writeSemicolon(); + writeTrailingSemicolon(); } function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) { @@ -2341,7 +2378,7 @@ namespace ts { nextPos = emitTokenWithComment(SyntaxKind.NamespaceKeyword, nextPos, writeKeyword, node); writeSpace(); emit(node.name); - writeSemicolon(); + writeTrailingSemicolon(); } function emitNamedExports(node: NamedExports) { @@ -2419,7 +2456,6 @@ namespace ts { } function emitJsxText(node: JsxText) { - commitPendingSemicolon(); writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); } @@ -2602,34 +2638,34 @@ namespace ts { function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: ReadonlyArray, types: ReadonlyArray, libs: ReadonlyArray) { if (hasNoDefaultLib) { - write(`/// `); + writeComment(`/// `); writeLine(); } if (currentSourceFile && currentSourceFile.moduleName) { - write(`/// `); + writeComment(`/// `); writeLine(); } if (currentSourceFile && currentSourceFile.amdDependencies) { for (const dep of currentSourceFile.amdDependencies) { if (dep.name) { - write(`/// `); + writeComment(`/// `); } else { - write(`/// `); + writeComment(`/// `); } writeLine(); } } for (const directive of files) { - write(`/// `); + writeComment(`/// `); writeLine(); } for (const directive of types) { - write(`/// `); + writeComment(`/// `); writeLine(); } for (const directive of libs) { - write(`/// `); + writeComment(`/// `); writeLine(); } } @@ -2701,7 +2737,7 @@ namespace ts { if (isSourceFile(sourceFileOrBundle)) { const shebang = getShebang(sourceFileOrBundle.text); if (shebang) { - write(shebang); + writeComment(shebang); writeLine(); return true; } @@ -2788,7 +2824,13 @@ namespace ts { else { writeLine(); increaseIndent(); - emit(node); + if (isEmptyStatement(node)) { + const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, EmitHint.EmbeddedStatement); + pipelinePhase(EmitHint.EmbeddedStatement, node); + } + else { + emit(node); + } decreaseIndent(); } } @@ -3024,83 +3066,63 @@ namespace ts { } } - function commitPendingSemicolonInternal() { - if (pendingSemicolon) { - writeSemicolonInternal(); - pendingSemicolon = false; - } - } - function writeLiteral(s: string) { - commitPendingSemicolon(); writer.writeLiteral(s); } function writeStringLiteral(s: string) { - commitPendingSemicolon(); writer.writeStringLiteral(s); } function writeBase(s: string) { - commitPendingSemicolon(); writer.write(s); } function writeSymbol(s: string, sym: Symbol) { - commitPendingSemicolon(); writer.writeSymbol(s, sym); } function writePunctuation(s: string) { - commitPendingSemicolon(); writer.writePunctuation(s); } - function deferWriteSemicolon() { - pendingSemicolon = true; - } - - function writeSemicolonInternal() { - writer.writePunctuation(";"); + function writeTrailingSemicolon() { + writer.writeTrailingSemicolon(";"); } function writeKeyword(s: string) { - commitPendingSemicolon(); writer.writeKeyword(s); } function writeOperator(s: string) { - commitPendingSemicolon(); writer.writeOperator(s); } function writeParameter(s: string) { - commitPendingSemicolon(); writer.writeParameter(s); } + function writeComment(s: string) { + writer.writeComment(s); + } + function writeSpace() { - commitPendingSemicolon(); writer.writeSpace(" "); } function writeProperty(s: string) { - commitPendingSemicolon(); writer.writeProperty(s); } function writeLine() { - commitPendingSemicolon(); writer.writeLine(); } function increaseIndent() { - commitPendingSemicolon(); writer.increaseIndent(); } function decreaseIndent() { - commitPendingSemicolon(); writer.decreaseIndent(); } @@ -3145,18 +3167,18 @@ namespace ts { if (line.length) { writeLine(); write(line); - writeLine(); + writer.rawWrite(newLine); } } } - function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { + function increaseIndentIf(value: boolean, writeSpaceIfNotIndenting: boolean) { if (value) { increaseIndent(); writeLine(); } - else if (valueToWriteWhenNotIndenting) { - write(valueToWriteWhenNotIndenting); + else if (writeSpaceIfNotIndenting) { + writeSpace(); } } @@ -3164,7 +3186,7 @@ namespace ts { // previous indent values to be considered at a time. This also allows caller to just // call this once, passing in all their appropriate indent values, instead of needing // to call this helper function multiple times. - function decreaseIndentIf(value1: boolean, value2?: boolean) { + function decreaseIndentIf(value1: boolean, value2: boolean) { if (value1) { decreaseIndent(); } diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 64709a12ba9..b51dc337865 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -19,6 +19,37 @@ namespace ts.performance { let marks: Map; let measures: Map; + export interface Timer { + enter(): void; + exit(): void; + } + + export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer { + let enterCount = 0; + return { + enter, + exit + }; + + function enter() { + if (++enterCount === 1) { + mark(startMarkName); + } + } + + function exit() { + if (--enterCount === 0) { + mark(endMarkName); + measure(measureName, startMarkName, endMarkName); + } + else if (enterCount < 0) { + Debug.fail("enter/exit count does not match."); + } + } + } + + export const nullTimer: Timer = { enter: noop, exit: noop }; + /** * Marks a performance event. * diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index eff543849cd..0c5af012b99 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -54,23 +54,14 @@ namespace ts { /** * Gets the text for the source map. */ - getText(): string; + getText(): string | undefined; /** * Gets the SourceMappingURL for the source map. */ - getSourceMappingURL(): string; + getSourceMappingURL(): string | undefined; } - // Used for initialize lastEncodedSourceMapSpan and reset lastEncodedSourceMapSpan when updateLastEncodedAndRecordedSpans - const defaultLastEncodedSourceMapSpan: SourceMapSpan = { - emittedLine: 0, - emittedColumn: 0, - sourceLine: 0, - sourceColumn: 0, - sourceIndex: 0 - }; - export interface SourceMapOptions { sourceMap?: boolean; inlineSourceMap?: boolean; @@ -80,24 +71,18 @@ namespace ts { extendedDiagnostics?: boolean; } - export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter, compilerOptions: SourceMapOptions = host.getCompilerOptions()): SourceMapWriter { - const extendedDiagnostics = compilerOptions.extendedDiagnostics; + export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter, writerOptions: SourceMapOptions = host.getCompilerOptions()): SourceMapWriter { let currentSource: SourceMapSource; - let currentSourceText: string; + let currentSourceIndex = -1; let sourceMapDir: string; // The directory in which sourcemap will be - // Current source map file and its index in the sources list - let sourceMapSourceIndex: number; - - // Last recorded and encoded spans - let lastRecordedSourceMapSpan: SourceMapSpan | undefined; - let lastEncodedSourceMapSpan: SourceMapSpan | undefined; - let lastEncodedNameIndex: number | undefined; - // Source map data let sourceMapData: SourceMapData; let sourceMapDataList: SourceMapData[] | undefined; - let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap); + let disabled: boolean = !(writerOptions.sourceMap || writerOptions.inlineSourceMap); + + let sourceMapGenerator: SourceMapGenerator | undefined; + let sourceMapEmitter: SourceMapEmitter | undefined; return { initialize, @@ -114,7 +99,7 @@ namespace ts { * Skips trivia such as comments and white-space that can optionally overriden by the source map source */ function skipSourceTrivia(pos: number): number { - return currentSource.skipTrivia ? currentSource.skipTrivia(pos) : skipTrivia(currentSourceText, pos); + return currentSource.skipTrivia ? currentSource.skipTrivia(pos) : skipTrivia(currentSource.text, pos); } /** @@ -132,30 +117,20 @@ namespace ts { if (sourceMapData) { reset(); } + sourceMapDataList = outputSourceMapDataList; - currentSource = undefined!; - currentSourceText = undefined!; - - // Current source map file and its index in the sources list - sourceMapSourceIndex = -1; - - // Last recorded and encoded spans - lastRecordedSourceMapSpan = undefined; - lastEncodedSourceMapSpan = defaultLastEncodedSourceMapSpan; - lastEncodedNameIndex = 0; - // Initialize source map data sourceMapData = { sourceMapFilePath, - jsSourceMappingURL: !compilerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined!, // TODO: GH#18217 + jsSourceMappingURL: !writerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined!, // TODO: GH#18217 sourceMapFile: getBaseFileName(normalizeSlashes(filePath)), - sourceMapSourceRoot: compilerOptions.sourceRoot || "", + sourceMapSourceRoot: writerOptions.sourceRoot || "", sourceMapSources: [], inputSourceFileNames: [], sourceMapNames: [], sourceMapMappings: "", - sourceMapSourcesContent: compilerOptions.inlineSources ? [] : undefined, + sourceMapSourcesContent: writerOptions.inlineSources ? [] : undefined, }; // Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the @@ -165,8 +140,8 @@ namespace ts { sourceMapData.sourceMapSourceRoot += directorySeparator; } - if (compilerOptions.mapRoot) { - sourceMapDir = normalizeSlashes(compilerOptions.mapRoot); + if (writerOptions.mapRoot) { + sourceMapDir = normalizeSlashes(writerOptions.mapRoot); if (sourceFileOrBundle.kind === SyntaxKind.SourceFile) { // emitting single module file // For modules or multiple emit files the mapRoot will have directory structure like the sources // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map @@ -190,6 +165,12 @@ namespace ts { else { sourceMapDir = getDirectoryPath(normalizePath(filePath)); } + + // If sourceroot option: Use the relative path corresponding to the common directory path + // otherwise source locations relative to map file location + const sourcesDirectoryPath = writerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir; + sourceMapGenerator = createSourceMapGenerator(host, sourceMapData, sourcesDirectoryPath, writerOptions); + sourceMapEmitter = getSourceMapEmitter(sourceMapGenerator, writer); } /** @@ -206,87 +187,11 @@ namespace ts { } currentSource = undefined!; + currentSourceIndex = -1; sourceMapDir = undefined!; - sourceMapSourceIndex = undefined!; - lastRecordedSourceMapSpan = undefined; - lastEncodedSourceMapSpan = undefined!; - lastEncodedNameIndex = undefined; sourceMapData = undefined!; sourceMapDataList = 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, - }; - } - - - // Encoding for sourcemap span - function encodeLastRecordedSourceMapSpan() { - if (!lastRecordedSourceMapSpan || lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) { - return; - } - - Debug.assert(lastRecordedSourceMapSpan.emittedColumn >= 0, "lastEncodedSourceMapSpan.emittedColumn was negative"); - Debug.assert(lastRecordedSourceMapSpan.sourceIndex >= 0, "lastEncodedSourceMapSpan.sourceIndex was negative"); - Debug.assert(lastRecordedSourceMapSpan.sourceLine >= 0, "lastEncodedSourceMapSpan.sourceLine was negative"); - Debug.assert(lastRecordedSourceMapSpan.sourceColumn >= 0, "lastEncodedSourceMapSpan.sourceColumn was negative"); - - let prevEncodedEmittedColumn = lastEncodedSourceMapSpan!.emittedColumn; - // Line/Comma delimiters - if (lastEncodedSourceMapSpan!.emittedLine === lastRecordedSourceMapSpan.emittedLine) { - // Emit comma to separate the entry - if (sourceMapData.sourceMapMappings) { - sourceMapData.sourceMapMappings += ","; - } - } - else { - // Emit line delimiters - for (let encodedLine = lastEncodedSourceMapSpan!.emittedLine; encodedLine < lastRecordedSourceMapSpan.emittedLine; encodedLine++) { - sourceMapData.sourceMapMappings += ";"; - } - prevEncodedEmittedColumn = 0; - } - - // 1. Relative Column 0 based - sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.emittedColumn - prevEncodedEmittedColumn); - - // 2. Relative sourceIndex - sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceIndex - lastEncodedSourceMapSpan!.sourceIndex); - - // 3. Relative sourceLine 0 based - sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceLine - lastEncodedSourceMapSpan!.sourceLine); - - // 4. Relative sourceColumn 0 based - sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceColumn - lastEncodedSourceMapSpan!.sourceColumn); - - // 5. Relative namePosition 0 based - if (lastRecordedSourceMapSpan.nameIndex! >= 0) { - Debug.assert(false, "We do not support name index right now, Make sure to update updateLastEncodedAndRecordedSpans when we start using this"); - sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.nameIndex! - lastEncodedNameIndex!); - lastEncodedNameIndex = lastRecordedSourceMapSpan.nameIndex; - } - - lastEncodedSourceMapSpan = lastRecordedSourceMapSpan; + sourceMapEmitter = undefined; } /** @@ -302,50 +207,8 @@ namespace ts { return; } - if (extendedDiagnostics) { - performance.mark("beforeSourcemap"); - } - - const sourceLinePos = getLineAndCharacterOfPosition(currentSource, pos); - - const emittedLine = writer.getLine(); - const emittedColumn = writer.getColumn(); - - // If this location wasn't recorded or the location in source is going backwards, record the span - if (!lastRecordedSourceMapSpan || - lastRecordedSourceMapSpan.emittedLine !== emittedLine || - lastRecordedSourceMapSpan.emittedColumn !== emittedColumn || - (lastRecordedSourceMapSpan.sourceIndex === sourceMapSourceIndex && - (lastRecordedSourceMapSpan.sourceLine > sourceLinePos.line || - (lastRecordedSourceMapSpan.sourceLine === sourceLinePos.line && lastRecordedSourceMapSpan.sourceColumn > sourceLinePos.character)))) { - - // Encode the last recordedSpan before assigning new - encodeLastRecordedSourceMapSpan(); - - // New span - lastRecordedSourceMapSpan = { - emittedLine, - emittedColumn, - sourceLine: sourceLinePos.line, - sourceColumn: sourceLinePos.character, - sourceIndex: sourceMapSourceIndex - }; - } - else { - // Take the new pos instead since there is no change in emittedLine and column since last location - lastRecordedSourceMapSpan.sourceLine = sourceLinePos.line; - lastRecordedSourceMapSpan.sourceColumn = sourceLinePos.character; - lastRecordedSourceMapSpan.sourceIndex = sourceMapSourceIndex; - } - - if (extendedDiagnostics) { - performance.mark("afterSourcemap"); - performance.measure("Source Map", "beforeSourcemap", "afterSourcemap"); - } - } - - function isPossiblySourceMap(x: {}): x is SourceMapSection { - return typeof x === "object" && !!(x as any).mappings && typeof (x as any).mappings === "string" && !!(x as any).sources; + const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(currentSource, pos); + Debug.assertDefined(sourceMapEmitter, "Not initialized").emitMapping(currentSourceIndex, sourceLine, sourceCharacter, /*nameIndex*/ undefined); } /** @@ -362,60 +225,13 @@ namespace ts { if (node) { if (isUnparsedSource(node) && node.sourceMapText !== undefined) { - const text = node.sourceMapText; - let parsed: {} | undefined; - try { - parsed = JSON.parse(text); + const parsed = tryParseRawSourceMap(node.sourceMapText); + if (parsed) { + Debug.assertDefined(sourceMapEmitter, "Not initialized").emitSourceMap(parsed, node.sourceMapPath!); } - catch { - // empty - } - if (!parsed || !isPossiblySourceMap(parsed)) { - return emitCallback(hint, node); - } - const offsetLine = writer.getLine(); - const firstLineColumnOffset = writer.getColumn(); - // First, decode the old component sourcemap - const originalMap = parsed; - - const sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir; - const resolvedPathCache = createMap(); - const absolutePathCache = createMap(); - const sourcemapIterator = sourcemaps.decodeMappings(originalMap); - for (let { value: raw, done } = sourcemapIterator.next(); !done; { value: raw, done } = sourcemapIterator.next()) { - const pathCacheKey = "" + raw.sourceIndex; - // Apply offsets to each position and fixup source entries - if (!resolvedPathCache.has(pathCacheKey)) { - const rawPath = originalMap.sources[raw.sourceIndex]; - const relativePath = originalMap.sourceRoot ? combinePaths(originalMap.sourceRoot, rawPath) : rawPath; - const combinedPath = combinePaths(getDirectoryPath(node.sourceMapPath!), relativePath); - const resolvedPath = getRelativePathToDirectoryOrUrl( - sourcesDirectoryPath, - combinedPath, - host.getCurrentDirectory(), - host.getCanonicalFileName, - /*isAbsolutePathAnUrl*/ true - ); - resolvedPathCache.set(pathCacheKey, resolvedPath); - absolutePathCache.set(pathCacheKey, getNormalizedAbsolutePath(resolvedPath, sourcesDirectoryPath)); - } - const resolvedPath = resolvedPathCache.get(pathCacheKey)!; - const absolutePath = absolutePathCache.get(pathCacheKey)!; - // tslint:disable-next-line:no-null-keyword - setupSourceEntry(absolutePath, originalMap.sourcesContent ? originalMap.sourcesContent[raw.sourceIndex] : null, resolvedPath); // TODO: Lookup content for inlining? - const newIndex = sourceMapData.sourceMapSources.indexOf(resolvedPath); - // Then reencode all the updated spans into the overall map - encodeLastRecordedSourceMapSpan(); - lastRecordedSourceMapSpan = { - ...raw, - emittedLine: raw.emittedLine + offsetLine, - emittedColumn: raw.emittedLine === 0 ? (raw.emittedColumn + firstLineColumnOffset) : raw.emittedColumn, - sourceIndex: newIndex, - }; - } - // And actually emit the text these sourcemaps are for return emitCallback(hint, node); } + const emitNode = node.emitNode; const emitFlags = emitNode && emitNode.flags || EmitFlags.None; const range = emitNode && emitNode.sourceMapRange; @@ -502,40 +318,16 @@ namespace ts { } currentSource = sourceFile; - currentSourceText = currentSource.text; if (isJsonSourceMapSource(sourceFile)) { return; } - setupSourceEntry(sourceFile.fileName, sourceFile.text); - } + if (!sourceMapGenerator) return Debug.fail("Not initialized"); - function setupSourceEntry(fileName: string, content: string | null, source?: string) { - if (!source) { - // Add the file to tsFilePaths - // If sourceroot option: Use the relative path corresponding to the common directory path - // otherwise source locations relative to map file location - const sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir; - - source = getRelativePathToDirectoryOrUrl(sourcesDirectoryPath, - fileName, - host.getCurrentDirectory(), - host.getCanonicalFileName, - /*isAbsolutePathAnUrl*/ true); - } - - sourceMapSourceIndex = sourceMapData.sourceMapSources.indexOf(source); - if (sourceMapSourceIndex === -1) { - sourceMapSourceIndex = sourceMapData.sourceMapSources.length; - sourceMapData.sourceMapSources.push(source); - - // The one that can be used from program to get the actual source file - sourceMapData.inputSourceFileNames.push(fileName); - - if (compilerOptions.inlineSources) { - sourceMapData.sourceMapSourcesContent!.push(content); - } + currentSourceIndex = sourceMapGenerator.addSource(sourceFile.fileName); + if (writerOptions.inlineSources) { + sourceMapGenerator.setSourceContent(currentSourceIndex, sourceFile.text); } } @@ -544,12 +336,10 @@ namespace ts { */ function getText() { if (disabled || isJsonSourceMapSource(currentSource)) { - return undefined!; // TODO: GH#18217 + return undefined; } - encodeLastRecordedSourceMapSpan(); - - return JSON.stringify(captureSection()); + return Debug.assertDefined(sourceMapGenerator, "Not initialized").toString(); } /** @@ -557,12 +347,13 @@ namespace ts { */ function getSourceMappingURL() { if (disabled || isJsonSourceMapSource(currentSource)) { - return undefined!; // TODO: GH#18217 + return undefined; } - if (compilerOptions.inlineSourceMap) { + if (writerOptions.inlineSourceMap) { // Encode the sourceMap into the sourceMap url - const base64SourceMapText = base64encode(sys, getText()); + const sourceMapText = Debug.assertDefined(sourceMapGenerator, "Not initialized").toString(); + const base64SourceMapText = base64encode(sys, sourceMapText); return sourceMapData.jsSourceMappingURL = `data:application/json;base64,${base64SourceMapText}`; } else { @@ -571,15 +362,316 @@ namespace ts { } } - export interface SourceMapSection { - version: 3; - file: string; - sourceRoot?: string; - sources: string[]; - names?: string[]; - mappings: string; - sourcesContent?: (string | null)[]; - sections?: undefined; + interface SourceMapGeneratorOptions { + extendedDiagnostics?: boolean; + } + + function createSourceMapGenerator(host: EmitHost, sourceMapData: SourceMapData, sourcesDirectoryPath: string, generatorOptions: SourceMapGeneratorOptions): SourceMapGenerator { + const { enter, exit } = generatorOptions.extendedDiagnostics + ? performance.createTimer("Source Map", "beforeSourcemap", "afterSourcemap") + : performance.nullTimer; + + // Current source map file and its index in the sources list + const sourceToSourceIndexMap = createMap(); + let nameToNameIndexMap: Map | undefined; + + // Last recorded and encoded mappings + let lastGeneratedLine = 0; + let lastGeneratedCharacter = 0; + let lastSourceIndex = 0; + let lastSourceLine = 0; + let lastSourceCharacter = 0; + let lastNameIndex = 0; + let hasLast = false; + + let pendingGeneratedLine = 0; + let pendingGeneratedCharacter = 0; + let pendingSourceIndex = 0; + let pendingSourceLine = 0; + let pendingSourceCharacter = 0; + let pendingNameIndex = 0; + let hasPending = false; + let hasPendingSource = false; + let hasPendingName = false; + + return { + addSource, + setSourceContent, + addName, + addMapping, + appendSourceMap, + toJSON, + toString: () => JSON.stringify(toJSON()) + }; + + function addSource(fileName: string) { + enter(); + const source = getRelativePathToDirectoryOrUrl(sourcesDirectoryPath, + fileName, + host.getCurrentDirectory(), + host.getCanonicalFileName, + /*isAbsolutePathAnUrl*/ true); + + let sourceIndex = sourceToSourceIndexMap.get(source); + if (sourceIndex === undefined) { + sourceIndex = sourceMapData.sourceMapSources.length; + sourceMapData.sourceMapSources.push(source); + sourceMapData.inputSourceFileNames.push(fileName); + sourceToSourceIndexMap.set(source, sourceIndex); + } + exit(); + return sourceIndex; + } + + function setSourceContent(sourceIndex: number, content: string | null) { + enter(); + if (content !== null) { + if (!sourceMapData.sourceMapSourcesContent) sourceMapData.sourceMapSourcesContent = []; + while (sourceMapData.sourceMapSourcesContent.length < sourceIndex) { + // tslint:disable-next-line:no-null-keyword boolean-trivia + sourceMapData.sourceMapSourcesContent.push(null); + } + sourceMapData.sourceMapSourcesContent[sourceIndex] = content; + } + exit(); + } + + function addName(name: string) { + enter(); + if (!sourceMapData.sourceMapNames) sourceMapData.sourceMapNames = []; + if (!nameToNameIndexMap) nameToNameIndexMap = createMap(); + let nameIndex = nameToNameIndexMap.get(name); + if (nameIndex === undefined) { + nameIndex = sourceMapData.sourceMapNames.length; + sourceMapData.sourceMapNames.push(name); + nameToNameIndexMap.set(name, nameIndex); + } + exit(); + return nameIndex; + } + + function isNewGeneratedPosition(generatedLine: number, generatedCharacter: number) { + return !hasPending + || pendingGeneratedLine !== generatedLine + || pendingGeneratedCharacter !== generatedCharacter; + } + + function isBacktrackingSourcePosition(sourceIndex: number | undefined, sourceLine: number | undefined, sourceCharacter: number | undefined) { + return sourceIndex !== undefined + && sourceLine !== undefined + && sourceCharacter !== undefined + && pendingSourceIndex === sourceIndex + && (pendingSourceLine > sourceLine + || pendingSourceLine === sourceLine && pendingSourceCharacter > sourceCharacter); + } + + function addMapping(generatedLine: number, generatedCharacter: number, sourceIndex?: number, sourceLine?: number, sourceCharacter?: number, nameIndex?: number) { + Debug.assert(generatedLine >= pendingGeneratedLine, "generatedLine cannot backtrack"); + Debug.assert(generatedCharacter >= 0, "generatedCharacter cannot be negative"); + Debug.assert(sourceIndex === undefined || sourceIndex >= 0, "sourceIndex cannot be negative"); + Debug.assert(sourceLine === undefined || sourceLine >= 0, "sourceLine cannot be negative"); + Debug.assert(sourceCharacter === undefined || sourceCharacter >= 0, "sourceCharacter cannot be negative"); + enter(); + // If this location wasn't recorded or the location in source is going backwards, record the mapping + if (isNewGeneratedPosition(generatedLine, generatedCharacter) || + isBacktrackingSourcePosition(sourceIndex, sourceLine, sourceCharacter)) { + commitPendingMapping(); + pendingGeneratedLine = generatedLine; + pendingGeneratedCharacter = generatedCharacter; + hasPendingSource = false; + hasPendingName = false; + hasPending = true; + } + + if (sourceIndex !== undefined && sourceLine !== undefined && sourceCharacter !== undefined) { + pendingSourceIndex = sourceIndex; + pendingSourceLine = sourceLine; + pendingSourceCharacter = sourceCharacter; + hasPendingSource = true; + if (nameIndex !== undefined) { + pendingNameIndex = nameIndex; + hasPendingName = true; + } + } + exit(); + } + + function appendSourceMap(generatedLine: number, generatedCharacter: number, map: RawSourceMap, sourceMapPath: string) { + Debug.assert(generatedLine >= pendingGeneratedLine, "generatedLine cannot backtrack"); + Debug.assert(generatedCharacter >= 0, "generatedCharacter cannot be negative"); + enter(); + // First, decode the old component sourcemap + const sourceIndexToNewSourceIndexMap: number[] = []; + let nameIndexToNewNameIndexMap: number[] | undefined; + const mappingIterator = sourcemaps.decodeMappings(map); + for (let { value: raw, done } = mappingIterator.next(); !done; { value: raw, done } = mappingIterator.next()) { + // Then reencode all the updated mappings into the overall map + let newSourceIndex: number | undefined; + let newSourceLine: number | undefined; + let newSourceCharacter: number | undefined; + let newNameIndex: number | undefined; + if (raw.sourceIndex !== undefined) { + newSourceIndex = sourceIndexToNewSourceIndexMap[raw.sourceIndex]; + if (newSourceIndex === undefined) { + // Apply offsets to each position and fixup source entries + const rawPath = map.sources[raw.sourceIndex]; + const relativePath = map.sourceRoot ? combinePaths(map.sourceRoot, rawPath) : rawPath; + const combinedPath = combinePaths(getDirectoryPath(sourceMapPath), relativePath); + sourceIndexToNewSourceIndexMap[raw.sourceIndex] = newSourceIndex = addSource(combinedPath); + if (map.sourcesContent && typeof map.sourcesContent[raw.sourceIndex] === "string") { + setSourceContent(newSourceIndex, map.sourcesContent[raw.sourceIndex]); + } + } + + newSourceLine = raw.sourceLine; + newSourceCharacter = raw.sourceColumn; + if (map.names && raw.nameIndex !== undefined) { + if (!nameIndexToNewNameIndexMap) nameIndexToNewNameIndexMap = []; + newNameIndex = nameIndexToNewNameIndexMap[raw.nameIndex]; + if (newNameIndex === undefined) { + nameIndexToNewNameIndexMap[raw.nameIndex] = newNameIndex = addName(map.names[raw.nameIndex]); + } + } + } + + const newGeneratedLine = raw.emittedLine + generatedLine; + const newGeneratedCharacter = raw.emittedLine === 0 ? raw.emittedColumn + generatedCharacter : raw.emittedColumn; + addMapping(newGeneratedLine, newGeneratedCharacter, newSourceIndex, newSourceLine, newSourceCharacter, newNameIndex); + } + exit(); + } + + function shouldCommitMapping() { + return !hasLast + || lastGeneratedLine !== pendingGeneratedLine + || lastGeneratedCharacter !== pendingGeneratedCharacter + || lastSourceIndex !== pendingSourceIndex + || lastSourceLine !== pendingSourceLine + || lastSourceCharacter !== pendingSourceCharacter + || lastNameIndex !== pendingNameIndex; + } + + // Encoding for sourcemap span + function commitPendingMapping() { + if (!hasPending || !shouldCommitMapping()) { + return; + } + + enter(); + + // Line/Comma delimiters + if (lastGeneratedLine < pendingGeneratedLine) { + // Emit line delimiters + do { + sourceMapData.sourceMapMappings += ";"; + lastGeneratedLine++; + lastGeneratedCharacter = 0; + } + while (lastGeneratedLine < pendingGeneratedLine); + } + else { + Debug.assertEqual(lastGeneratedLine, pendingGeneratedLine, "generatedLine cannot backtrack"); + // Emit comma to separate the entry + if (hasLast) { + sourceMapData.sourceMapMappings += ","; + } + } + + // 1. Relative generated character + sourceMapData.sourceMapMappings += base64VLQFormatEncode(pendingGeneratedCharacter - lastGeneratedCharacter); + lastGeneratedCharacter = pendingGeneratedCharacter; + + if (hasPendingSource) { + // 2. Relative sourceIndex + sourceMapData.sourceMapMappings += base64VLQFormatEncode(pendingSourceIndex - lastSourceIndex); + lastSourceIndex = pendingSourceIndex; + + // 3. Relative source line + sourceMapData.sourceMapMappings += base64VLQFormatEncode(pendingSourceLine - lastSourceLine); + lastSourceLine = pendingSourceLine; + + // 4. Relative source character + sourceMapData.sourceMapMappings += base64VLQFormatEncode(pendingSourceCharacter - lastSourceCharacter); + lastSourceCharacter = pendingSourceCharacter; + + if (hasPendingName) { + // 5. Relative nameIndex + sourceMapData.sourceMapMappings += base64VLQFormatEncode(pendingNameIndex - lastNameIndex); + lastNameIndex = pendingNameIndex; + } + } + + hasLast = true; + exit(); + } + + function toJSON(): RawSourceMap { + commitPendingMapping(); + return { + version: 3, + file: sourceMapData.sourceMapFile, + sourceRoot: sourceMapData.sourceMapSourceRoot, + sources: sourceMapData.sourceMapSources, + names: sourceMapData.sourceMapNames, + mappings: sourceMapData.sourceMapMappings, + sourcesContent: sourceMapData.sourceMapSourcesContent, + }; + } + } + + function getSourceMapEmitter(generator: SourceMapGenerator, writer: EmitTextWriter): SourceMapEmitter { + if (writer.getSourceMapEmitter) { + return writer.getSourceMapEmitter(generator); + } + + return createSourceMapEmitter(generator, writer); + } + + function createSourceMapEmitter(generator: SourceMapGenerator, writer: EmitTextWriter) { + return { + emitMapping, + emitSourceMap + }; + + function emitMapping(sourceIndex?: number, sourceLine?: number, sourceCharacter?: number, nameIndex?: number) { + return generator.addMapping(writer.getLine(), writer.getColumn(), sourceIndex!, sourceLine!, sourceCharacter!, nameIndex); + } + + function emitSourceMap(sourceMap: RawSourceMap, sourceMapPath: string): void { + return generator.appendSourceMap(writer.getLine(), writer.getColumn(), sourceMap, sourceMapPath); + } + } + + function isStringOrNull(x: any) { + // tslint:disable-next-line:no-null-keyword + return typeof x === "string" || x === null; + } + + export function isRawSourceMap(x: any): x is RawSourceMap { + // tslint:disable-next-line:no-null-keyword + return x !== null + && typeof x === "object" + && x.version === 3 + && typeof x.file === "string" + && typeof x.mappings === "string" + && isArray(x.sources) && every(x.sources, isString) + && (x.sourceRoot === undefined || x.sourceRoot === null || typeof x.sourceRoot === "string") + && (x.sourcesContent === undefined || x.sourcesContent === null || isArray(x.sourcesContent) && every(x.sourcesContent, isStringOrNull)) + && (x.names === undefined || x.names === null || isArray(x.names) && every(x.names, isString)); + } + + export function tryParseRawSourceMap(text: string) { + try { + const parsed = JSON.parse(text); + if (isRawSourceMap(parsed)) { + return parsed; + } + } + catch { + // empty + } + + return undefined; } const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; diff --git a/src/compiler/sourcemapDecoder.ts b/src/compiler/sourcemapDecoder.ts index 28cbbe39cf5..8cd274491e0 100644 --- a/src/compiler/sourcemapDecoder.ts +++ b/src/compiler/sourcemapDecoder.ts @@ -30,16 +30,6 @@ namespace ts { /* @internal */ namespace ts.sourcemaps { - export interface SourceMapData { - version?: number; - file?: string; - sourceRoot?: string; - sources: string[]; - sourcesContent?: (string | null)[]; - names?: string[]; - mappings: string; - } - export interface SourceMappableLocation { fileName: string; position: number; @@ -59,7 +49,7 @@ namespace ts.sourcemaps { log(text: string): void; } - export function decode(host: SourceMapDecodeHost, mapPath: string, map: SourceMapData, program?: Program, fallbackCache = createSourceFileLikeCache(host)): SourceMapper { + export function decode(host: SourceMapDecodeHost, mapPath: string, map: RawSourceMap, program?: Program, fallbackCache = createSourceFileLikeCache(host)): SourceMapper { const currentDirectory = getDirectoryPath(mapPath); const sourceRoot = map.sourceRoot ? getNormalizedAbsolutePath(map.sourceRoot, currentDirectory) : currentDirectory; let decodedMappings: ProcessedSourceMapPosition[]; @@ -82,7 +72,7 @@ namespace ts.sourcemaps { if (!maps[targetIndex] || comparePaths(loc.fileName, maps[targetIndex].sourcePath, sourceRoot) !== 0) { return loc; } - return { fileName: toPath(map.file!, sourceRoot, host.getCanonicalFileName), position: maps[targetIndex].emittedPosition }; // Closest pos + return { fileName: toPath(map.file, sourceRoot, host.getCanonicalFileName), position: maps[targetIndex].emittedPosition }; // Closest pos } function getOriginalPosition(loc: SourceMappableLocation): SourceMappableLocation { @@ -140,7 +130,7 @@ namespace ts.sourcemaps { function processPosition(position: RawSourceMapPosition): ProcessedSourceMapPosition { const sourcePath = map.sources[position.sourceIndex]; return { - emittedPosition: getPositionOfLineAndCharacterUsingName(map.file!, currentDirectory, position.emittedLine, position.emittedColumn), + emittedPosition: getPositionOfLineAndCharacterUsingName(map.file, currentDirectory, position.emittedLine, position.emittedColumn), sourcePosition: getPositionOfLineAndCharacterUsingName(sourcePath, sourceRoot, position.sourceLine, position.sourceColumn), sourcePath, // TODO: Consider using `name` field to remap the expected identifier to scan for renames to handle another tool renaming oout output @@ -157,7 +147,7 @@ namespace ts.sourcemaps { } /*@internal*/ - export function decodeMappings(map: SourceMapData): MappingsDecoder { + export function decodeMappings(map: Pick): MappingsDecoder { const state: DecoderState = { encodedText: map.mappings, currentNameIndex: undefined, @@ -191,7 +181,7 @@ namespace ts.sourcemaps { }; } - function calculateDecodedMappings(map: SourceMapData, processPosition: (position: RawSourceMapPosition) => T, host?: { log?(s: string): void }): T[] { + function calculateDecodedMappings(map: RawSourceMap, processPosition: (position: RawSourceMapPosition) => T, host?: { log?(s: string): void }): T[] { const decoder = decodeMappings(map); const positions = arrayFrom(decoder, processPosition); if (decoder.error) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4330a776d04..dedbdb10bc6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5065,6 +5065,7 @@ namespace ts { IdentifierName, // Emitting an IdentifierName MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode Unspecified, // Emitting an otherwise unspecified node + EmbeddedStatement, // Emitting an embedded statement } /* @internal */ @@ -5320,10 +5321,80 @@ namespace ts { /*@internal*/ onlyPrintJsDocStyle?: boolean; } + /* @internal */ + export interface RawSourceMap { + version: 3; + file: string; + sourceRoot?: string | null; + sources: string[]; + sourcesContent?: (string | null)[] | null; + mappings: string; + names?: string[] | null; + } + + /** + * Generates a source map. + * @internal + */ + export interface SourceMapGenerator { + /** + * Adds a source to the source map. + */ + addSource(fileName: string): number; + /** + * Set the content for a source. + */ + setSourceContent(sourceIndex: number, content: string | null): void; + /** + * Adds a name. + */ + addName(name: string): number; + /** + * Adds a mapping without source information. + */ + addMapping(generatedLine: number, generatedCharacter: number): void; + /** + * Adds a mapping with source information. + */ + addMapping(generatedLine: number, generatedCharacter: number, sourceIndex: number, sourceLine: number, sourceCharacter: number, nameIndex?: number): void; + /** + * Appends a source map. + */ + appendSourceMap(generatedLine: number, generatedCharacter: number, sourceMap: RawSourceMap, sourceMapPath: string): void; + /** + * Gets the source map as a `RawSourceMap` object. + */ + toJSON(): RawSourceMap; + /** + * Gets the string representation of the source map. + */ + toString(): string; + } + + /** + * Emits SourceMap information through an entangled `EmitTextWriter` + * @internal + */ + export interface SourceMapEmitter { + /** + * Emits a mapping without source information. + */ + emitMapping(): void; + /** + * Emits a mapping with source information. + */ + emitMapping(sourceIndex: number, sourceLine: number, sourceCharacter: number, nameIndex?: number): void; + /** + * Emits a source map. + */ + emitSourceMap(sourceMap: RawSourceMap, sourceMapPath: string): void; + } + /* @internal */ export interface EmitTextWriter extends SymbolWriter { write(s: string): void; - writeTextOfNode(text: string, node: Node): void; + writeTrailingSemicolon(text: string): void; + writeComment(text: string): void; getText(): string; rawWrite(s: string): void; writeLiteral(s: string): void; @@ -5332,6 +5403,7 @@ namespace ts { getColumn(): number; getIndent(): number; isAtStartOfLine(): boolean; + getSourceMapEmitter?(generator: SourceMapGenerator): SourceMapEmitter; } export interface GetEffectiveTypeRootsHost { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 33dfd2617c9..c2171edd09a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -65,7 +65,6 @@ namespace ts { getText: () => str, write: writeText, rawWrite: writeText, - writeTextOfNode: writeText, writeKeyword: writeText, writeOperator: writeText, writePunctuation: writeText, @@ -74,7 +73,9 @@ namespace ts { writeLiteral: writeText, writeParameter: writeText, writeProperty: writeText, - writeSymbol: writeText, + writeSymbol: (s, _) => writeText(s), + writeTrailingSemicolon: writeText, + writeComment: writeText, getTextPos: () => str.length, getLine: () => 0, getColumn: () => 0, @@ -3130,18 +3131,11 @@ namespace ts { } } - function writeTextOfNode(text: string, node: Node) { - const s = getTextOfNodeFromSourceText(text, node); - write(s); - updateLineCountAndPosFor(s); - } - reset(); return { write, rawWrite, - writeTextOfNode, writeLiteral, writeLine, increaseIndent: () => { indent++; }, @@ -3164,7 +3158,79 @@ namespace ts { writePunctuation: write, writeSpace: write, writeStringLiteral: write, - writeSymbol: write + writeSymbol: (s, _) => write(s), + writeTrailingSemicolon: write, + writeComment: write + }; + } + + export function getTrailingSemicolonOmittingWriter(writer: EmitTextWriter): EmitTextWriter { + let pendingTrailingSemicolon = false; + + function commitPendingTrailingSemicolon() { + if (pendingTrailingSemicolon) { + writer.writeTrailingSemicolon(";"); + pendingTrailingSemicolon = false; + } + } + + return { + ...writer, + writeTrailingSemicolon() { + pendingTrailingSemicolon = true; + }, + writeLiteral(s) { + commitPendingTrailingSemicolon(); + writer.writeLiteral(s); + }, + writeStringLiteral(s) { + commitPendingTrailingSemicolon(); + writer.writeStringLiteral(s); + }, + writeSymbol(s, sym) { + commitPendingTrailingSemicolon(); + writer.writeSymbol(s, sym); + }, + writePunctuation(s) { + commitPendingTrailingSemicolon(); + writer.writePunctuation(s); + }, + writeKeyword(s) { + commitPendingTrailingSemicolon(); + writer.writeKeyword(s); + }, + writeOperator(s) { + commitPendingTrailingSemicolon(); + writer.writeOperator(s); + }, + writeParameter(s) { + commitPendingTrailingSemicolon(); + writer.writeParameter(s); + }, + writeSpace(s) { + commitPendingTrailingSemicolon(); + writer.writeSpace(s); + }, + writeProperty(s) { + commitPendingTrailingSemicolon(); + writer.writeProperty(s); + }, + writeComment(s) { + commitPendingTrailingSemicolon(); + writer.writeComment(s); + }, + writeLine() { + commitPendingTrailingSemicolon(); + writer.writeLine(); + }, + increaseIndent() { + commitPendingTrailingSemicolon(); + writer.increaseIndent(); + }, + decreaseIndent() { + commitPendingTrailingSemicolon(); + writer.decreaseIndent(); + } }; } @@ -3444,13 +3510,13 @@ namespace ts { writeComment: (text: string, lineMap: ReadonlyArray, writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void) { if (comments && comments.length > 0) { if (leadingSeparator) { - writer.write(" "); + writer.writeSpace(" "); } let emitInterveningSeparator = false; for (const comment of comments) { if (emitInterveningSeparator) { - writer.write(" "); + writer.writeSpace(" "); emitInterveningSeparator = false; } @@ -3464,7 +3530,7 @@ namespace ts { } if (emitInterveningSeparator && trailingSeparator) { - writer.write(" "); + writer.writeSpace(" "); } } } @@ -3598,7 +3664,7 @@ namespace ts { } else { // Single line comment of style //.... - writer.write(text.substring(commentPos, commentEnd)); + writer.writeComment(text.substring(commentPos, commentEnd)); } } @@ -3607,14 +3673,14 @@ namespace ts { const currentLineText = text.substring(pos, end).replace(/^\s+|\s+$/g, ""); if (currentLineText) { // trimmed forward and ending spaces text - writer.write(currentLineText); + writer.writeComment(currentLineText); if (end !== commentEnd) { writer.writeLine(); } } else { // Empty string - make sure we write empty line - writer.writeLiteral(newLine); + writer.rawWrite(newLine); } } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 23ee10d7412..089edcd474e 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1643,6 +1643,7 @@ namespace Harness { else { sourceMapCode = ""; result.maps.forEach(sourceMap => { + if (sourceMapCode) sourceMapCode += "\r\n"; sourceMapCode += fileOutput(sourceMap, harnessSettings); }); } @@ -1668,6 +1669,9 @@ namespace Harness { let jsCode = ""; result.js.forEach(file => { + if (jsCode.length && jsCode.charCodeAt(jsCode.length - 1) !== ts.CharacterCodes.lineFeed) { + jsCode += "\r\n"; + } jsCode += fileOutput(file, harnessSettings); }); diff --git a/src/harness/sourceMapRecorder.ts b/src/harness/sourceMapRecorder.ts index a1aa07f8733..65236e78cb7 100644 --- a/src/harness/sourceMapRecorder.ts +++ b/src/harness/sourceMapRecorder.ts @@ -19,11 +19,7 @@ namespace Harness.SourceMapRecorder { decodingIndex = 0; sourceMapMappings = sourceMapData.sourceMapMappings; mappings = ts.sourcemaps.decodeMappings({ - version: 3, - file: sourceMapData.sourceMapFile, sources: sourceMapData.sourceMapSources, - sourceRoot: sourceMapData.sourceMapSourceRoot, - sourcesContent: sourceMapData.sourceMapSourcesContent, mappings: sourceMapData.sourceMapMappings, names: sourceMapData.sourceMapNames }); @@ -130,12 +126,19 @@ namespace Harness.SourceMapRecorder { } export function recordNewSourceFileSpan(sourceMapSpan: ts.SourceMapSpan, newSourceFileCode: string) { - assert.isTrue(spansOnSingleLine.length === 0 || spansOnSingleLine[0].sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine, "new file source map span should be on new line. We currently handle only that scenario"); + let continuesLine = false; + if (spansOnSingleLine.length > 0 && spansOnSingleLine[0].sourceMapSpan.emittedLine === sourceMapSpan.emittedLine) { + writeRecordedSpans(); + spansOnSingleLine = []; + nextJsLineToWrite--; // walk back one line to reprint the line + continuesLine = true; + } + recordSourceMapSpan(sourceMapSpan); assert.isTrue(spansOnSingleLine.length === 1); sourceMapRecorder.WriteLine("-------------------------------------------------------------------"); - sourceMapRecorder.WriteLine("emittedFile:" + jsFile.file); + sourceMapRecorder.WriteLine("emittedFile:" + jsFile.file + (continuesLine ? ` (${sourceMapSpan.emittedLine + 1}, ${sourceMapSpan.emittedColumn + 1})` : "")); sourceMapRecorder.WriteLine("sourceFile:" + sourceMapSources[spansOnSingleLine[0].sourceMapSpan.sourceIndex]); sourceMapRecorder.WriteLine("-------------------------------------------------------------------"); diff --git a/src/services/sourcemaps.ts b/src/services/sourcemaps.ts index 1833c415be4..8ad5385a408 100644 --- a/src/services/sourcemaps.ts +++ b/src/services/sourcemaps.ts @@ -42,14 +42,8 @@ namespace ts { } function convertDocumentToSourceMapper(file: { sourceMapper?: sourcemaps.SourceMapper }, contents: string, mapFileName: string) { - let maps: sourcemaps.SourceMapData | undefined; - try { - maps = JSON.parse(contents); - } - catch { - // swallow error - } - if (!maps || !maps.sources || !maps.file || !maps.mappings) { + const map = tryParseRawSourceMap(contents); + if (!map || !map.sources || !map.file || !map.mappings) { // obviously invalid map return file.sourceMapper = sourcemaps.identitySourceMapper; } @@ -58,7 +52,7 @@ namespace ts { fileExists: s => host.fileExists!(s), // TODO: GH#18217 getCanonicalFileName, log, - }, mapFileName, maps, getProgram(), sourcemappedFileCache); + }, mapFileName, map, getProgram(), sourcemappedFileCache); } function getSourceMapper(fileName: string, file: SourceFileLike): sourcemaps.SourceMapper { diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 51e60d08741..c326235baf5 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -878,6 +878,9 @@ namespace ts.textChanges { this.writer.write(s); this.setLastNonTriviaPosition(s, /*force*/ false); } + writeComment(s: string): void { + this.writer.writeComment(s); + } writeKeyword(s: string): void { this.writer.writeKeyword(s); this.setLastNonTriviaPosition(s, /*force*/ false); @@ -890,6 +893,10 @@ namespace ts.textChanges { this.writer.writePunctuation(s); this.setLastNonTriviaPosition(s, /*force*/ false); } + writeTrailingSemicolon(s: string): void { + this.writer.writeTrailingSemicolon(s); + this.setLastNonTriviaPosition(s, /*force*/ false); + } writeParameter(s: string): void { this.writer.writeParameter(s); this.setLastNonTriviaPosition(s, /*force*/ false); @@ -910,9 +917,6 @@ namespace ts.textChanges { this.writer.writeSymbol(s, sym); this.setLastNonTriviaPosition(s, /*force*/ false); } - writeTextOfNode(text: string, node: Node): void { - this.writer.writeTextOfNode(text, node); - } writeLine(): void { this.writer.writeLine(); } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 9e7b823fe54..49d688f407e 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1431,6 +1431,7 @@ namespace ts { writeKeyword: text => writeKind(text, SymbolDisplayPartKind.keyword), writeOperator: text => writeKind(text, SymbolDisplayPartKind.operator), writePunctuation: text => writeKind(text, SymbolDisplayPartKind.punctuation), + writeTrailingSemicolon: text => writeKind(text, SymbolDisplayPartKind.punctuation), writeSpace: text => writeKind(text, SymbolDisplayPartKind.space), writeStringLiteral: text => writeKind(text, SymbolDisplayPartKind.stringLiteral), writeParameter: text => writeKind(text, SymbolDisplayPartKind.parameterName), @@ -1439,7 +1440,7 @@ namespace ts { writeSymbol, writeLine, write: unknownWrite, - writeTextOfNode: unknownWrite, + writeComment: unknownWrite, getText: () => "", getTextPos: () => 0, getColumn: () => 0, diff --git a/src/testRunner/projectsRunner.ts b/src/testRunner/projectsRunner.ts index 3dcf1911dde..d0663533dce 100644 --- a/src/testRunner/projectsRunner.ts +++ b/src/testRunner/projectsRunner.ts @@ -427,6 +427,7 @@ namespace project { skipDefaultLibCheck: false, moduleResolution: ts.ModuleResolutionKind.Classic, module: moduleKind, + newLine: ts.NewLineKind.CarriageReturnLineFeed, mapRoot: testCase.resolveMapRoot && testCase.mapRoot ? vpath.resolve(vfs.srcFolder, testCase.mapRoot) : testCase.mapRoot, diff --git a/src/testRunner/unittests/customTransforms.ts b/src/testRunner/unittests/customTransforms.ts index 819b5d80df5..c4ff21d0a25 100644 --- a/src/testRunner/unittests/customTransforms.ts +++ b/src/testRunner/unittests/customTransforms.ts @@ -18,7 +18,7 @@ namespace ts { writeFile: (fileName, text) => outputs.set(fileName, text), }; - const program = createProgram(arrayFrom(fileMap.keys()), options, host); + const program = createProgram(arrayFrom(fileMap.keys()), { newLine: NewLineKind.LineFeed, ...options }, host); program.emit(/*targetSourceFile*/ undefined, host.writeFile, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ false, customTransformers); let content = ""; for (const [file, text] of arrayFrom(outputs.entries())) { diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index b99a2d9fe99..110d9ca1f10 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -9224,7 +9224,7 @@ export function Test2() { const configContent = JSON.stringify({ compilerOptions }); const aTsconfig: File = { path: "/a/tsconfig.json", content: configContent }; - const aDtsMapContent: SourceMapSection = { + const aDtsMapContent: RawSourceMap = { version: 3, file: "a.d.ts", sourceRoot: "", @@ -9248,7 +9248,7 @@ export function Test2() { }; const bTsconfig: File = { path: "/b/tsconfig.json", content: configContent }; - const bDtsMapContent: SourceMapSection = { + const bDtsMapContent: RawSourceMap = { version: 3, file: "b.d.ts", sourceRoot: "", diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5e992f3847a..49bc01284ba 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2745,7 +2745,8 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, - Unspecified = 4 + Unspecified = 4, + EmbeddedStatement = 5 } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 32761156558..d3b20cb3f06 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2745,7 +2745,8 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, - Unspecified = 4 + Unspecified = 4, + EmbeddedStatement = 5 } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ diff --git a/tests/baselines/reference/declarationMapsMultifile.js.map b/tests/baselines/reference/declarationMapsMultifile.js.map index cf1f1472a60..80d8ac624c6 100644 --- a/tests/baselines/reference/declarationMapsMultifile.js.map +++ b/tests/baselines/reference/declarationMapsMultifile.js.map @@ -1,3 +1,4 @@ //// [a.d.ts.map] -{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["a.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd"}//// [index.d.ts.map] +{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["a.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd"} +//// [index.d.ts.map] {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,eAAO,IAAI,CAAC;;CAAqB,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsWithSourceMap.js.map b/tests/baselines/reference/declarationMapsWithSourceMap.js.map index 41114d427d0..9a442371965 100644 --- a/tests/baselines/reference/declarationMapsWithSourceMap.js.map +++ b/tests/baselines/reference/declarationMapsWithSourceMap.js.map @@ -1,3 +1,4 @@ //// [bundle.js.map] -{"version":3,"file":"bundle.js","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IAAA;IAOA,CAAC;IANG,qBAAO,GAAP,UAAQ,CAAc;QAClB,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,CAAC;IACpB,CAAC;IACM,QAAI,GAAX;QACI,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IACL,UAAC;AAAD,CAAC,AAPD,IAOC;ACPD,IAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;AACpB,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC;AAEnB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC"}//// [bundle.d.ts.map] +{"version":3,"file":"bundle.js","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IAAA;IAOA,CAAC;IANG,qBAAO,GAAP,UAAQ,CAAc;QAClB,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,CAAC;IACpB,CAAC;IACM,QAAI,GAAX;QACI,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IACL,UAAC;AAAD,CAAC,AAPD,IAOC;ACPD,IAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;AACpB,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC;AAEnB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC"} +//// [bundle.d.ts.map] {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA,cAAM,GAAG;IACL,OAAO,CAAC,GAAG;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/jsxFactoryIdentifier.js b/tests/baselines/reference/jsxFactoryIdentifier.js index 817ef769d53..a5ed7e66035 100644 --- a/tests/baselines/reference/jsxFactoryIdentifier.js +++ b/tests/baselines/reference/jsxFactoryIdentifier.js @@ -68,7 +68,8 @@ exports.createElement = Element.createElement; function toCamelCase(text) { return text[0].toLowerCase() + text.substring(1); } -//# sourceMappingURL=Element.js.map//// [test.js] +//# sourceMappingURL=Element.js.map +//// [test.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Element_1 = require("./Element"); diff --git a/tests/baselines/reference/jsxFactoryIdentifier.js.map b/tests/baselines/reference/jsxFactoryIdentifier.js.map index ea0db5040ef..5ecfbe9d87e 100644 --- a/tests/baselines/reference/jsxFactoryIdentifier.js.map +++ b/tests/baselines/reference/jsxFactoryIdentifier.js.map @@ -1,3 +1,4 @@ //// [Element.js.map] -{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}//// [test.js.map] +{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"} +//// [test.js.map] {"version":3,"file":"test.js","sourceRoot":"","sources":["test.tsx"],"names":[],"mappings":";;AAAA,uCAAmC;AACnC,IAAI,aAAa,GAAG,iBAAO,CAAC,aAAa,CAAC;AAC1C,IAAI,CAIH,CAAC;AAEF,MAAM,CAAC;IACN,IAAI;QACH,OAAO;YACN,wBAAM,OAAO,EAAC,YAAY,GAAQ;YAClC,wBAAM,OAAO,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,GAAS;SAC9B,CAAC;IACH,CAAC;CACD"} \ No newline at end of file diff --git a/tests/baselines/reference/jsxFactoryQualifiedName.js b/tests/baselines/reference/jsxFactoryQualifiedName.js index 63b45e878dd..14906d7a087 100644 --- a/tests/baselines/reference/jsxFactoryQualifiedName.js +++ b/tests/baselines/reference/jsxFactoryQualifiedName.js @@ -67,7 +67,8 @@ exports.createElement = Element.createElement; function toCamelCase(text) { return text[0].toLowerCase() + text.substring(1); } -//# sourceMappingURL=Element.js.map//// [test.js] +//# sourceMappingURL=Element.js.map +//// [test.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Element_1 = require("./Element"); diff --git a/tests/baselines/reference/jsxFactoryQualifiedName.js.map b/tests/baselines/reference/jsxFactoryQualifiedName.js.map index 4767276bd10..619537c50ef 100644 --- a/tests/baselines/reference/jsxFactoryQualifiedName.js.map +++ b/tests/baselines/reference/jsxFactoryQualifiedName.js.map @@ -1,3 +1,4 @@ //// [Element.js.map] -{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}//// [test.js.map] +{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"} +//// [test.js.map] {"version":3,"file":"test.js","sourceRoot":"","sources":["test.tsx"],"names":[],"mappings":";;AAAA,uCAAmC;AAEnC,IAAI,CAIH,CAAC;AAEF,MAAM,CAAC;IACN,IAAI;QACH,OAAO;YACN,0CAAM,OAAO,EAAC,YAAY,GAAQ;YAClC,0CAAM,OAAO,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,GAAS;SAC9B,CAAC;IACH,CAAC;CACD"} \ No newline at end of file diff --git a/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js b/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js index 7b7abae996c..490f38acf44 100644 --- a/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js +++ b/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js @@ -18,7 +18,8 @@ var c = /** @class */ (function () { } return c; }()); -//# sourceMappingURL=app.js.map//// [app2.js] +//# sourceMappingURL=app.js.map +//// [app2.js] var d = /** @class */ (function () { function d() { } diff --git a/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js.map b/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js.map index 7a172c4e0a7..b517b5694b0 100644 --- a/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js.map +++ b/tests/baselines/reference/sourceMapWithCaseSensitiveFileNamesAndOutDir.js.map @@ -1,3 +1,4 @@ //// [app.js.map] -{"version":3,"file":"app.js","sourceRoot":"","sources":["../testFiles/app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wIAAwI;AACxI;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}//// [app2.js.map] +{"version":3,"file":"app.js","sourceRoot":"","sources":["../testFiles/app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wIAAwI;AACxI;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"} +//// [app2.js.map] {"version":3,"file":"app2.js","sourceRoot":"","sources":["../testFiles/app2.ts"],"names":[],"mappings":"AAAA;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"} \ No newline at end of file diff --git a/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js b/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js index 3487a70dc7b..6e49d774aa9 100644 --- a/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js +++ b/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js @@ -18,7 +18,8 @@ var c = /** @class */ (function () { } return c; }()); -//# sourceMappingURL=app.js.map//// [app2.js] +//# sourceMappingURL=app.js.map +//// [app2.js] var d = /** @class */ (function () { function d() { } diff --git a/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js.map b/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js.map index 5025ed2212a..ef9951d2519 100644 --- a/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js.map +++ b/tests/baselines/reference/sourceMapWithNonCaseSensitiveFileNamesAndOutDir.js.map @@ -1,3 +1,4 @@ //// [app.js.map] -{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0GAA0G;AAC1G;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}//// [app2.js.map] +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0GAA0G;AAC1G;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"} +//// [app2.js.map] {"version":3,"file":"app2.js","sourceRoot":"","sources":["app2.ts"],"names":[],"mappings":"AAAA;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"} \ No newline at end of file