Fix source maps for arrow functions, comments in sourcemap writer.

This commit is contained in:
Ron Buckton
2016-05-11 13:33:59 -07:00
parent d594865aaa
commit b69e65f1f9
6 changed files with 481 additions and 79 deletions

View File

@@ -155,7 +155,8 @@ const _super = (function (geti, seti) {
const {
emitStart,
emitEnd,
emitPos
emitTokenStart,
emitTokenEnd
} = sourceMap;
const comments = createCommentWriter(host, writer, sourceMap);
@@ -172,8 +173,9 @@ const _super = (function (geti, seti) {
let context: TransformationContext;
let getNodeEmitFlags: (node: Node) => NodeEmitFlags;
let setNodeEmitFlags: (node: Node, flags: NodeEmitFlags) => void;
let getCommentRange: (node: Node) => TextRange;
let getSourceMapRange: (node: Node) => TextRange;
let getTokenSourceMapRange: (node: Node, token: SyntaxKind) => TextRange;
let getCommentRange: (node: Node) => TextRange;
let isSubstitutionEnabled: (node: Node) => boolean;
let isEmitNotificationEnabled: (node: Node) => boolean;
let onSubstituteNode: (node: Node, isExpression: boolean) => Node;
@@ -234,8 +236,9 @@ const _super = (function (geti, seti) {
getNodeEmitFlags = undefined;
setNodeEmitFlags = undefined;
getCommentRange = undefined;
getSourceMapRange = undefined;
getTokenSourceMapRange = undefined;
getCommentRange = undefined;
isSubstitutionEnabled = undefined;
isEmitNotificationEnabled = undefined;
onSubstituteNode = undefined;
@@ -255,8 +258,9 @@ const _super = (function (geti, seti) {
context = _context;
getNodeEmitFlags = context.getNodeEmitFlags;
setNodeEmitFlags = context.setNodeEmitFlags;
getCommentRange = context.getCommentRange;
getSourceMapRange = context.getSourceMapRange;
getTokenSourceMapRange = context.getTokenSourceMapRange;
getCommentRange = context.getCommentRange;
isSubstitutionEnabled = context.isSubstitutionEnabled;
isEmitNotificationEnabled = context.isEmitNotificationEnabled;
onSubstituteNode = context.onSubstituteNode;
@@ -343,9 +347,9 @@ const _super = (function (geti, seti) {
const leadingComments = getLeadingComments(node, shouldSkipLeadingCommentsForNode, getCommentRange);
const trailingComments = getTrailingComments(node, shouldSkipTrailingCommentsForNode, getCommentRange);
emitLeadingComments(node, leadingComments, getCommentRange);
emitStart(node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitWorker(node);
emitEnd(node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitTrailingComments(node, trailingComments);
}
}
@@ -1646,7 +1650,7 @@ const _super = (function (geti, seti) {
emitTrailingDetachedComments(body.statements, body, shouldSkipTrailingCommentsForNode);
decreaseIndent();
writeToken(SyntaxKind.CloseBraceToken, body.statements.end);
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body);
}
function emitClassDeclaration(node: ClassDeclaration) {
@@ -2410,12 +2414,10 @@ const _super = (function (geti, seti) {
}
}
function writeToken(token: SyntaxKind, tokenStartPos: number, contextNode?: Node) {
tokenStartPos = skipTrivia(currentText, tokenStartPos);
emitPos(tokenStartPos, contextNode, shouldSkipLeadingSourceMapForToken);
function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
const tokenStartPos = emitTokenStart(token, pos, contextNode, shouldSkipLeadingSourceMapForToken, getTokenSourceMapRange);
const tokenEndPos = writeTokenText(token, tokenStartPos);
emitPos(tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken);
return tokenEndPos;
return emitTokenEnd(token, tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken, getTokenSourceMapRange);
}
function shouldSkipLeadingSourceMapForToken(contextNode: Node) {
@@ -2434,9 +2436,9 @@ const _super = (function (geti, seti) {
function writeTokenNode(node: Node) {
if (node) {
emitStart(node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
writeTokenText(node.kind);
emitEnd(node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
}
}

View File

@@ -3,25 +3,197 @@
/* @internal */
namespace ts {
export interface SourceMapWriter {
/**
* Initialize the SourceMapWriter for a new output file.
*
* @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file.
* @param sourceFiles The input source files for the program.
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void;
/**
* Reset the SourceMapWriter to an empty state.
*/
reset(): void;
/**
* Gets test data for source maps.
*/
getSourceMapData(): SourceMapData;
/**
* Set the current source file.
*
* @param sourceFile The source file.
*/
setSourceFile(sourceFile: SourceFile): void;
emitPos(pos: number, contextNode: Node, shouldIgnorePosCallback: (node: Node) => boolean): void;
/**
* Emits a mapping.
*
* If the position is synthetic (undefined or a negative value), no mapping will be
* created.
*
* @param pos The position.
*/
emitPos(pos: number): void;
emitStart(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void;
/**
* Emits a mapping for the start of a range.
*
* If the range's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.
*/
emitStart(range: TextRange): void;
emitEnd(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void;
/**
* Emits a mapping for the start of a range.
*
* If the node's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param shouldIgnoreNodeCallback A callback used to determine whether to skip source map
* emit for the start position of this node.
* @param shouldIgnoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getCustomSourceMapRangeForNodeCallback A callback used to get a custom source map
* range for this node.
*/
emitStart(range: TextRange, contextNode: Node, shouldIgnoreNodeCallback: (node: Node) => boolean, shouldIgnoreChildrenCallback: (node: Node) => boolean, getCustomSourceMapRangeForNodeCallback: (node: Node) => TextRange): void;
/**
* Emits a mapping for the end of a range.
*
* If the range's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
*/
emitEnd(range: TextRange): void;
/**
* Emits a mapping for the end of a range.
*
* If the node's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param shouldIgnoreNodeCallback A callback used to determine whether to skip source map
* emit for the end position of this node.
* @param shouldIgnoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getCustomSourceMapRangeForNodeCallback A callback used to get a custom source map
* range for this node.
*/
emitEnd(range: TextRange, contextNode: Node, shouldIgnoreNodeCallback: (node: Node) => boolean, shouldIgnoreChildrenCallback: (node: Node) => boolean, getCustomSourceMapRangeForNodeCallback: (node: Node) => TextRange): void;
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @returns The start position of the token, following any trivia.
*/
emitTokenStart(token: SyntaxKind, tokenStartPos: number): number;
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @param contextNode The node containing this token.
* @param shouldIgnoreTokenCallback A callback used to determine whether to skip source map
* emit for the start position of this token.
* @param getCustomSourceMapRangeForTokenCallback A callback used to get a custom source
* map range for this node.
* @returns The start position of the token, following any trivia.
*/
emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode: Node, shouldIgnoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getCustomSourceMapRangeForTokenCallback: (node: Node, token: SyntaxKind) => TextRange): number;
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @returns The end position of the token.
*/
emitTokenEnd(token: SyntaxKind, tokenEndPos: number): number;
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @param contextNode The node containing this token.
* @param shouldIgnoreTokenCallback A callback used to determine whether to skip source map
* emit for the end position of this token.
* @param getCustomSourceMapRangeForTokenCallback A callback used to get a custom source
* map range for this node.
* @returns The end position of the token.
*/
emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode: Node, shouldIgnoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getCustomSourceMapRangeForTokenCallback: (node: Node, token: SyntaxKind) => TextRange): number;
/*@deprecated*/ changeEmitSourcePos(): void;
/*@deprecated*/ stopOverridingSpan(): void;
/**
* Gets the text for the source map.
*/
getText(): string;
/**
* Gets the SourceMappingURL for the source map.
*/
getSourceMappingURL(): string;
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void;
reset(): void;
enable(): void;
disable(): void;
}
let nullSourceMapWriter: SourceMapWriter;
export function getNullSourceMapWriter(): SourceMapWriter {
if (nullSourceMapWriter === undefined) {
nullSourceMapWriter = {
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { },
reset(): void { },
getSourceMapData(): SourceMapData { return undefined; },
setSourceFile(sourceFile: SourceFile): void { },
emitPos(pos: number): void { },
emitStart(range: TextRange, contextNode?: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void { },
emitEnd(range: TextRange, contextNode?: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void { },
emitTokenStart(token: SyntaxKind, pos: number, contextNode?: Node, shouldIgnoreTokenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForTokenCallback?: (node: Node, token: SyntaxKind, pos: number) => TextRange): number { return -1; },
emitTokenEnd(token: SyntaxKind, end: number, contextNode?: Node, shouldIgnoreTokenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForTokenCallback?: (node: Node, token: SyntaxKind, pos: number) => TextRange): number { return -1; },
changeEmitSourcePos(): void { },
stopOverridingSpan(): void { },
getText(): string { return undefined; },
getSourceMappingURL(): string { return undefined; }
};
}
return nullSourceMapWriter;
}
// Used for initialize lastEncodedSourceMapSpan and reset lastEncodedSourceMapSpan when updateLastEncodedAndRecordedSpans
const defaultLastEncodedSourceMapSpan: SourceMapSpan = {
emittedLine: 1,
@@ -31,31 +203,10 @@ namespace ts {
sourceIndex: 0
};
export function getNullSourceMapWriter(): SourceMapWriter {
if (nullSourceMapWriter === undefined) {
nullSourceMapWriter = {
getSourceMapData(): SourceMapData { return undefined; },
setSourceFile(sourceFile: SourceFile): void { },
emitStart(range: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void { },
emitEnd(range: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void { },
emitPos(pos: number): void { },
changeEmitSourcePos(): void { },
stopOverridingSpan(): void { },
getText(): string { return undefined; },
getSourceMappingURL(): string { return undefined; },
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { },
reset(): void { },
enable(): void { },
disable(): void { }
};
}
return nullSourceMapWriter;
}
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
let currentSourceFile: SourceFile;
let currentSourceText: string;
let sourceMapDir: string; // The directory in which sourcemap will be
let stopOverridingSpan = false;
let modifyLastSourcePos = false;
@@ -79,27 +230,36 @@ namespace ts {
let disableDepth: number;
return {
initialize,
reset,
getSourceMapData: () => sourceMapData,
setSourceFile,
emitPos,
emitStart,
emitEnd,
emitTokenStart,
emitTokenEnd,
changeEmitSourcePos,
stopOverridingSpan: () => stopOverridingSpan = true,
getText,
getSourceMappingURL,
initialize,
reset,
enable,
disable,
};
/**
* Initialize the SourceMapWriter for a new output file.
*
* @param filePath The path to the generated output file.
* @param sourceMapFilePath The path to the output source map file.
* @param sourceFiles The input source files for the program.
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/
function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
if (sourceMapData) {
reset();
}
currentSourceFile = undefined;
currentSourceText = undefined;
disableDepth = 0;
// Current source map file and its index in the sources list
@@ -159,6 +319,9 @@ namespace ts {
}
}
/**
* Reset the SourceMapWriter to an empty state.
*/
function reset() {
currentSourceFile = undefined;
sourceMapDir = undefined;
@@ -272,15 +435,19 @@ namespace ts {
sourceMapData.sourceMapDecodedMappings.push(lastEncodedSourceMapSpan);
}
function emitPos(pos: number, contextNode?: Node, shouldIgnorePosCallback?: (node: Node) => boolean) {
/**
* Emits a mapping.
*
* If the position is synthetic (undefined or a negative value), no mapping will be
* created.
*
* @param pos The position.
*/
function emitPos(pos: number) {
if (positionIsSynthesized(pos) || disableDepth > 0) {
return;
}
if (shouldIgnorePosCallback && shouldIgnorePosCallback(contextNode)) {
return;
}
const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos);
// Convert the location to be one-based.
@@ -322,54 +489,205 @@ namespace ts {
updateLastEncodedAndRecordedSpans();
}
function getStartPos(range: TextRange) {
function getStartPosPastDecorators(range: TextRange) {
const rangeHasDecorators = !!(range as Node).decorators;
return range.pos !== -1 ? skipTrivia(currentSourceFile.text, rangeHasDecorators ? (range as Node).decorators.end : range.pos) : -1;
return skipTrivia(currentSourceText, rangeHasDecorators ? (range as Node).decorators.end : range.pos);
}
function getStartPos(range: TextRange) {
return skipTrivia(currentSourceText, range.pos);
}
/**
* Emits a mapping for the start of a range.
*
* If the range's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.0
*/
function emitStart(range: TextRange): void;
function emitStart(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void;
function emitStart(nodeOrRange: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange) {
let range = <TextRange>nodeOrRange;
if (!(shouldIgnoreNodeCallback && shouldIgnoreNodeCallback(<Node>nodeOrRange))) {
if (getCustomSourceMapRangeForNode) {
range = getCustomSourceMapRangeForNode(<Node>nodeOrRange) || range;
/**
* Emits a mapping for the start of a range.
*
* If the node's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param shouldIgnoreNodeCallback A callback used to determine whether to skip source map
* emit for the start position of this node.
* @param shouldIgnoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getCustomSourceMapRangeForNodeCallback A callback used to get a custom source map
* range for this node.
*/
function emitStart(range: TextRange, contextNode: Node, shouldIgnoreNodeCallback: (node: Node) => boolean, shouldIgnoreChildrenCallback: (node: Node) => boolean, getCustomSourceMapRangeForNode: (node: Node) => TextRange): void;
function emitStart(range: TextRange, contextNode?: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange) {
if (contextNode) {
if (!shouldIgnoreNodeCallback(contextNode)) {
range = getCustomSourceMapRangeForNode(contextNode) || range;
emitPos(getStartPosPastDecorators(range));
}
emitPos(getStartPos(range));
if (shouldIgnoreChildrenCallback(contextNode)) {
disable();
}
}
if (shouldIgnoreChildrenCallback && shouldIgnoreChildrenCallback(<Node>nodeOrRange)) {
disable();
else {
emitPos(getStartPosPastDecorators(range));
}
}
/**
* Emits a mapping for the end of a range.
*
* If the range's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
*/
function emitEnd(range: TextRange): void;
function emitEnd(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void;
function emitEnd(nodeOrRange: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange) {
let range = <TextRange>nodeOrRange;
if (shouldIgnoreChildrenCallback && shouldIgnoreChildrenCallback(<Node>nodeOrRange)) {
enable();
}
if (!(shouldIgnoreNodeCallback && shouldIgnoreNodeCallback(<Node>nodeOrRange))) {
if (getCustomSourceMapRangeForNode) {
range = getCustomSourceMapRangeForNode(<Node>nodeOrRange) || range;
/**
* Emits a mapping for the end of a range.
*
* If the node's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param shouldIgnoreNodeCallback A callback used to determine whether to skip source map
* emit for the end position of this node.
* @param shouldIgnoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getCustomSourceMapRangeForNodeCallback A callback used to get a custom source map
* range for this node.
*/
function emitEnd(range: TextRange, contextNode: Node, shouldIgnoreNodeCallback: (node: Node) => boolean, shouldIgnoreChildrenCallback: (node: Node) => boolean, getCustomSourceMapRangeForNode: (node: Node) => TextRange): void;
function emitEnd(range: TextRange, contextNode?: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange) {
if (contextNode) {
if (shouldIgnoreChildrenCallback(contextNode)) {
enable();
}
if (!shouldIgnoreNodeCallback(contextNode)) {
range = getCustomSourceMapRangeForNode(contextNode) || range;
emitPos(range.end);
}
}
else {
emitPos(range.end);
}
stopOverridingSpan = false;
}
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @returns The start position of the token, following any trivia.
*/
function emitTokenStart(token: SyntaxKind, tokenStartPos: number): number;
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @param contextNode The node containing this token.
* @param shouldIgnoreTokenCallback A callback used to determine whether to skip source map
* emit for the start position of this token.
* @param getCustomSourceMapRangeForTokenCallback A callback used to get a custom source
* map range for this node.
* @returns The start position of the token, following any trivia.
*/
function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode: Node, shouldIgnoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getCustomSourceMapRangeForTokenCallback: (node: Node, token: SyntaxKind) => TextRange): number;
function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, shouldIgnoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getCustomSourceMapRangeForTokenCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
if (contextNode) {
if (shouldIgnoreTokenCallback(contextNode, token)) {
return skipTrivia(currentSourceText, tokenStartPos);
}
const range = getCustomSourceMapRangeForTokenCallback(contextNode, token);
if (range) {
tokenStartPos = range.pos;
}
}
tokenStartPos = skipTrivia(currentSourceText, tokenStartPos);
emitPos(tokenStartPos);
return tokenStartPos;
}
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @returns The end position of the token.
*/
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number): number;
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @param contextNode The node containing this token.
* @param shouldIgnoreTokenCallback A callback used to determine whether to skip source map
* emit for the end position of this token.
* @param getCustomSourceMapRangeForTokenCallback A callback used to get a custom source
* map range for this node.
* @returns The end position of the token.
*/
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode: Node, shouldIgnoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getCustomSourceMapRangeForTokenCallback: (node: Node, token: SyntaxKind) => TextRange): number;
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, shouldIgnoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getCustomSourceMapRangeForTokenCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
if (contextNode) {
if (shouldIgnoreTokenCallback(contextNode, token)) {
return tokenEndPos;
}
const range = getCustomSourceMapRangeForTokenCallback(contextNode, token);
if (range) {
tokenEndPos = range.end;
}
}
emitPos(tokenEndPos);
return tokenEndPos;
}
// @deprecated
function changeEmitSourcePos() {
Debug.assert(!modifyLastSourcePos);
modifyLastSourcePos = true;
}
/**
* Set the current source file.
*
* @param sourceFile The source file.
*/
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
currentSourceText = sourceFile.text;
// Add the file to tsFilePaths
// If sourceroot option: Use the relative path corresponding to the common directory path
@@ -396,6 +714,9 @@ namespace ts {
}
}
/**
* Gets the text for the source map.
*/
function getText() {
encodeLastRecordedSourceMapSpan();
@@ -410,6 +731,9 @@ namespace ts {
});
}
/**
* Gets the SourceMappingURL for the source map.
*/
function getSourceMappingURL() {
if (compilerOptions.inlineSourceMap) {
// Encode the sourceMap into the sourceMap url

View File

@@ -74,6 +74,8 @@ namespace ts {
setNodeEmitFlags,
getSourceMapRange,
setSourceMapRange,
getTokenSourceMapRange,
setTokenSourceMapRange,
getCommentRange,
setCommentRange,
hoistVariableDeclaration,
@@ -237,6 +239,47 @@ namespace ts {
return node;
}
function getTokenSourceMapRanges(node: Node) {
let current = node;
while (current) {
const options = getEmitOptions(current);
if (options && options.tokenSourceMapRange) {
return options.tokenSourceMapRange;
}
current = current.original;
}
return undefined;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
const ranges = getTokenSourceMapRanges(node);
if (ranges) {
return ranges[token];
}
return undefined;
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
const options = getEmitOptions(node, /*create*/ true);
if (!options.tokenSourceMapRange) {
const existingRanges = getTokenSourceMapRanges(node);
const ranges = existingRanges ? clone(existingRanges) : { };
options.tokenSourceMapRange = ranges;
}
options.tokenSourceMapRange[token] = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*/

View File

@@ -149,6 +149,7 @@ namespace ts {
setCommentRange,
getSourceMapRange,
setSourceMapRange,
setTokenSourceMapRange,
} = context;
const resolver = context.getEmitResolver();
@@ -1313,6 +1314,7 @@ namespace ts {
let multiLine = false; // indicates whether the block *must* be emitted as multiple lines
let singleLine = false; // indicates whether the block *may* be emitted as a single line
let statementsLocation: TextRange;
let closeBraceLocation: TextRange;
const statements: Statement[] = [];
const body = node.body;
@@ -1363,11 +1365,14 @@ namespace ts {
}
const expression = visitNode(body, visitor, isExpression);
if (expression) {
const returnStatement = createReturn(expression, /*location*/ statementsLocation);
setNodeEmitFlags(returnStatement, NodeEmitFlags.NoTokenSourceMaps);
statements.push(returnStatement);
}
const returnStatement = createReturn(expression);
setSourceMapRange(returnStatement, body);
setNodeEmitFlags(returnStatement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoTrailingSourceMap);
statements.push(returnStatement);
// To align with the source map emit for the old emitter, we set a custom
// source map location for the close brace.
closeBraceLocation = body;
}
const lexicalEnvironment = endLexicalEnvironment();
@@ -1383,6 +1388,10 @@ namespace ts {
setNodeEmitFlags(block, NodeEmitFlags.SingleLine);
}
if (closeBraceLocation) {
setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation);
}
setOriginalNode(block, node.body);
return block;
}

View File

@@ -2963,6 +2963,10 @@ namespace ts {
* Specifies a custom range to use when emitting source maps.
*/
sourceMapRange?: TextRange;
/**
* Specifies a custom range to use when emitting tokens of a node.
*/
tokenSourceMapRange?: Map<TextRange>;
/**
* Specifies a custom range to use when emitting comments.
*/
@@ -3009,6 +3013,16 @@ namespace ts {
*/
setSourceMapRange<T extends Node>(node: T, range: TextRange): T;
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange;
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange): T;
/**
* Gets the TextRange to use for comments for the node.
*/

View File

@@ -3058,6 +3058,16 @@ namespace ts {
}
}
/**
* Increases (or decreases) a position by the provided amount.
*
* @param pos The position.
* @param value The delta.
*/
export function movePos(pos: number, value: number) {
return positionIsSynthesized(pos) ? -1 : pos + value;
}
/**
* Creates a new TextRange from the provided pos and end.
*