mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Refactored emit pipeline.
This commit is contained in:
+15
-20
@@ -5,7 +5,7 @@ namespace ts {
|
||||
export interface CommentWriter {
|
||||
reset(): void;
|
||||
setSourceFile(sourceFile: SourceFile): void;
|
||||
emitNodeWithComments(node: Node, emitCallback: (node: Node) => void): void;
|
||||
emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
|
||||
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
|
||||
emitTrailingCommentsOfPosition(pos: number): void;
|
||||
}
|
||||
@@ -34,9 +34,9 @@ namespace ts {
|
||||
emitTrailingCommentsOfPosition,
|
||||
};
|
||||
|
||||
function emitNodeWithComments(node: Node, emitCallback: (node: Node) => void) {
|
||||
function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
|
||||
if (disabled) {
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,10 +46,12 @@ namespace ts {
|
||||
if ((pos < 0 && end < 0) || (pos === end)) {
|
||||
// Both pos and end are synthesized, so just emit the node without comments.
|
||||
if (emitFlags & EmitFlags.NoNestedComments) {
|
||||
disableCommentsAndEmit(node, emitCallback);
|
||||
disabled = true;
|
||||
emitCallback(emitContext, node);
|
||||
disabled = false;
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -91,10 +93,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (emitFlags & EmitFlags.NoNestedComments) {
|
||||
disableCommentsAndEmit(node, emitCallback);
|
||||
disabled = true;
|
||||
emitCallback(emitContext, node);
|
||||
disabled = false;
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
@@ -137,8 +141,10 @@ namespace ts {
|
||||
performance.measure("commentTime", "preEmitBodyWithDetachedComments");
|
||||
}
|
||||
|
||||
if (emitFlags & EmitFlags.NoNestedComments) {
|
||||
disableCommentsAndEmit(node, emitCallback);
|
||||
if (emitFlags & EmitFlags.NoNestedComments && !disabled) {
|
||||
disabled = true;
|
||||
emitCallback(node);
|
||||
disabled = false;
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
@@ -284,17 +290,6 @@ namespace ts {
|
||||
detachedCommentsInfo = undefined;
|
||||
}
|
||||
|
||||
function disableCommentsAndEmit(node: Node, emitCallback: (node: Node) => void): void {
|
||||
if (disabled) {
|
||||
emitCallback(node);
|
||||
}
|
||||
else {
|
||||
disabled = true;
|
||||
emitCallback(node);
|
||||
disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function hasDetachedComments(pos: number) {
|
||||
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
|
||||
}
|
||||
|
||||
+115
-104
@@ -229,33 +229,27 @@ const _super = (function (geti, seti) {
|
||||
let isOwnFileEmit: boolean;
|
||||
let emitSkipped = false;
|
||||
|
||||
performance.mark("beforeTransform");
|
||||
const sourceFiles = getSourceFilesToEmit(host, targetSourceFile);
|
||||
|
||||
// Transform the source files
|
||||
const transformed = transformFiles(
|
||||
resolver,
|
||||
host,
|
||||
getSourceFilesToEmit(host, targetSourceFile),
|
||||
transformers);
|
||||
|
||||
performance.measure("transformTime", "beforeTransform");
|
||||
|
||||
// Extract helpers from the result
|
||||
performance.mark("beforeTransform");
|
||||
const {
|
||||
transformed,
|
||||
emitNodeWithSubstitution,
|
||||
emitNodeWithNotification
|
||||
} = transformed;
|
||||
|
||||
performance.mark("beforePrint");
|
||||
} = transformFiles(resolver, host, sourceFiles, transformers);
|
||||
performance.measure("transformTime", "beforeTransform");
|
||||
|
||||
// Emit each output file
|
||||
forEachTransformedEmitFile(host, transformed.getSourceFiles(), emitFile);
|
||||
|
||||
// Clean up after transformation
|
||||
transformed.dispose();
|
||||
|
||||
performance.mark("beforePrint");
|
||||
forEachTransformedEmitFile(host, transformed, emitFile);
|
||||
performance.measure("printTime", "beforePrint");
|
||||
|
||||
// Clean up emit nodes on parse tree
|
||||
for (const sourceFile of sourceFiles) {
|
||||
disposeEmitNodes(sourceFile);
|
||||
}
|
||||
|
||||
return {
|
||||
emitSkipped,
|
||||
diagnostics: emitterDiagnostics.getDiagnostics(),
|
||||
@@ -346,111 +340,137 @@ const _super = (function (geti, seti) {
|
||||
currentFileIdentifiers = node.identifiers;
|
||||
sourceMap.setSourceFile(node);
|
||||
comments.setSourceFile(node);
|
||||
emitNodeWithNotification(node, emitWithSubstitution);
|
||||
pipelineEmitWithNotification(EmitContext.SourceFile, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node.
|
||||
*/
|
||||
function emit(node: Node) {
|
||||
emitNodeWithNotification(node, emitWithComments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with comments.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emit.
|
||||
*/
|
||||
function emitWithComments(node: Node) {
|
||||
emitNodeWithComments(node, emitWithSourceMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with source maps.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emitWithComments.
|
||||
*/
|
||||
function emitWithSourceMap(node: Node) {
|
||||
emitNodeWithSourceMap(node, emitWithSubstitution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with possible substitution.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emitWithSourceMap or
|
||||
* emitIdentifierNameWithComments.
|
||||
*/
|
||||
function emitWithSubstitution(node: Node) {
|
||||
emitNodeWithSubstitution(node, /*isExpression*/ false, emitWorker);
|
||||
pipelineEmitWithNotification(EmitContext.Unspecified, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an IdentifierName.
|
||||
*/
|
||||
function emitIdentifierName(node: Identifier) {
|
||||
if (node) {
|
||||
emitNodeWithNotification(node, emitIdentifierNameWithComments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an IdentifierName with possible comments.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emitIdentifierName.
|
||||
*/
|
||||
function emitIdentifierNameWithComments(node: Identifier) {
|
||||
emitNodeWithComments(node, emitWithSubstitution);
|
||||
pipelineEmitWithNotification(EmitContext.IdentifierName, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression node.
|
||||
*/
|
||||
function emitExpression(node: Expression) {
|
||||
emitNodeWithNotification(node, emitExpressionWithComments);
|
||||
pipelineEmitWithNotification(EmitContext.Expression, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression with comments.
|
||||
* Emits a node with possible notification.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
|
||||
* and should only be called indirectly from emitExpression.
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called from printSourceFile, emit, emitExpression, or
|
||||
* emitIdentifierName.
|
||||
*/
|
||||
function emitExpressionWithComments(node: Expression) {
|
||||
emitNodeWithComments(node, emitExpressionWithSourceMap);
|
||||
function pipelineEmitWithNotification(emitContext: EmitContext, node: Node) {
|
||||
emitNodeWithNotification(emitContext, node, pipelineEmitWithComments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression with possible source maps.
|
||||
* Emits a node with comments.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
|
||||
* and should only be called indirectly from emitExpressionWithComments.
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitWithNotification.
|
||||
*/
|
||||
function emitExpressionWithSourceMap(node: Expression) {
|
||||
emitNodeWithSourceMap(node, emitExpressionWithSubstitution);
|
||||
function pipelineEmitWithComments(emitContext: EmitContext, node: Node) {
|
||||
// Do not emit comments for SourceFile
|
||||
if (emitContext === EmitContext.SourceFile) {
|
||||
pipelineEmitWithSourceMap(emitContext, node);
|
||||
return;
|
||||
}
|
||||
|
||||
emitNodeWithComments(emitContext, node, pipelineEmitWithSourceMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression with possible substitution.
|
||||
* Emits a node with source maps.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
|
||||
* and should only be called indirectly from emitExpressionWithSourceMap or
|
||||
* from emitWorker.
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitWithComments.
|
||||
*/
|
||||
function emitExpressionWithSubstitution(node: Expression) {
|
||||
emitNodeWithSubstitution(node, /*isExpression*/ true, emitExpressionWorker);
|
||||
function pipelineEmitWithSourceMap(emitContext: EmitContext, node: Node) {
|
||||
// Do not emit source mappings for SourceFile or IdentifierName
|
||||
if (emitContext === EmitContext.SourceFile
|
||||
|| emitContext === EmitContext.IdentifierName) {
|
||||
pipelineEmitWithSubstitution(emitContext, node);
|
||||
return;
|
||||
}
|
||||
|
||||
emitNodeWithSourceMap(emitContext, node, pipelineEmitWithSubstitution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with possible substitution.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitWithSourceMap or
|
||||
* pipelineEmitInUnspecifiedContext (when pickign a more specific context).
|
||||
*/
|
||||
function pipelineEmitWithSubstitution(emitContext: EmitContext, node: Node) {
|
||||
emitNodeWithSubstitution(emitContext, node, pipelineEmitForContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emitNodeWithSubstitution.
|
||||
* and should only be called indirectly from pipelineEmitWithSubstitution.
|
||||
*/
|
||||
function emitWorker(node: Node): void {
|
||||
function pipelineEmitForContext(emitContext: EmitContext, node: Node): void {
|
||||
switch (emitContext) {
|
||||
case EmitContext.SourceFile: return pipelineEmitInSourceFileContext(node);
|
||||
case EmitContext.IdentifierName: return pipelineEmitInIdentifierNameContext(node);
|
||||
case EmitContext.Unspecified: return pipelineEmitInUnspecifiedContext(node);
|
||||
case EmitContext.Expression: return pipelineEmitInExpressionContext(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node in the SourceFile EmitContext.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitForContext.
|
||||
*/
|
||||
function pipelineEmitInSourceFileContext(node: Node): void {
|
||||
const kind = node.kind;
|
||||
switch (kind) {
|
||||
// Top-level nodes
|
||||
case SyntaxKind.SourceFile:
|
||||
return emitSourceFile(<SourceFile>node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node in the IdentifierName EmitContext.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitForContext.
|
||||
*/
|
||||
function pipelineEmitInIdentifierNameContext(node: Node): void {
|
||||
const kind = node.kind;
|
||||
switch (kind) {
|
||||
// Identifiers
|
||||
case SyntaxKind.Identifier:
|
||||
return emitIdentifier(<Identifier>node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node in the Unspecified EmitContext.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitForContext.
|
||||
*/
|
||||
function pipelineEmitInUnspecifiedContext(node: Node): void {
|
||||
const kind = node.kind;
|
||||
switch (kind) {
|
||||
// Pseudo-literals
|
||||
@@ -486,7 +506,8 @@ const _super = (function (geti, seti) {
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.GlobalKeyword:
|
||||
return emitTokenNode(node);
|
||||
writeTokenText(kind);
|
||||
return;
|
||||
|
||||
// Parse tree nodes
|
||||
|
||||
@@ -691,27 +712,24 @@ const _super = (function (geti, seti) {
|
||||
case SyntaxKind.EnumMember:
|
||||
return emitEnumMember(<EnumMember>node);
|
||||
|
||||
// Top-level nodes
|
||||
case SyntaxKind.SourceFile:
|
||||
return emitSourceFile(<SourceFile>node);
|
||||
|
||||
// JSDoc nodes (ignored)
|
||||
|
||||
// Transformation nodes (ignored)
|
||||
}
|
||||
|
||||
// If the node is an expression, try to emit it as an expression with
|
||||
// substitution.
|
||||
if (isExpression(node)) {
|
||||
return emitExpressionWithSubstitution(node);
|
||||
return pipelineEmitWithSubstitution(EmitContext.Expression, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression.
|
||||
* Emits a node in the Expression EmitContext.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
|
||||
* and should only be called indirectly from emitExpressionWithNotification.
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from pipelineEmitForContext.
|
||||
*/
|
||||
function emitExpressionWorker(node: Node) {
|
||||
function pipelineEmitInExpressionContext(node: Node): void {
|
||||
const kind = node.kind;
|
||||
switch (kind) {
|
||||
// Literals
|
||||
@@ -733,7 +751,8 @@ const _super = (function (geti, seti) {
|
||||
case SyntaxKind.SuperKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
return emitTokenNode(node);
|
||||
writeTokenText(kind);
|
||||
return;
|
||||
|
||||
// Expressions
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
@@ -2444,15 +2463,7 @@ const _super = (function (geti, seti) {
|
||||
function writeTokenText(token: SyntaxKind, pos?: number) {
|
||||
const tokenString = tokenToString(token);
|
||||
write(tokenString);
|
||||
return positionIsSynthesized(pos) ? -1 : pos + tokenString.length;
|
||||
}
|
||||
|
||||
function emitTokenNode(node: Node) {
|
||||
emitNodeWithSourceMap(node, emitTokenNodeWorker);
|
||||
}
|
||||
|
||||
function emitTokenNodeWorker(node: Node) {
|
||||
writeTokenText(node.kind);
|
||||
return pos < 0 ? pos : pos + tokenString.length;
|
||||
}
|
||||
|
||||
function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) {
|
||||
|
||||
+134
-1
@@ -2665,6 +2665,139 @@ namespace ts {
|
||||
return destRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any EmitNode entries from parse-tree nodes.
|
||||
* @param sourceFile A source file.
|
||||
*/
|
||||
export function disposeEmitNodes(sourceFile: SourceFile) {
|
||||
// During transformation we may need to annotate a parse tree node with transient
|
||||
// transformation properties. As parse tree nodes live longer than transformation
|
||||
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
|
||||
// from these nodes to ensure we do not hold onto entire subtrees just for position
|
||||
// information. We also need to reset these nodes to a pre-transformation state
|
||||
// for incremental parsing scenarios so that we do not impact later emit.
|
||||
sourceFile = getSourceFileOfNode(getParseTreeNode(sourceFile));
|
||||
const emitNode = sourceFile && sourceFile.emitNode;
|
||||
const annotatedNodes = emitNode && emitNode.annotatedNodes;
|
||||
if (annotatedNodes) {
|
||||
for (const node of annotatedNodes) {
|
||||
node.emitNode = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a node with the current transformation, initializing
|
||||
* various transient transformation properties.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
function getOrCreateEmitNode(node: Node) {
|
||||
if (!node.emitNode) {
|
||||
if (isParseTreeNode(node)) {
|
||||
// To avoid holding onto transformation artifacts, we keep track of any
|
||||
// parse tree node we are annotating. This allows us to clean them up after
|
||||
// all transformations have completed.
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
return node.emitNode = { annotatedNodes: [node] };
|
||||
}
|
||||
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
getOrCreateEmitNode(sourceFile).annotatedNodes.push(node);
|
||||
}
|
||||
|
||||
node.emitNode = {};
|
||||
}
|
||||
|
||||
return node.emitNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets flags that control emit behavior of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
export function getEmitFlags(node: Node) {
|
||||
const emitNode = node.emitNode;
|
||||
return emitNode && emitNode.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags that control emit behavior of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param emitFlags The NodeEmitFlags for the node.
|
||||
*/
|
||||
export function setEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
|
||||
getOrCreateEmitNode(node).flags = emitFlags;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom text range to use when emitting source maps.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param range The text range.
|
||||
*/
|
||||
export function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
|
||||
getOrCreateEmitNode(node).sourceMapRange = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the TextRange to use for source maps for a token of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param token The token.
|
||||
* @param range The text range.
|
||||
*/
|
||||
export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
|
||||
const emitNode = getOrCreateEmitNode(node);
|
||||
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap<TextRange>());
|
||||
tokenSourceMapRanges[token] = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom text range to use when emitting comments.
|
||||
*/
|
||||
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
|
||||
getOrCreateEmitNode(node).commentRange = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a custom text range to use when emitting comments.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
export function getCommentRange(node: Node) {
|
||||
const emitNode = node.emitNode;
|
||||
return (emitNode && emitNode.commentRange) || node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a custom text range to use when emitting source maps.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
export function getSourceMapRange(node: Node) {
|
||||
const emitNode = node.emitNode;
|
||||
return (emitNode && emitNode.sourceMapRange) || node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TextRange to use for source maps for a token of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param token The token.
|
||||
*/
|
||||
export function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
|
||||
const emitNode = node.emitNode;
|
||||
const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges;
|
||||
return tokenSourceMapRanges && tokenSourceMapRanges[token];
|
||||
}
|
||||
|
||||
export function setTextRange<T extends TextRange>(node: T, location: TextRange): T {
|
||||
if (location) {
|
||||
node.pos = location.pos;
|
||||
@@ -2706,7 +2839,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get the name of a target module from an import/export declaration as should be written in the emitted output.
|
||||
* The emitted output name can be different from the input if:
|
||||
* 1. The module has a /// <amd-module name="<new name>" />
|
||||
|
||||
@@ -38,10 +38,11 @@ namespace ts {
|
||||
/**
|
||||
* Emits a node with possible leading and trailing source maps.
|
||||
*
|
||||
* @param emitContext The current emit context
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback The callback used to emit the node.
|
||||
*/
|
||||
emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void): void;
|
||||
emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
|
||||
|
||||
/**
|
||||
* Emits a token of a node node with possible leading and trailing source maps.
|
||||
@@ -313,9 +314,9 @@ namespace ts {
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback The callback used to emit the node.
|
||||
*/
|
||||
function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) {
|
||||
function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
|
||||
if (disabled) {
|
||||
return emitCallback(node);
|
||||
return emitCallback(emitContext, node);
|
||||
}
|
||||
|
||||
if (node) {
|
||||
@@ -331,11 +332,11 @@ namespace ts {
|
||||
|
||||
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
|
||||
disabled = true;
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
disabled = false;
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
}
|
||||
|
||||
if (node.kind !== SyntaxKind.NotEmittedStatement
|
||||
|
||||
+23
-167
@@ -28,29 +28,25 @@ namespace ts {
|
||||
/**
|
||||
* Gets the transformed source files.
|
||||
*/
|
||||
getSourceFiles(): SourceFile[];
|
||||
transformed: SourceFile[];
|
||||
|
||||
/**
|
||||
* Emits the substitute for a node, if one is available; otherwise, emits the node.
|
||||
*
|
||||
* @param emitContext The current emit context.
|
||||
* @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.
|
||||
*/
|
||||
emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void): void;
|
||||
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
|
||||
|
||||
/**
|
||||
* Emits a node with possible notification.
|
||||
*
|
||||
* @param emitContext The current emit context.
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback A callback used to emit the node.
|
||||
*/
|
||||
emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void): void;
|
||||
|
||||
/**
|
||||
* Reset transient transformation properties on parse tree nodes.
|
||||
*/
|
||||
dispose(): void;
|
||||
emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
|
||||
}
|
||||
|
||||
export interface TransformationContext extends LexicalEnvironment {
|
||||
@@ -82,7 +78,7 @@ namespace ts {
|
||||
* Hook used by transformers to substitute expressions just before they
|
||||
* are emitted by the pretty printer.
|
||||
*/
|
||||
onSubstituteNode?: (node: Node, isExpression: boolean) => Node;
|
||||
onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node;
|
||||
|
||||
/**
|
||||
* Enables before/after emit notifications in the pretty printer for the provided
|
||||
@@ -100,7 +96,7 @@ namespace ts {
|
||||
* Hook used to allow transformers to capture state before or after
|
||||
* the printer emits a node.
|
||||
*/
|
||||
onEmitNode?: (node: Node, emit: (node: Node) => void) => void;
|
||||
onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -129,139 +125,6 @@ namespace ts {
|
||||
return transformers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any EmitNode entries from parse-tree nodes.
|
||||
* @param sourceFile A source file.
|
||||
*/
|
||||
export function disposeEmitNodes(sourceFile: SourceFile) {
|
||||
// During transformation we may need to annotate a parse tree node with transient
|
||||
// transformation properties. As parse tree nodes live longer than transformation
|
||||
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
|
||||
// from these nodes to ensure we do not hold onto entire subtrees just for position
|
||||
// information. We also need to reset these nodes to a pre-transformation state
|
||||
// for incremental parsing scenarios so that we do not impact later emit.
|
||||
sourceFile = getSourceFileOfNode(getParseTreeNode(sourceFile));
|
||||
const emitNode = sourceFile && sourceFile.emitNode;
|
||||
const annotatedNodes = emitNode && emitNode.annotatedNodes;
|
||||
if (annotatedNodes) {
|
||||
for (const node of annotatedNodes) {
|
||||
node.emitNode = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a node with the current transformation, initializing
|
||||
* various transient transformation properties.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
function getOrCreateEmitNode(node: Node) {
|
||||
if (!node.emitNode) {
|
||||
if (isParseTreeNode(node)) {
|
||||
// To avoid holding onto transformation artifacts, we keep track of any
|
||||
// parse tree node we are annotating. This allows us to clean them up after
|
||||
// all transformations have completed.
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
return node.emitNode = { annotatedNodes: [node] };
|
||||
}
|
||||
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
getOrCreateEmitNode(sourceFile).annotatedNodes.push(node);
|
||||
}
|
||||
|
||||
node.emitNode = {};
|
||||
}
|
||||
|
||||
return node.emitNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets flags that control emit behavior of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
export function getEmitFlags(node: Node) {
|
||||
const emitNode = node.emitNode;
|
||||
return emitNode && emitNode.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags that control emit behavior of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param emitFlags The NodeEmitFlags for the node.
|
||||
*/
|
||||
export function setEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
|
||||
getOrCreateEmitNode(node).flags = emitFlags;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom text range to use when emitting source maps.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param range The text range.
|
||||
*/
|
||||
export function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
|
||||
getOrCreateEmitNode(node).sourceMapRange = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the TextRange to use for source maps for a token of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param token The token.
|
||||
* @param range The text range.
|
||||
*/
|
||||
export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
|
||||
const emitNode = getOrCreateEmitNode(node);
|
||||
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap<TextRange>());
|
||||
tokenSourceMapRanges[token] = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom text range to use when emitting comments.
|
||||
*/
|
||||
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
|
||||
getOrCreateEmitNode(node).commentRange = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a custom text range to use when emitting comments.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
export function getCommentRange(node: Node) {
|
||||
const emitNode = node.emitNode;
|
||||
return (emitNode && emitNode.commentRange) || node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a custom text range to use when emitting source maps.
|
||||
*
|
||||
* @param node The node.
|
||||
*/
|
||||
export function getSourceMapRange(node: Node) {
|
||||
const emitNode = node.emitNode;
|
||||
return (emitNode && emitNode.sourceMapRange) || node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TextRange to use for source maps for a token of a node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param token The token.
|
||||
*/
|
||||
export function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
|
||||
const emitNode = node.emitNode;
|
||||
const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges;
|
||||
return tokenSourceMapRanges && tokenSourceMapRanges[token];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of SourceFiles by passing them through each transformer.
|
||||
*
|
||||
@@ -290,10 +153,10 @@ namespace ts {
|
||||
hoistFunctionDeclaration,
|
||||
startLexicalEnvironment,
|
||||
endLexicalEnvironment,
|
||||
onSubstituteNode: (node, isExpression) => node,
|
||||
onSubstituteNode: (emitContext, node) => node,
|
||||
enableSubstitution,
|
||||
isSubstitutionEnabled,
|
||||
onEmitNode: (node, emitCallback) => emitCallback(node),
|
||||
onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext),
|
||||
enableEmitNotification,
|
||||
isEmitNotificationEnabled
|
||||
};
|
||||
@@ -308,20 +171,9 @@ namespace ts {
|
||||
lexicalEnvironmentDisabled = true;
|
||||
|
||||
return {
|
||||
getSourceFiles: () => transformed,
|
||||
transformed,
|
||||
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
|
||||
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
|
||||
// from these nodes to ensure we do not hold onto entire subtrees just for position
|
||||
// information. We also need to reset these nodes to a pre-transformation state
|
||||
// for incremental parsing scenarios so that we do not impact later emit.
|
||||
for (const sourceFile of sourceFiles) {
|
||||
disposeEmitNodes(sourceFile);
|
||||
}
|
||||
}
|
||||
emitNodeWithNotification
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -355,21 +207,21 @@ namespace ts {
|
||||
/**
|
||||
* Emits a node with possible substitution.
|
||||
*
|
||||
* @param emitContext The current emit context.
|
||||
* @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 emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void) {
|
||||
function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
|
||||
if (node) {
|
||||
if (isSubstitutionEnabled(node)) {
|
||||
const substitute = context.onSubstituteNode(node, isExpression);
|
||||
const substitute = context.onSubstituteNode(emitContext, node);
|
||||
if (substitute && substitute !== node) {
|
||||
emitCallback(substitute);
|
||||
emitCallback(emitContext, substitute);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,14 +243,18 @@ namespace ts {
|
||||
|
||||
/**
|
||||
* Emits a node with possible emit notification.
|
||||
*
|
||||
* @param emitContext The current emit context.
|
||||
* @param node The node to emit.
|
||||
* @param emitCallback The callback used to emit the node.
|
||||
*/
|
||||
function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) {
|
||||
function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
|
||||
if (node) {
|
||||
if (isEmitNotificationEnabled(node)) {
|
||||
context.onEmitNode(node, emitCallback);
|
||||
context.onEmitNode(emitContext, node, emitCallback);
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
emitCallback(emitContext, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2852,7 +2852,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to be printed.
|
||||
*/
|
||||
function onEmitNode(node: Node, emit: (node: Node) => void) {
|
||||
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
|
||||
const savedUseCapturedThis = useCapturedThis;
|
||||
|
||||
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
|
||||
@@ -2861,7 +2861,7 @@ namespace ts {
|
||||
useCapturedThis = (getEmitFlags(node) & EmitFlags.CapturesThis) !== 0;
|
||||
}
|
||||
|
||||
previousOnEmitNode(node, emit);
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
|
||||
useCapturedThis = savedUseCapturedThis;
|
||||
}
|
||||
@@ -2902,10 +2902,10 @@ namespace ts {
|
||||
* @param isExpression A value indicating whether the node is to be used in an expression
|
||||
* position.
|
||||
*/
|
||||
function onSubstituteNode(node: Node, isExpression: boolean) {
|
||||
node = previousOnSubstituteNode(node, isExpression);
|
||||
function onSubstituteNode(emitContext: EmitContext, node: Node) {
|
||||
node = previousOnSubstituteNode(emitContext, node);
|
||||
|
||||
if (isExpression) {
|
||||
if (emitContext === EmitContext.Expression) {
|
||||
return substituteExpression(node);
|
||||
}
|
||||
|
||||
|
||||
@@ -1883,9 +1883,9 @@ namespace ts {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function onSubstituteNode(node: Node, isExpression: boolean): Node {
|
||||
node = previousOnSubstituteNode(node, isExpression);
|
||||
if (isExpression) {
|
||||
function onSubstituteNode(emitContext: EmitContext, node: Node): Node {
|
||||
node = previousOnSubstituteNode(emitContext, node);
|
||||
if (emitContext === EmitContext.Expression) {
|
||||
return substituteExpression(<Expression>node);
|
||||
}
|
||||
return node;
|
||||
|
||||
@@ -822,14 +822,14 @@ namespace ts {
|
||||
return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node);
|
||||
}
|
||||
|
||||
function onEmitNode(node: Node, emit: (node: Node) => void): void {
|
||||
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
bindingNameExportSpecifiersMap = bindingNameExportSpecifiersForFileMap[getOriginalNodeId(node)];
|
||||
previousOnEmitNode(node, emit);
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
bindingNameExportSpecifiersMap = undefined;
|
||||
}
|
||||
else {
|
||||
previousOnEmitNode(node, emit);
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,9 +840,9 @@ namespace ts {
|
||||
* @param isExpression A value indicating whether the node is to be used in an expression
|
||||
* position.
|
||||
*/
|
||||
function onSubstituteNode(node: Node, isExpression: boolean) {
|
||||
node = previousOnSubstituteNode(node, isExpression);
|
||||
if (isExpression) {
|
||||
function onSubstituteNode(emitContext: EmitContext, node: Node) {
|
||||
node = previousOnSubstituteNode(emitContext, node);
|
||||
if (emitContext === EmitContext.Expression) {
|
||||
return substituteExpression(<Expression>node);
|
||||
}
|
||||
else if (isShorthandPropertyAssignment(node)) {
|
||||
|
||||
@@ -986,14 +986,14 @@ namespace ts {
|
||||
// Substitutions
|
||||
//
|
||||
|
||||
function onEmitNode(node: Node, emit: (node: Node) => void): void {
|
||||
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
exportFunctionForFile = exportFunctionForFileMap[getOriginalNodeId(node)];
|
||||
previousOnEmitNode(node, emit);
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
exportFunctionForFile = undefined;
|
||||
}
|
||||
else {
|
||||
previousOnEmitNode(node, emit);
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1004,9 +1004,9 @@ namespace ts {
|
||||
* @param isExpression A value indicating whether the node is to be used in an expression
|
||||
* position.
|
||||
*/
|
||||
function onSubstituteNode(node: Node, isExpression: boolean) {
|
||||
node = previousOnSubstituteNode(node, isExpression);
|
||||
if (isExpression) {
|
||||
function onSubstituteNode(emitContext: EmitContext, node: Node) {
|
||||
node = previousOnSubstituteNode(emitContext, node);
|
||||
if (emitContext === EmitContext.Expression) {
|
||||
return substituteExpression(<Expression>node);
|
||||
}
|
||||
|
||||
|
||||
@@ -3231,7 +3231,7 @@ namespace ts {
|
||||
* @param node The node to emit.
|
||||
* @param emit A callback used to emit the node in the printer.
|
||||
*/
|
||||
function onEmitNode(node: Node, emit: (node: Node) => void): void {
|
||||
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
|
||||
const savedApplicableSubstitutions = applicableSubstitutions;
|
||||
const savedCurrentSuperContainer = currentSuperContainer;
|
||||
// If we need to support substitutions for `super` in an async method,
|
||||
@@ -3248,7 +3248,7 @@ namespace ts {
|
||||
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
|
||||
}
|
||||
|
||||
previousOnEmitNode(node, emit);
|
||||
previousOnEmitNode(emitContext, node, emitCallback);
|
||||
|
||||
applicableSubstitutions = savedApplicableSubstitutions;
|
||||
currentSuperContainer = savedCurrentSuperContainer;
|
||||
@@ -3261,9 +3261,9 @@ namespace ts {
|
||||
* @param isExpression A value indicating whether the node is to be used in an expression
|
||||
* position.
|
||||
*/
|
||||
function onSubstituteNode(node: Node, isExpression: boolean) {
|
||||
node = previousOnSubstituteNode(node, isExpression);
|
||||
if (isExpression) {
|
||||
function onSubstituteNode(emitContext: EmitContext, node: Node) {
|
||||
node = previousOnSubstituteNode(emitContext, node);
|
||||
if (emitContext === EmitContext.Expression) {
|
||||
return substituteExpression(<Expression>node);
|
||||
}
|
||||
else if (isShorthandPropertyAssignment(node)) {
|
||||
|
||||
@@ -3189,6 +3189,14 @@ namespace ts {
|
||||
CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum EmitContext {
|
||||
SourceFile, // Emitting a SourceFile
|
||||
Expression, // Emitting an Expression
|
||||
IdentifierName, // Emitting an IdentifierName
|
||||
Unspecified, // Emitting an otherwise unspecified node
|
||||
}
|
||||
|
||||
/** Additional context provided to `visitEachChild` */
|
||||
/* @internal */
|
||||
export interface LexicalEnvironment {
|
||||
|
||||
Reference in New Issue
Block a user