From 331b9bcfde86453d0e3d68126fc5c9fc46160591 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 24 Jan 2019 10:20:17 +0000 Subject: [PATCH] Use the correct source when skipping trivia A custom `SourceMapSource` can optionally provide its own `skipTrivia` function. If this is not provided then the compiler will use the default function designed for TypeScript source files. Previously, when calling this default function we were passing the current `sourceMapSource` rather than the specified `source` whose trivia needs to be skipped. This resulted in the `pos` being incorrectly calculated for external source files that need mapping. **Side note:** There are actually two possible constructors available for creating `SourceMapSource` objects. One of them defaults to an identity function for the `skipTrivia` function if it is not provided (see https://github.com/Microsoft/TypeScript/blob/49689894d747714d6b2b8461b9033020efec9625/src/compiler/utilities.ts#L6972-L6976) and the other one leaves the `skipTrivia` field `undefined` (see https://github.com/Microsoft/TypeScript/blob/5fc8f1dd801dbacfe7e2d624f80b7a6a3868d180/src/services/services.ts#L776-L797) Unfortunately, it appears that the second of these two constructors is the one available when importing the "typescript" module in node.js code. --- src/compiler/emitter.ts | 4 +-- src/testRunner/unittests/customTransforms.ts | 29 +++++++++++++++++++ .../skipTriviaExternalSourceFiles.js | 6 ++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/customTransforms/skipTriviaExternalSourceFiles.js diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 395f64488909b..afc2a025da512 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4372,10 +4372,10 @@ namespace ts { } /** - * Skips trivia such as comments and white-space that can optionally overriden by the source map source + * Skips trivia such as comments and white-space that can be optionally overridden by the source-map source */ function skipSourceTrivia(source: SourceMapSource, pos: number): number { - return source.skipTrivia ? source.skipTrivia(pos) : skipTrivia(sourceMapSource.text, pos); + return source.skipTrivia ? source.skipTrivia(pos) : skipTrivia(source.text, pos); } /** diff --git a/src/testRunner/unittests/customTransforms.ts b/src/testRunner/unittests/customTransforms.ts index aef9ed745ec56..0ac1434134231 100644 --- a/src/testRunner/unittests/customTransforms.ts +++ b/src/testRunner/unittests/customTransforms.ts @@ -129,5 +129,34 @@ namespace ts { }, { sourceMap: true } ); + + emitsCorrectly("skipTriviaExternalSourceFiles", + [ + { + file: "source.ts", + // The source file contains preceding trivia (e.g. whitespace) to try to confuse the `skipSourceTrivia` function. + text: " original;" + }, + ], + { + before: [ + context => node => visitNode(node, function visitor(node: Node): Node { + if (isIdentifier(node) && node.text === "original") { + const newNode = createIdentifier("changed"); + setSourceMapRange(newNode, { + pos: 0, + end: 7, + // Do not provide a custom skipTrivia function for `source`. + source: createSourceMapSource("another.html", "changed;") + }); + return newNode; + } + return visitEachChild(node, visitor, context); + }) + ] + }, + { sourceMap: true } + ); + }); } diff --git a/tests/baselines/reference/customTransforms/skipTriviaExternalSourceFiles.js b/tests/baselines/reference/customTransforms/skipTriviaExternalSourceFiles.js new file mode 100644 index 0000000000000..8c7fecf97f0bb --- /dev/null +++ b/tests/baselines/reference/customTransforms/skipTriviaExternalSourceFiles.js @@ -0,0 +1,6 @@ +// [source.js.map] +{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts","another.html"],"names":[],"mappings":"ACAA,OAAO,CDAW"} + +// [source.js] +changed; +//# sourceMappingURL=source.js.map \ No newline at end of file