mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-15 16:53:31 -06:00
Clean up SourceMapWriter and emitter.
This commit is contained in:
parent
f2de4508df
commit
c1ee534974
@ -203,10 +203,8 @@ const _super = (function (geti, seti) {
|
||||
|
||||
const sourceMap = createSourceMapWriter(host, writer);
|
||||
const {
|
||||
emitStart,
|
||||
emitEnd,
|
||||
emitTokenStart,
|
||||
emitTokenEnd
|
||||
emitNodeWithSourceMap,
|
||||
emitTokenWithSourceMap
|
||||
} = sourceMap;
|
||||
|
||||
const comments = createCommentWriter(host, writer, sourceMap);
|
||||
@ -244,10 +242,8 @@ const _super = (function (geti, seti) {
|
||||
|
||||
// Extract helpers from the result
|
||||
const {
|
||||
isSubstitutionEnabled,
|
||||
isEmitNotificationEnabled,
|
||||
onSubstituteNode,
|
||||
onEmitNode
|
||||
emitNodeWithSubstitution,
|
||||
emitNodeWithNotification
|
||||
} = transformed;
|
||||
|
||||
performance.mark("beforePrint");
|
||||
@ -448,101 +444,6 @@ const _super = (function (geti, seti) {
|
||||
emitNodeWithSubstitution(node, /*isExpression*/ true, emitExpressionWorker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with possible emit notification.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into transformer.ts
|
||||
function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (node) {
|
||||
if (isEmitNotificationEnabled(node)) {
|
||||
onEmitNode(node, emitCallback);
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with possible source maps.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into sourcemap.ts
|
||||
function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (node) {
|
||||
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
|
||||
emitCallback(node);
|
||||
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to skip leading comment emit for a node.
|
||||
*
|
||||
* We do not emit comments for NotEmittedStatement nodes or any node that has
|
||||
* NodeEmitFlags.NoLeadingComments.
|
||||
*
|
||||
* @param node A Node.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into comments.ts
|
||||
function shouldSkipLeadingCommentsForNode(node: Node) {
|
||||
return isNotEmittedStatement(node)
|
||||
|| (getEmitFlags(node) & EmitFlags.NoLeadingComments) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to skip source map emit for the start position of a node.
|
||||
*
|
||||
* We do not emit source maps for NotEmittedStatement nodes or any node that
|
||||
* has NodeEmitFlags.NoLeadingSourceMap.
|
||||
*
|
||||
* @param node A Node.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into sourcemap.ts
|
||||
function shouldSkipLeadingSourceMapForNode(node: Node) {
|
||||
return isNotEmittedStatement(node)
|
||||
|| (getEmitFlags(node) & EmitFlags.NoLeadingSourceMap) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to skip source map emit for the end position of a node.
|
||||
*
|
||||
* We do not emit source maps for NotEmittedStatement nodes or any node that
|
||||
* has NodeEmitFlags.NoTrailingSourceMap.
|
||||
*
|
||||
* @param node A Node.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into sourcemap.ts
|
||||
function shouldSkipTrailingSourceMapForNode(node: Node) {
|
||||
return isNotEmittedStatement(node)
|
||||
|| (getEmitFlags(node) & EmitFlags.NoTrailingSourceMap) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to skip source map emit for a node and its children.
|
||||
*
|
||||
* We do not emit source maps for a node that has NodeEmitFlags.NoNestedSourceMaps.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into sourcemap.ts
|
||||
function shouldSkipSourceMapForChildren(node: Node) {
|
||||
return (getEmitFlags(node) & EmitFlags.NoNestedSourceMaps) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with possible substitution.
|
||||
*/
|
||||
// TODO(rbuckton): Move this into transformer.ts
|
||||
function emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void) {
|
||||
if (isSubstitutionEnabled(node) && (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0) {
|
||||
const substitute = onSubstituteNode(node, isExpression);
|
||||
if (substitute !== node) {
|
||||
emitCallback(substitute);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emitCallback(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node.
|
||||
*
|
||||
@ -585,7 +486,7 @@ const _super = (function (geti, seti) {
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.GlobalKeyword:
|
||||
return writeTokenNode(node);
|
||||
return emitTokenNode(node);
|
||||
|
||||
// Parse tree nodes
|
||||
|
||||
@ -832,7 +733,7 @@ const _super = (function (geti, seti) {
|
||||
case SyntaxKind.SuperKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
return writeTokenNode(node);
|
||||
return emitTokenNode(node);
|
||||
|
||||
// Expressions
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
@ -2129,7 +2030,7 @@ const _super = (function (geti, seti) {
|
||||
// "comment1" is not considered to be leading comment for node.initializer
|
||||
// but rather a trailing comment on the previous node.
|
||||
const initializer = node.initializer;
|
||||
if (!shouldSkipLeadingCommentsForNode(initializer)) {
|
||||
if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
|
||||
const commentRange = getCommentRange(initializer);
|
||||
emitTrailingCommentsOfPosition(commentRange.pos);
|
||||
}
|
||||
@ -2537,17 +2438,7 @@ const _super = (function (geti, seti) {
|
||||
}
|
||||
|
||||
function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
|
||||
const tokenStartPos = emitTokenStart(token, pos, contextNode, shouldSkipLeadingSourceMapForToken, getTokenSourceMapRange);
|
||||
const tokenEndPos = writeTokenText(token, tokenStartPos);
|
||||
return emitTokenEnd(token, tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken, getTokenSourceMapRange);
|
||||
}
|
||||
|
||||
function shouldSkipLeadingSourceMapForToken(contextNode: Node) {
|
||||
return (getEmitFlags(contextNode) & EmitFlags.NoTokenLeadingSourceMaps) !== 0;
|
||||
}
|
||||
|
||||
function shouldSkipTrailingSourceMapForToken(contextNode: Node) {
|
||||
return (getEmitFlags(contextNode) & EmitFlags.NoTokenTrailingSourceMaps) !== 0;
|
||||
return emitTokenWithSourceMap(contextNode, token, pos, writeTokenText);
|
||||
}
|
||||
|
||||
function writeTokenText(token: SyntaxKind, pos?: number) {
|
||||
@ -2556,12 +2447,12 @@ const _super = (function (geti, seti) {
|
||||
return positionIsSynthesized(pos) ? -1 : pos + tokenString.length;
|
||||
}
|
||||
|
||||
function writeTokenNode(node: Node) {
|
||||
if (node) {
|
||||
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
|
||||
writeTokenText(node.kind);
|
||||
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
|
||||
}
|
||||
function emitTokenNode(node: Node) {
|
||||
emitNodeWithSourceMap(node, emitTokenNodeWorker);
|
||||
}
|
||||
|
||||
function emitTokenNodeWorker(node: Node) {
|
||||
writeTokenText(node.kind);
|
||||
}
|
||||
|
||||
function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) {
|
||||
|
||||
@ -18,11 +18,6 @@ namespace ts {
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* Gets test data for source maps.
|
||||
*/
|
||||
getSourceMapData(): SourceMapData;
|
||||
|
||||
/**
|
||||
* Set the current source file.
|
||||
*
|
||||
@ -41,123 +36,22 @@ namespace ts {
|
||||
emitPos(pos: number): void;
|
||||
|
||||
/**
|
||||
* Emits a mapping for the start of a range.
|
||||
* Emits a node with possible leading and trailing source maps.
|
||||
*
|
||||
* 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.
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback The callback used to emit the node.
|
||||
*/
|
||||
emitStart(range: TextRange): void;
|
||||
emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void): 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 ignoreNodeCallback A callback used to determine whether to skip source map
|
||||
* emit for the start position of this node.
|
||||
* @param ignoreChildrenCallback A callback used to determine whether to skip source
|
||||
* map emit for all children of this node.
|
||||
* @param getTextRangeCallbackCallback A callback used to get a custom source map
|
||||
* range for this node.
|
||||
*/
|
||||
emitStart(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallbackCallback: (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 ignoreNodeCallback A callback used to determine whether to skip source map
|
||||
* emit for the end position of this node.
|
||||
* @param ignoreChildrenCallback A callback used to determine whether to skip source
|
||||
* map emit for all children of this node.
|
||||
* @param getTextRangeCallbackCallback A callback used to get a custom source map
|
||||
* range for this node.
|
||||
*/
|
||||
emitEnd(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallbackCallback: (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.
|
||||
* Emits a token of a node node with possible leading and trailing source maps.
|
||||
*
|
||||
* @param node The node containing the token.
|
||||
* @param token The token to emit.
|
||||
* @param tokenStartPos The start position of the token.
|
||||
* @returns The start position of the token, following any trivia.
|
||||
* @param tokenStartPos The start pos of the token.
|
||||
* @param emitCallback The callback used to emit the token.
|
||||
*/
|
||||
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 ignoreTokenCallback A callback used to determine whether to skip source map
|
||||
* emit for the start position of this token.
|
||||
* @param getTokenTextRangeCallback 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, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (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 ignoreTokenCallback A callback used to determine whether to skip source map
|
||||
* emit for the end position of this token.
|
||||
* @param getTokenTextRangeCallback 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, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
|
||||
|
||||
/*@deprecated*/ changeEmitSourcePos(): void;
|
||||
/*@deprecated*/ stopOverridingSpan(): void;
|
||||
emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenStartPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number): number;
|
||||
|
||||
/**
|
||||
* Gets the text for the source map.
|
||||
@ -168,44 +62,11 @@ namespace ts {
|
||||
* Gets the SourceMappingURL for the source map.
|
||||
*/
|
||||
getSourceMappingURL(): string;
|
||||
}
|
||||
|
||||
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
|
||||
const compilerOptions = host.getCompilerOptions();
|
||||
if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) {
|
||||
if (compilerOptions.extendedDiagnostics) {
|
||||
return createSourceMapWriterWithExtendedDiagnostics(host, writer);
|
||||
}
|
||||
|
||||
return createSourceMapWriterWorker(host, writer);
|
||||
}
|
||||
else {
|
||||
return getNullSourceMapWriter();
|
||||
}
|
||||
}
|
||||
|
||||
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, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { },
|
||||
emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { },
|
||||
emitTokenStart(token: SyntaxKind, pos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; },
|
||||
emitTokenEnd(token: SyntaxKind, end: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; },
|
||||
changeEmitSourcePos(): void { },
|
||||
stopOverridingSpan(): void { },
|
||||
getText(): string { return undefined; },
|
||||
getSourceMappingURL(): string { return undefined; }
|
||||
};
|
||||
}
|
||||
|
||||
return nullSourceMapWriter;
|
||||
/**
|
||||
* Gets test data for source maps.
|
||||
*/
|
||||
getSourceMapData(): SourceMapData;
|
||||
}
|
||||
|
||||
// Used for initialize lastEncodedSourceMapSpan and reset lastEncodedSourceMapSpan when updateLastEncodedAndRecordedSpans
|
||||
@ -217,14 +78,12 @@ namespace ts {
|
||||
sourceIndex: 0
|
||||
};
|
||||
|
||||
function createSourceMapWriterWorker(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
|
||||
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
|
||||
const compilerOptions = host.getCompilerOptions();
|
||||
const extendedDiagnostics = compilerOptions.extendedDiagnostics;
|
||||
let currentSourceFile: SourceFile;
|
||||
let currentSourceText: string;
|
||||
let sourceMapDir: string; // The directory in which sourcemap will be
|
||||
let stopOverridingSpan = false;
|
||||
let modifyLastSourcePos = false;
|
||||
|
||||
// Current source map file and its index in the sources list
|
||||
let sourceMapSourceIndex: number;
|
||||
@ -236,13 +95,7 @@ namespace ts {
|
||||
|
||||
// Source map data
|
||||
let sourceMapData: SourceMapData;
|
||||
|
||||
// This keeps track of the number of times `disable` has been called without a
|
||||
// corresponding call to `enable`. As long as this value is non-zero, mappings will not
|
||||
// be recorded.
|
||||
// This is primarily used to provide a better experience when debugging binding
|
||||
// patterns and destructuring assignments for simple expressions.
|
||||
let disableDepth: number;
|
||||
let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap);
|
||||
|
||||
return {
|
||||
initialize,
|
||||
@ -250,12 +103,8 @@ namespace ts {
|
||||
getSourceMapData: () => sourceMapData,
|
||||
setSourceFile,
|
||||
emitPos,
|
||||
emitStart,
|
||||
emitEnd,
|
||||
emitTokenStart,
|
||||
emitTokenEnd,
|
||||
changeEmitSourcePos,
|
||||
stopOverridingSpan: () => stopOverridingSpan = true,
|
||||
emitNodeWithSourceMap,
|
||||
emitTokenWithSourceMap,
|
||||
getText,
|
||||
getSourceMappingURL,
|
||||
};
|
||||
@ -269,13 +118,16 @@ namespace ts {
|
||||
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
|
||||
*/
|
||||
function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sourceMapData) {
|
||||
reset();
|
||||
}
|
||||
|
||||
currentSourceFile = undefined;
|
||||
currentSourceText = undefined;
|
||||
disableDepth = 0;
|
||||
|
||||
// Current source map file and its index in the sources list
|
||||
sourceMapSourceIndex = -1;
|
||||
@ -338,6 +190,10 @@ namespace ts {
|
||||
* Reset the SourceMapWriter to an empty state.
|
||||
*/
|
||||
function reset() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSourceFile = undefined;
|
||||
sourceMapDir = undefined;
|
||||
sourceMapSourceIndex = undefined;
|
||||
@ -345,64 +201,6 @@ namespace ts {
|
||||
lastEncodedSourceMapSpan = undefined;
|
||||
lastEncodedNameIndex = undefined;
|
||||
sourceMapData = undefined;
|
||||
disableDepth = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enables the recording of mappings.
|
||||
*/
|
||||
function enable() {
|
||||
if (disableDepth > 0) {
|
||||
disableDepth--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the recording of mappings.
|
||||
*/
|
||||
function disable() {
|
||||
disableDepth++;
|
||||
}
|
||||
|
||||
function updateLastEncodedAndRecordedSpans() {
|
||||
if (modifyLastSourcePos) {
|
||||
// Reset the source pos
|
||||
modifyLastSourcePos = false;
|
||||
|
||||
// Change Last recorded Map with last encoded emit line and character
|
||||
lastRecordedSourceMapSpan.emittedLine = lastEncodedSourceMapSpan.emittedLine;
|
||||
lastRecordedSourceMapSpan.emittedColumn = lastEncodedSourceMapSpan.emittedColumn;
|
||||
|
||||
// Pop sourceMapDecodedMappings to remove last entry
|
||||
sourceMapData.sourceMapDecodedMappings.pop();
|
||||
|
||||
// Point the lastEncodedSourceMapSpace to the previous encoded sourceMapSpan
|
||||
// If the list is empty which indicates that we are at the beginning of the file,
|
||||
// we have to reset it to default value (same value when we first initialize sourceMapWriter)
|
||||
lastEncodedSourceMapSpan = sourceMapData.sourceMapDecodedMappings.length ?
|
||||
sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
|
||||
defaultLastEncodedSourceMapSpan;
|
||||
|
||||
// TODO: Update lastEncodedNameIndex
|
||||
// Since we dont support this any more, lets not worry about it right now.
|
||||
// When we start supporting nameIndex, we will get back to this
|
||||
|
||||
// Change the encoded source map
|
||||
const sourceMapMappings = sourceMapData.sourceMapMappings;
|
||||
let lenthToSet = sourceMapMappings.length - 1;
|
||||
for (; lenthToSet >= 0; lenthToSet--) {
|
||||
const currentChar = sourceMapMappings.charAt(lenthToSet);
|
||||
if (currentChar === ",") {
|
||||
// Separator for the entry found
|
||||
break;
|
||||
}
|
||||
if (currentChar === ";" && lenthToSet !== 0 && sourceMapMappings.charAt(lenthToSet - 1) !== ";") {
|
||||
// Last line separator found
|
||||
break;
|
||||
}
|
||||
}
|
||||
sourceMapData.sourceMapMappings = sourceMapMappings.substr(0, Math.max(0, lenthToSet));
|
||||
}
|
||||
}
|
||||
|
||||
// Encoding for sourcemap span
|
||||
@ -459,7 +257,7 @@ namespace ts {
|
||||
* @param pos The position.
|
||||
*/
|
||||
function emitPos(pos: number) {
|
||||
if (positionIsSynthesized(pos) || disableDepth > 0) {
|
||||
if (disabled || positionIsSynthesized(pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -495,209 +293,89 @@ namespace ts {
|
||||
sourceColumn: sourceLinePos.character,
|
||||
sourceIndex: sourceMapSourceIndex
|
||||
};
|
||||
|
||||
stopOverridingSpan = false;
|
||||
}
|
||||
else if (!stopOverridingSpan) {
|
||||
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;
|
||||
}
|
||||
|
||||
updateLastEncodedAndRecordedSpans();
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
performance.mark("afterSourcemap");
|
||||
performance.measure("Source Map", "beforeSourcemap", "afterSourcemap");
|
||||
}
|
||||
}
|
||||
|
||||
function getStartPosPastDecorators(range: TextRange) {
|
||||
const rangeHasDecorators = !!(range as Node).decorators;
|
||||
return skipTrivia(currentSourceText, rangeHasDecorators ? (range as Node).decorators.end : range.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a mapping for the start of a range.
|
||||
* Emits a node with possible leading and trailing source maps.
|
||||
*
|
||||
* 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
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback The callback used to emit the node.
|
||||
*/
|
||||
function emitStart(range: 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 ignoreNodeCallback A callback used to determine whether to skip source map
|
||||
* emit for the start position of this node.
|
||||
* @param ignoreChildrenCallback A callback used to determine whether to skip source
|
||||
* map emit for all children of this node.
|
||||
* @param getTextRangeCallbackCallback A callback used to get a custom source map
|
||||
* range for this node.
|
||||
*/
|
||||
function emitStart(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallback: (node: Node) => TextRange): void;
|
||||
function emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange) {
|
||||
if (contextNode) {
|
||||
if (!ignoreNodeCallback(contextNode)) {
|
||||
range = getTextRangeCallback(contextNode) || range;
|
||||
emitPos(getStartPosPastDecorators(range));
|
||||
}
|
||||
|
||||
if (ignoreChildrenCallback(contextNode)) {
|
||||
disable();
|
||||
}
|
||||
function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (disabled) {
|
||||
return emitCallback(node);
|
||||
}
|
||||
else {
|
||||
emitPos(getStartPosPastDecorators(range));
|
||||
|
||||
if (node) {
|
||||
const emitNode = node.emitNode;
|
||||
const emitFlags = emitNode && emitNode.flags;
|
||||
const { pos, end } = emitNode && emitNode.sourceMapRange || node;
|
||||
|
||||
if (node.kind !== SyntaxKind.NotEmittedStatement
|
||||
&& (emitFlags & EmitFlags.NoLeadingSourceMap) === 0
|
||||
&& pos >= 0) {
|
||||
emitPos(skipTrivia(currentSourceText, pos));
|
||||
}
|
||||
|
||||
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
|
||||
disabled = true;
|
||||
emitCallback(node);
|
||||
disabled = false;
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
}
|
||||
|
||||
if (node.kind !== SyntaxKind.NotEmittedStatement
|
||||
&& (emitFlags & EmitFlags.NoTrailingSourceMap) === 0
|
||||
&& end >= 0) {
|
||||
emitPos(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* 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 ignoreNodeCallback A callback used to determine whether to skip source map
|
||||
* emit for the end position of this node.
|
||||
* @param ignoreChildrenCallback A callback used to determine whether to skip source
|
||||
* map emit for all children of this node.
|
||||
* @param getTextRangeCallbackCallback A callback used to get a custom source map
|
||||
* range for this node.
|
||||
*/
|
||||
function emitEnd(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallback: (node: Node) => TextRange): void;
|
||||
function emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange) {
|
||||
if (contextNode) {
|
||||
if (ignoreChildrenCallback(contextNode)) {
|
||||
enable();
|
||||
}
|
||||
|
||||
if (!ignoreNodeCallback(contextNode)) {
|
||||
range = getTextRangeCallback(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.
|
||||
* Emits a token of a node node with possible leading and trailing source maps.
|
||||
*
|
||||
* @param node The node containing the token.
|
||||
* @param token The token to emit.
|
||||
* @param tokenStartPos The start position of the token.
|
||||
* @returns The start position of the token, following any trivia.
|
||||
* @param tokenStartPos The start pos of the token.
|
||||
* @param emitCallback The callback used to emit the token.
|
||||
*/
|
||||
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 ignoreTokenCallback A callback used to determine whether to skip source map
|
||||
* emit for the start position of this token.
|
||||
* @param getTokenTextRangeCallback 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, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
|
||||
function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
|
||||
if (contextNode) {
|
||||
if (ignoreTokenCallback(contextNode, token)) {
|
||||
return skipTrivia(currentSourceText, tokenStartPos);
|
||||
}
|
||||
|
||||
const range = getTokenTextRangeCallback(contextNode, token);
|
||||
if (range) {
|
||||
tokenStartPos = range.pos;
|
||||
}
|
||||
function emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number) {
|
||||
if (disabled) {
|
||||
return emitCallback(token, tokenPos);
|
||||
}
|
||||
|
||||
tokenStartPos = skipTrivia(currentSourceText, tokenStartPos);
|
||||
emitPos(tokenStartPos);
|
||||
return tokenStartPos;
|
||||
}
|
||||
const emitNode = node && node.emitNode;
|
||||
const emitFlags = emitNode && emitNode.flags;
|
||||
const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token];
|
||||
|
||||
/**
|
||||
* 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 ignoreTokenCallback A callback used to determine whether to skip source map
|
||||
* emit for the end position of this token.
|
||||
* @param getTokenTextRangeCallback 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, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
|
||||
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
|
||||
if (contextNode) {
|
||||
if (ignoreTokenCallback(contextNode, token)) {
|
||||
return tokenEndPos;
|
||||
}
|
||||
|
||||
const range = getTokenTextRangeCallback(contextNode, token);
|
||||
if (range) {
|
||||
tokenEndPos = range.end;
|
||||
}
|
||||
tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos);
|
||||
if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) {
|
||||
emitPos(tokenPos);
|
||||
}
|
||||
|
||||
emitPos(tokenEndPos);
|
||||
return tokenEndPos;
|
||||
}
|
||||
tokenPos = emitCallback(token, tokenPos);
|
||||
|
||||
if (range) tokenPos = range.end;
|
||||
if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) {
|
||||
emitPos(tokenPos);
|
||||
}
|
||||
|
||||
// @deprecated
|
||||
function changeEmitSourcePos() {
|
||||
Debug.assert(!modifyLastSourcePos);
|
||||
modifyLastSourcePos = true;
|
||||
return tokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -706,6 +384,10 @@ namespace ts {
|
||||
* @param sourceFile The source file.
|
||||
*/
|
||||
function setSourceFile(sourceFile: SourceFile) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSourceFile = sourceFile;
|
||||
currentSourceText = currentSourceFile.text;
|
||||
|
||||
@ -738,6 +420,10 @@ namespace ts {
|
||||
* Gets the text for the source map.
|
||||
*/
|
||||
function getText() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
encodeLastRecordedSourceMapSpan();
|
||||
|
||||
return stringify({
|
||||
@ -755,6 +441,10 @@ namespace ts {
|
||||
* Gets the SourceMappingURL for the source map.
|
||||
*/
|
||||
function getSourceMappingURL() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (compilerOptions.inlineSourceMap) {
|
||||
// Encode the sourceMap into the sourceMap url
|
||||
const base64SourceMapText = convertToBase64(getText());
|
||||
@ -766,61 +456,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createSourceMapWriterWithExtendedDiagnostics(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
|
||||
const {
|
||||
initialize,
|
||||
reset,
|
||||
getSourceMapData,
|
||||
setSourceFile,
|
||||
emitPos,
|
||||
emitStart,
|
||||
emitEnd,
|
||||
emitTokenStart,
|
||||
emitTokenEnd,
|
||||
changeEmitSourcePos,
|
||||
stopOverridingSpan,
|
||||
getText,
|
||||
getSourceMappingURL,
|
||||
} = createSourceMapWriterWorker(host, writer);
|
||||
return {
|
||||
initialize,
|
||||
reset,
|
||||
getSourceMapData,
|
||||
setSourceFile,
|
||||
emitPos(pos: number): void {
|
||||
performance.mark("sourcemapStart");
|
||||
emitPos(pos);
|
||||
performance.measure("sourceMapTime", "sourcemapStart");
|
||||
},
|
||||
emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void {
|
||||
performance.mark("emitSourcemap:emitStart");
|
||||
emitStart(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback);
|
||||
performance.measure("sourceMapTime", "emitSourcemap:emitStart");
|
||||
},
|
||||
emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void {
|
||||
performance.mark("emitSourcemap:emitEnd");
|
||||
emitEnd(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback);
|
||||
performance.measure("sourceMapTime", "emitSourcemap:emitEnd");
|
||||
},
|
||||
emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
|
||||
performance.mark("emitSourcemap:emitTokenStart");
|
||||
tokenStartPos = emitTokenStart(token, tokenStartPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback);
|
||||
performance.measure("sourceMapTime", "emitSourcemap:emitTokenStart");
|
||||
return tokenStartPos;
|
||||
},
|
||||
emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
|
||||
performance.mark("emitSourcemap:emitTokenEnd");
|
||||
tokenEndPos = emitTokenEnd(token, tokenEndPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback);
|
||||
performance.measure("sourceMapTime", "emitSourcemap:emitTokenEnd");
|
||||
return tokenEndPos;
|
||||
},
|
||||
changeEmitSourcePos,
|
||||
stopOverridingSpan,
|
||||
getText,
|
||||
getSourceMappingURL,
|
||||
};
|
||||
}
|
||||
|
||||
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
function base64FormatEncode(inValue: number) {
|
||||
|
||||
@ -31,33 +31,21 @@ namespace ts {
|
||||
getSourceFiles(): SourceFile[];
|
||||
|
||||
/**
|
||||
* Determines whether expression substitutions are enabled for the provided node.
|
||||
*/
|
||||
isSubstitutionEnabled(node: Node): boolean;
|
||||
|
||||
/**
|
||||
* Determines whether before/after emit notifications should be raised in the pretty
|
||||
* printer when it emits a node.
|
||||
*/
|
||||
isEmitNotificationEnabled(node: Node): boolean;
|
||||
|
||||
/**
|
||||
* Hook used by transformers to substitute expressions just before they
|
||||
* are emitted by the pretty printer.
|
||||
* Emits the substitute for a node, if one is available; otherwise, emits the node.
|
||||
*
|
||||
* @param node The node to substitute.
|
||||
* @param isExpression A value indicating whether the node is in an expression context.
|
||||
* @param emitCallback A callback used to emit the node or its substitute.
|
||||
*/
|
||||
onSubstituteNode(node: Node, isExpression: boolean): Node;
|
||||
emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void): void;
|
||||
|
||||
/**
|
||||
* Hook used to allow transformers to capture state before or after
|
||||
* the printer emits a node.
|
||||
* Emits a node with possible notification.
|
||||
*
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback A callback used to emit the node.
|
||||
*/
|
||||
onEmitNode(node: Node, emitCallback: (node: Node) => void): void;
|
||||
emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void): void;
|
||||
|
||||
/**
|
||||
* Reset transient transformation properties on parse tree nodes.
|
||||
@ -302,10 +290,10 @@ namespace ts {
|
||||
hoistFunctionDeclaration,
|
||||
startLexicalEnvironment,
|
||||
endLexicalEnvironment,
|
||||
onSubstituteNode,
|
||||
onSubstituteNode: (node, isExpression) => node,
|
||||
enableSubstitution,
|
||||
isSubstitutionEnabled,
|
||||
onEmitNode,
|
||||
onEmitNode: (node, emitCallback) => emitCallback(node),
|
||||
enableEmitNotification,
|
||||
isEmitNotificationEnabled
|
||||
};
|
||||
@ -321,10 +309,8 @@ namespace ts {
|
||||
|
||||
return {
|
||||
getSourceFiles: () => transformed,
|
||||
isSubstitutionEnabled,
|
||||
isEmitNotificationEnabled,
|
||||
onSubstituteNode: context.onSubstituteNode,
|
||||
onEmitNode: context.onEmitNode,
|
||||
emitNodeWithSubstitution,
|
||||
emitNodeWithNotification,
|
||||
dispose() {
|
||||
// During transformation we may need to annotate a parse tree node with transient
|
||||
// transformation properties. As parse tree nodes live longer than transformation
|
||||
@ -362,18 +348,29 @@ namespace ts {
|
||||
* Determines whether expression substitutions are enabled for the provided node.
|
||||
*/
|
||||
function isSubstitutionEnabled(node: Node) {
|
||||
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0;
|
||||
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0
|
||||
&& (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default hook for node substitutions.
|
||||
* Emits a node with possible substitution.
|
||||
*
|
||||
* @param node The node to substitute.
|
||||
* @param isExpression A value indicating whether the node is to be used in an expression
|
||||
* position.
|
||||
* @param node The node to emit.
|
||||
* @param isExpression Whether the node represents an expression.
|
||||
* @param emitCallback The callback used to emit the node or its substitute.
|
||||
*/
|
||||
function onSubstituteNode(node: Node, isExpression: boolean) {
|
||||
return node;
|
||||
function emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void) {
|
||||
if (node) {
|
||||
if (isSubstitutionEnabled(node)) {
|
||||
const substitute = context.onSubstituteNode(node, isExpression);
|
||||
if (substitute && substitute !== node) {
|
||||
emitCallback(substitute);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emitCallback(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -393,13 +390,17 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Default hook for node emit.
|
||||
*
|
||||
* @param node The node to emit.
|
||||
* @param emit A callback used to emit the node in the printer.
|
||||
* Emits a node with possible emit notification.
|
||||
*/
|
||||
function onEmitNode(node: Node, emit: (node: Node) => void) {
|
||||
emit(node);
|
||||
function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (node) {
|
||||
if (isEmitNotificationEnabled(node)) {
|
||||
context.onEmitNode(node, emitCallback);
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user