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
49689894d7/src/compiler/utilities.ts (L6972-L6976))
and the other one leaves the `skipTrivia` field `undefined` (see
5fc8f1dd80/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.
This commit is contained in:
Pete Bacon Darwin
2019-01-24 10:20:17 +00:00
parent 2f6c65e691
commit 331b9bcfde
3 changed files with 37 additions and 2 deletions

View File

@@ -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);
}
/**

View File

@@ -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 }
);
});
}

View File

@@ -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