Added more control over sourcemap/comment emit and fixed a number of source map emit issues.

This commit is contained in:
Ron Buckton 2016-04-22 16:28:26 -07:00
parent 3ef9ab475d
commit cee4954090
10 changed files with 298 additions and 150 deletions

View File

@ -77,10 +77,15 @@ namespace ts {
emitTrailingDetachedComments
};
function getLeadingComments(range: Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange): CommentRange[];
function getLeadingComments(range: TextRange): CommentRange[];
function getLeadingComments(range: TextRange | Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange) {
if (shouldSkipCommentsForNodeCallback && shouldSkipCommentsForNodeCallback(<Node>range)) {
function getLeadingComments(node: Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange): CommentRange[];
function getLeadingComments(nodeOrRange: TextRange | Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange) {
let range = nodeOrRange;
if (getCustomCommentRangeForNodeCallback) {
range = getCustomCommentRangeForNodeCallback(<Node>nodeOrRange) || range;
}
if (shouldSkipCommentsForNodeCallback && shouldSkipCommentsForNodeCallback(<Node>nodeOrRange)) {
// If the node will not be emitted in JS, remove all the comments (normal,
// pinned and `///`) associated with the node, unless it is a triple slash
// comment at the top of the file.
@ -100,10 +105,6 @@ namespace ts {
return undefined;
}
if (getCustomCommentRangeForNodeCallback) {
range = getCustomCommentRangeForNodeCallback(<Node>range) || range;
}
return getLeadingCommentsOfPosition(range.pos);
}
@ -123,15 +124,16 @@ namespace ts {
return false;
}
function getTrailingComments(range: Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange): CommentRange[];
function getTrailingComments(range: TextRange): CommentRange[];
function getTrailingComments(range: TextRange | Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange) {
if (shouldSkipCommentsForNodeCallback && shouldSkipCommentsForNodeCallback(<Node>range)) {
function getTrailingComments(node: Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange): CommentRange[];
function getTrailingComments(nodeOrRange: TextRange | Node, shouldSkipCommentsForNodeCallback?: (node: Node) => boolean, getCustomCommentRangeForNodeCallback?: (node: Node) => TextRange) {
let range = <TextRange>nodeOrRange;
if (shouldSkipCommentsForNodeCallback && shouldSkipCommentsForNodeCallback(<Node>nodeOrRange)) {
return undefined;
}
if (getCustomCommentRangeForNodeCallback) {
range = getCustomCommentRangeForNodeCallback(<Node>range) || range;
range = getCustomCommentRangeForNodeCallback(<Node>nodeOrRange) || range;
}
return getTrailingCommentsOfPosition(range.end);

View File

@ -8,7 +8,7 @@ namespace ts {
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags): Node {
function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags, emitOptions?: NodeEmitOptions): Node {
const ConstructorForKind = kind === SyntaxKind.SourceFile
? (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))
: (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()));
@ -21,6 +21,10 @@ namespace ts {
node.flags = flags;
}
if (emitOptions) {
node.emitOptions = emitOptions;
}
return node;
}
@ -64,11 +68,11 @@ namespace ts {
/**
* Creates a shallow, memberwise clone of a node with no source map location.
*/
export function getSynthesizedClone<T extends Node>(node: T): T {
export function getSynthesizedClone<T extends Node>(node: T, emitOptions?: NodeEmitOptions): T {
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
// the original node. We also need to exclude specific properties and only include own-
// properties (to skip members already defined on the shared prototype).
const clone = <T>createNode(node.kind, /*location*/ undefined);
const clone = <T>createNode(node.kind, /*location*/ undefined, /*flags*/ undefined, emitOptions);
clone.flags = node.flags;
clone.original = node;
@ -86,29 +90,19 @@ namespace ts {
/**
* Creates a shallow, memberwise clone of a node for mutation.
*/
export function getMutableClone<T extends Node>(node: T): T {
const clone = getSynthesizedClone(node);
export function getMutableClone<T extends Node>(node: T, emitOptions?: NodeEmitOptions): T {
const clone = getSynthesizedClone(node, emitOptions);
clone.pos = node.pos;
clone.end = node.end;
clone.parent = node.parent;
return clone;
}
/**
* Creates a shallow, memberwise clone of a node at the specified source map location.
*/
export function getRelocatedClone<T extends Node>(node: T, location: TextRange): T {
const clone = getSynthesizedClone(node);
clone.pos = location.pos;
clone.end = location.end;
return clone;
}
/**
* Gets a clone of a node with a unique node ID.
*/
export function getUniqueClone<T extends Node>(node: T): T {
const clone = getMutableClone(node);
export function getUniqueClone<T extends Node>(node: T, emitOptions?: NodeEmitOptions): T {
const clone = getMutableClone(node, emitOptions);
clone.id = undefined;
getNodeId(clone);
return clone;
@ -116,26 +110,26 @@ namespace ts {
// Literals
export function createLiteral(textSource: StringLiteral | Identifier, location?: TextRange): StringLiteral;
export function createLiteral(value: string, location?: TextRange): StringLiteral;
export function createLiteral(value: number, location?: TextRange): LiteralExpression;
export function createLiteral(value: string | number | boolean, location?: TextRange): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | Identifier, location?: TextRange): PrimaryExpression {
export function createLiteral(textSource: StringLiteral | Identifier, location?: TextRange, emitOptions?: NodeEmitOptions): StringLiteral;
export function createLiteral(value: string, location?: TextRange, emitOptions?: NodeEmitOptions): StringLiteral;
export function createLiteral(value: number, location?: TextRange, emitOptions?: NodeEmitOptions): LiteralExpression;
export function createLiteral(value: string | number | boolean, location?: TextRange, emitOptions?: NodeEmitOptions): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | Identifier, location?: TextRange, emitOptions?: NodeEmitOptions): PrimaryExpression {
if (typeof value === "number") {
const node = <LiteralExpression>createNode(SyntaxKind.NumericLiteral, location);
const node = <LiteralExpression>createNode(SyntaxKind.NumericLiteral, location, /*flags*/ undefined, emitOptions);
node.text = value.toString();
return node;
}
else if (typeof value === "boolean") {
return <PrimaryExpression>createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, location);
return <PrimaryExpression>createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, location, /*flags*/ undefined, emitOptions);
}
else if (typeof value === "string") {
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location);
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location, /*flags*/ undefined, emitOptions);
node.text = value;
return node;
}
else {
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location);
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location, /*flags*/ undefined, emitOptions);
node.textSourceNode = value;
node.text = value.text;
return node;
@ -256,10 +250,14 @@ namespace ts {
}
export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression, location?: TextRange) {
return createParameterWithDotDotDotToken(/*dotDotDotToken*/ undefined, name, initializer, location);
}
export function createParameterWithDotDotDotToken(dotDotDotToken: Node, name: string | Identifier | BindingPattern, initializer?: Expression, location?: TextRange) {
const node = <ParameterDeclaration>createNode(SyntaxKind.Parameter, location);
node.decorators = undefined;
node.modifiers = undefined;
node.dotDotDotToken = undefined;
node.dotDotDotToken = dotDotDotToken;
node.name = typeof name === "string" ? createIdentifier(name) : name;
node.questionToken = undefined;
node.type = undefined;
@ -287,8 +285,8 @@ namespace ts {
return node;
}
export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, location);
export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange, emitOptions?: NodeEmitOptions) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, location, /*flags*/ undefined, emitOptions);
node.expression = parenthesizeForAccess(expression);
node.dotToken = createSynthesizedNode(SyntaxKind.DotToken);
node.name = typeof name === "string" ? createIdentifier(name) : name;
@ -738,7 +736,7 @@ namespace ts {
export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression {
if (isIdentifier(memberName)) {
return createPropertyAccess(target, getSynthesizedClone(memberName), location);
return createPropertyAccess(target, memberName, location, { flags: NodeEmitFlags.NoNestedSourceMaps | NodeEmitFlags.Merge });
}
else if (isComputedPropertyName(memberName)) {
return createElementAccess(target, memberName.expression, location);
@ -749,8 +747,7 @@ namespace ts {
}
export function createRestParameter(name: string | Identifier) {
const node = createParameter(name, /*initializer*/ undefined);
node.dotDotDotToken = createSynthesizedNode(SyntaxKind.DotDotDotToken);
const node = createParameterWithDotDotDotToken(createSynthesizedNode(SyntaxKind.DotDotDotToken), name, /*initializer*/ undefined);
return node;
}
@ -1279,10 +1276,16 @@ namespace ts {
: getSynthesizedClone(node);
}
export function createExpressionForPropertyName(memberName: PropertyName, location?: TextRange): Expression {
return isIdentifier(memberName) ? createLiteral(memberName.text, location)
: isComputedPropertyName(memberName) ? getRelocatedClone(memberName.expression, location || synthesizedLocation)
: getRelocatedClone(memberName, location || synthesizedLocation);
export function createExpressionForPropertyName(memberName: PropertyName, emitOptions: NodeEmitOptions): Expression {
if (isIdentifier(memberName)) {
return createLiteral(memberName, /*location*/ undefined, emitOptions);
}
else if (isComputedPropertyName(memberName)) {
return getMutableClone(memberName.expression, emitOptions);
}
else {
return getMutableClone(memberName, emitOptions);
}
}
// Utilities

View File

@ -172,7 +172,8 @@ const _super = (function (geti, seti) {
let context: TransformationContext;
let getNodeEmitFlags: (node: Node) => NodeEmitFlags;
let setNodeEmitFlags: (node: Node, flags: NodeEmitFlags) => void;
let getNodeCustomCommentRange: (node: Node) => TextRange;
let getCommentRange: (node: Node) => TextRange;
let getSourceMapRange: (node: Node) => TextRange;
let isSubstitutionEnabled: (node: Node) => boolean;
let isEmitNotificationEnabled: (node: Node) => boolean;
let onSubstituteNode: (node: Node, isExpression: boolean) => Node;
@ -233,7 +234,8 @@ const _super = (function (geti, seti) {
getNodeEmitFlags = undefined;
setNodeEmitFlags = undefined;
getNodeCustomCommentRange = undefined;
getCommentRange = undefined;
getSourceMapRange = undefined;
isSubstitutionEnabled = undefined;
isEmitNotificationEnabled = undefined;
onSubstituteNode = undefined;
@ -253,7 +255,8 @@ const _super = (function (geti, seti) {
context = _context;
getNodeEmitFlags = context.getNodeEmitFlags;
setNodeEmitFlags = context.setNodeEmitFlags;
getNodeCustomCommentRange = context.getNodeCustomCommentRange;
getCommentRange = context.getCommentRange;
getSourceMapRange = context.getSourceMapRange;
isSubstitutionEnabled = context.isSubstitutionEnabled;
isEmitNotificationEnabled = context.isEmitNotificationEnabled;
onSubstituteNode = context.onSubstituteNode;
@ -337,12 +340,12 @@ const _super = (function (geti, seti) {
function emitNodeWithWorker(node: Node, emitWorker: (node: Node) => void) {
if (node) {
const leadingComments = getLeadingComments(node, shouldSkipLeadingCommentsForNode, getNodeCustomCommentRange);
const trailingComments = getTrailingComments(node, shouldSkipTrailingCommentsForNode, getNodeCustomCommentRange);
const leadingComments = getLeadingComments(node, shouldSkipLeadingCommentsForNode, getCommentRange);
const trailingComments = getTrailingComments(node, shouldSkipTrailingCommentsForNode, getCommentRange);
emitLeadingComments(node, leadingComments);
emitStart(node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren);
emitStart(node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitWorker(node);
emitEnd(node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren);
emitEnd(node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitTrailingComments(node, trailingComments);
}
}
@ -2429,9 +2432,9 @@ const _super = (function (geti, seti) {
function writeTokenNode(node: Node) {
if (node) {
emitStart(node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren);
emitStart(node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
writeTokenText(node.kind);
emitEnd(node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren);
emitEnd(node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
}
}

View File

@ -7,9 +7,9 @@ namespace ts {
setSourceFile(sourceFile: SourceFile): void;
emitPos(pos: number, contextNode: Node, shouldIgnorePosCallback: (node: Node) => boolean): void;
emitPos(pos: number): void;
emitStart(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean): void;
emitStart(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void;
emitStart(range: TextRange): void;
emitEnd(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean): void;
emitEnd(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean, getCustomSourceMapRangeForNode?: (node: Node) => TextRange): void;
emitEnd(range: TextRange): void;
/*@deprecated*/ changeEmitSourcePos(): void;
/*@deprecated*/ stopOverridingSpan(): void;
@ -36,8 +36,8 @@ namespace ts {
nullSourceMapWriter = {
getSourceMapData(): SourceMapData { return undefined; },
setSourceFile(sourceFile: SourceFile): void { },
emitStart(range: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean): void { },
emitEnd(range: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean): 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 { },
@ -327,26 +327,36 @@ namespace ts {
return range.pos !== -1 ? skipTrivia(currentSourceFile.text, rangeHasDecorators ? (range as Node).decorators.end : range.pos) : -1;
}
function emitStart(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean): void;
function emitStart(range: TextRange): void;
function emitStart(range: TextRange | Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean) {
if (!(shouldIgnoreNodeCallback && shouldIgnoreNodeCallback(<Node>range))) {
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;
}
emitPos(getStartPos(range));
}
if (shouldIgnoreChildrenCallback && shouldIgnoreChildrenCallback(<Node>range)) {
if (shouldIgnoreChildrenCallback && shouldIgnoreChildrenCallback(<Node>nodeOrRange)) {
disable();
}
}
function emitEnd(node: Node, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean): void;
function emitEnd(range: TextRange): void;
function emitEnd(range: TextRange, shouldIgnoreNodeCallback?: (node: Node) => boolean, shouldIgnoreChildrenCallback?: (node: Node) => boolean) {
if (shouldIgnoreChildrenCallback && shouldIgnoreChildrenCallback(<Node>range)) {
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>range))) {
if (!(shouldIgnoreNodeCallback && shouldIgnoreNodeCallback(<Node>nodeOrRange))) {
if (getCustomSourceMapRangeForNode) {
range = getCustomSourceMapRangeForNode(<Node>nodeOrRange) || range;
}
emitPos(range.end);
}

View File

@ -54,8 +54,7 @@ namespace ts {
* @param transforms An array of Transformers.
*/
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) {
const nodeEmitFlags: NodeEmitFlags[] = [];
const nodeCustomCommentRange: TextRange[] = [];
const nodeEmitOptions: NodeEmitOptions[] = [];
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
@ -73,8 +72,10 @@ namespace ts {
getEmitHost: () => host,
getNodeEmitFlags,
setNodeEmitFlags,
getNodeCustomCommentRange,
setNodeCustomCommentRange,
getSourceMapRange,
setSourceMapRange,
getCommentRange,
setCommentRange,
hoistVariableDeclaration,
hoistFunctionDeclaration,
startLexicalEnvironment,
@ -107,10 +108,16 @@ namespace ts {
return transformation(sourceFile);
}
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
function enableSubstitution(kind: SyntaxKind) {
enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.Substitution;
}
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
function isSubstitutionEnabled(node: Node) {
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0;
}
@ -126,10 +133,17 @@ namespace ts {
return node;
}
/**
* Enables before/after emit notifications in the pretty printer for the provided SyntaxKind.
*/
function enableEmitNotification(kind: SyntaxKind) {
enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.EmitNotifications;
}
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
function isEmitNotificationEnabled(node: Node) {
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0
|| (getNodeEmitFlags(node) & NodeEmitFlags.AdviseOnEmitNode) !== 0;
@ -154,51 +168,94 @@ namespace ts {
emit(node);
}
function getNodeEmitOptions(node: Node, createIfMissing: boolean) {
// Keeps track of the nearest set of options
let options: NodeEmitOptions;
let currentNode = node;
while (currentNode) {
const currentOptions = currentNode.emitOptions || nodeEmitOptions[getNodeId(currentNode)];
if (currentOptions) {
options = currentOptions;
break;
}
currentNode = currentNode.original;
}
if (currentNode !== node && createIfMissing) {
options = options ? clone(options) : { };
if (isSourceTreeNode(node)) {
nodeEmitOptions[getNodeId(node)] = options;
}
else {
node.emitOptions = options;
}
}
// Merge with previous options on get.
if (options && options.flags & NodeEmitFlags.Merge) {
const previousOptions = getNodeEmitOptions(currentNode.original, /*createIfMissing*/ false);
if (previousOptions) {
options.flags = (options.flags | previousOptions.flags) & ~NodeEmitFlags.Merge;
if (!options.sourceMapRange && (options.flags & NodeEmitFlags.NoSourceMap) === 0) {
options.sourceMapRange = previousOptions.sourceMapRange;
}
if (!options.commentRange && (options.flags & NodeEmitFlags.NoComments) === 0) {
options.commentRange = previousOptions.commentRange;
}
}
}
return options;
}
/**
* Gets flags that control emit behavior of a node.
*/
function getNodeEmitFlags(node: Node) {
while (node) {
const nodeId = node.id;
if (nodeId && nodeEmitFlags[nodeId] !== undefined) {
return nodeEmitFlags[nodeId];
}
node = node.original;
}
return undefined;
const options = getNodeEmitOptions(node, /*createIfMissing*/ false);
return options && options.flags;
}
/**
* Sets flags that control emit behavior of a node.
*/
function setNodeEmitFlags<T extends Node>(node: T, flags: NodeEmitFlags) {
nodeEmitFlags[getNodeId(node)] = flags;
getNodeEmitOptions(node, /*createIfMissing*/ true).flags = flags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*/
function getSourceMapRange(node: Node) {
const options = getNodeEmitOptions(node, /*createIfMissing*/ false);
return options && options.sourceMapRange;
}
/**
* Sets a custom text range to use when emitting source maps.
*/
function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
getNodeEmitOptions(node, /*createIfMissing*/ true).sourceMapRange = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*/
function getNodeCustomCommentRange(node: Node) {
while (node) {
const nodeId = node.id;
if (nodeId && nodeCustomCommentRange[nodeId] !== undefined) {
return nodeCustomCommentRange[nodeId];
}
node = node.original;
}
return undefined;
function getCommentRange(node: Node) {
const options = getNodeEmitOptions(node, /*createIfMissing*/ false);
return options && options.commentRange;
}
/**
* Sets a custom text range to use when emitting comments.
*/
function setNodeCustomCommentRange(node: Node, range: TextRange) {
nodeCustomCommentRange[getNodeId(node)] = range;
function setCommentRange<T extends Node>(node: T, range: TextRange) {
getNodeEmitOptions(node, /*createIfMissing*/ true).commentRange = range;
return node;
}
/**

View File

@ -251,7 +251,7 @@ namespace ts {
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value, location);
}
else {
const name = getRelocatedClone(<Identifier>target, /*location*/ target);
const name = getSynthesizedClone(<Identifier>target, { sourceMapRange: target, commentRange: target });
emitAssignment(name, value, location, /*original*/ undefined);
}
}

View File

@ -145,6 +145,8 @@ namespace ts {
hoistVariableDeclaration,
getNodeEmitFlags,
setNodeEmitFlags,
setCommentRange,
setSourceMapRange
} = context;
const resolver = context.getEmitResolver();
@ -1055,18 +1057,19 @@ namespace ts {
function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): void {
if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) {
enableSubstitutionsForCapturedThis();
statements.push(
createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList([
createVariableDeclaration(
"_this",
createThis()
)
]),
/*location*/ node
)
const captureThisStatement = createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList([
createVariableDeclaration(
"_this",
createThis()
)
])
);
setNodeEmitFlags(captureThisStatement, NodeEmitFlags.NoComments);
setSourceMapRange(captureThisStatement, node);
statements.push(captureThisStatement);
}
}
@ -1174,11 +1177,8 @@ namespace ts {
function transformAccessorsToExpression(receiver: LeftHandSideExpression, { firstAccessor, getAccessor, setAccessor }: AllAccessorDeclarations): Expression {
// To align with source maps in the old emitter, the receiver and property name
// arguments are both mapped contiguously to the accessor name.
const target = getSynthesizedClone(receiver);
target.pos = firstAccessor.name.pos;
const propertyName = createExpressionForPropertyName(visitNode(firstAccessor.name, visitor, isPropertyName));
propertyName.end = firstAccessor.name.end;
const target = getMutableClone(receiver, { flags: NodeEmitFlags.Merge | NodeEmitFlags.NoComments, sourceMapRange: moveRangeEnd(firstAccessor.name, -1) });
const propertyName = createExpressionForPropertyName(visitNode(firstAccessor.name, visitor, isPropertyName), { flags: NodeEmitFlags.NoComments, sourceMapRange: moveRangePos(firstAccessor.name, -1) });
let getAccessorExpression: FunctionExpression;
if (getAccessor) {

View File

@ -24,8 +24,10 @@ namespace ts {
export function transformTypeScript(context: TransformationContext) {
const {
setNodeEmitFlags,
getNodeEmitFlags,
setNodeEmitFlags,
setCommentRange,
setSourceMapRange,
startLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
@ -911,16 +913,18 @@ namespace ts {
*/
function transformParameterWithPropertyAssignment(node: ParameterDeclaration) {
Debug.assert(isIdentifier(node.name));
const name = node.name as Identifier;
const propertyName = getMutableClone(name, { flags: NodeEmitFlags.NoComments | NodeEmitFlags.NoSourceMap });
const localName = getMutableClone(name, { flags: NodeEmitFlags.NoComments });
return startOnNewLine(
createStatement(
createAssignment(
createPropertyAccess(
createThis(),
getMutableClone(<Identifier>node.name),
propertyName,
/*location*/ node.name
),
getUniqueClone(<Identifier>node.name)
localName
),
/*location*/ node
)
@ -1011,8 +1015,13 @@ namespace ts {
function transformInitializedProperty(node: ClassExpression | ClassDeclaration, property: PropertyDeclaration, receiver: LeftHandSideExpression, location?: TextRange) {
const propertyName = visitPropertyNameOfClassElement(property);
const initializer = visitNode(property.initializer, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
if (!isComputedPropertyName(propertyName)) {
setNodeEmitFlags(memberAccess, NodeEmitFlags.NoNestedSourceMaps);
}
return createAssignment(
createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName),
memberAccess,
initializer,
location
);
@ -1910,7 +1919,7 @@ namespace ts {
visitPropertyNameOfClassElement(node),
visitNodes(node.parameters, visitor, isParameter),
transformFunctionBody(node),
/*location*/ getUndecoratedRange(node)
/*location*/ node
);
setOriginalNode(method, node);
@ -1918,7 +1927,8 @@ namespace ts {
// While we emit the source map for the node after skipping the decorators,
// we need to emit the comments for the original range.
if (node.decorators) {
context.setNodeCustomCommentRange(method, node);
setCommentRange(method, node);
setSourceMapRange(method, getUndecoratedRange(node));
}
return method;
@ -1952,7 +1962,7 @@ namespace ts {
visitNodes(node.modifiers, visitor, isModifier),
visitPropertyNameOfClassElement(node),
node.body ? visitEachChild(node.body, visitor, context) : createBlock([]),
/*location*/ getUndecoratedRange(node)
/*location*/ node
);
setOriginalNode(accessor, node);
@ -1960,7 +1970,8 @@ namespace ts {
// While we emit the source map for the node after skipping the decorators,
// we need to emit the comments for the original range.
if (node.decorators) {
context.setNodeCustomCommentRange(accessor, node);
setCommentRange(accessor, node);
setSourceMapRange(accessor, getUndecoratedRange(node));
}
return accessor;
@ -1985,7 +1996,7 @@ namespace ts {
visitPropertyNameOfClassElement(node),
visitNode(firstOrUndefined(node.parameters), visitor, isParameter),
node.body ? visitEachChild(node.body, visitor, context) : createBlock([]),
/*location*/ getUndecoratedRange(node)
/*location*/ node
);
setOriginalNode(accessor, node);
@ -1993,7 +2004,8 @@ namespace ts {
// While we emit the source map for the node after skipping the decorators,
// we need to emit the comments for the original range.
if (node.decorators) {
context.setNodeCustomCommentRange(accessor, node);
setCommentRange(accessor, node);
setSourceMapRange(accessor, getUndecoratedRange(node));
}
return accessor;
@ -2176,21 +2188,27 @@ namespace ts {
* @param node The parameter declaration node.
*/
function visitParameter(node: ParameterDeclaration) {
if (node.name && (node.name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword) {
if (node.name && isIdentifier(node.name) && (node.name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword) {
return undefined;
}
const clone = getMutableClone(node);
clone.decorators = undefined;
clone.modifiers = undefined;
clone.questionToken = undefined;
clone.type = undefined;
const parameter = createParameterWithDotDotDotToken(
node.dotDotDotToken,
visitNode(node.name, visitor, isBindingName),
visitNode(node.initializer, visitor, isExpression),
/*location*/ node
);
setOriginalNode(parameter, node);
// While we emit the source map for the node after skipping the decorators,
// we need to emit the comments for the original range.
if (node.decorators) {
setTextRange(clone, getUndecoratedRange(node));
setCommentRange(parameter, node);
setSourceMapRange(parameter, getUndecoratedRange(node));
}
aggregateTransformFlags(clone);
return visitEachChild(clone, visitor, context);
return parameter;
}
/**
@ -3097,7 +3115,7 @@ namespace ts {
if (declaration) {
const classAlias = currentDecoratedClassAliases[getNodeId(declaration)];
if (classAlias) {
return getRelocatedClone(classAlias, /*location*/ node);
return getSynthesizedClone(classAlias, { sourceMapRange: node, commentRange: node });
}
}
}

View File

@ -469,6 +469,7 @@ namespace ts {
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
/* @internal */ emitOptions?: NodeEmitOptions; // Options used to control node emit (used by transforms, should never be set directly on a source tree node)
}
export interface NodeArray<T extends Node> extends Array<T>, TextRange {
@ -2941,16 +2942,34 @@ namespace ts {
ExportName = 1 << 16, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
LocalName = 1 << 17, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
Indented = 1 << 18, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
Merge = 1 << 19, // When getting emit options, merge with existing emit options.
// SourceMap Specialization.
// TODO(rbuckton): These should be removed once source maps are aligned with the old
// emitter and new baselines are taken. This exists solely to
// align with the old emitter.
SourceMapEmitOpenBraceAsToken = 1 << 19, // Emits the open brace of a block function body as a source mapped token.
SourceMapAdjustRestParameterLoop = 1 << 20, // Emits adjusted source map positions for a ForStatement generated when transforming a rest parameter for ES5/3.
SourceMapEmitOpenBraceAsToken = 1 << 20, // Emits the open brace of a block function body as a source mapped token.
SourceMapAdjustRestParameterLoop = 1 << 21, // Emits adjusted source map positions for a ForStatement generated when transforming a rest parameter for ES5/3.
}
/* @internal */
export interface NodeEmitOptions {
/**
* Specifies a custom range to use when emitting source maps.
*/
sourceMapRange?: TextRange;
/**
* Specifies a custom range to use when emitting comments.
*/
commentRange?: TextRange;
/**
* Specifies flags to use to customize emit.
*/
flags?: NodeEmitFlags;
}
/** Additional context provided to `visitEachChild` */
/* @internal */
export interface LexicalEnvironment {
/** Starts a new lexical environment. */
startLexicalEnvironment(): void;
@ -2964,22 +2983,54 @@ namespace ts {
getCompilerOptions(): CompilerOptions;
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/**
* Gets flags used to customize later transformations or emit.
*/
getNodeEmitFlags(node: Node): NodeEmitFlags;
/**
* Sets flags used to customize later transformations or emit.
*/
setNodeEmitFlags<T extends Node>(node: T, flags: NodeEmitFlags): T;
getNodeCustomCommentRange(node: Node): TextRange;
setNodeCustomCommentRange(node: Node, range: TextRange): void;
/**
* Gets the TextRange to use for source maps for the node.
*/
getSourceMapRange(node: Node): TextRange;
/**
* Sets the TextRange to use for source maps for the node.
*/
setSourceMapRange<T extends Node>(node: T, range: TextRange): T;
/**
* Gets the TextRange to use for comments for the node.
*/
getCommentRange(node: Node): TextRange;
/**
* Sets the TextRange to use for comments for the node.
*/
setCommentRange<T extends Node>(node: T, range: TextRange): T;
/**
* Hoists a function declaration to the containing scope.
*/
hoistFunctionDeclaration(node: FunctionDeclaration): void;
/**
* Hoists a variable declaration to the containing scope.
*/
hoistVariableDeclaration(node: Identifier): void;
/**
* Enables expression substitutions in the pretty printer for
* the provided SyntaxKind.
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
enableSubstitution(kind: SyntaxKind): void;
/**
* Determines whether expression substitutions are enabled for the
* provided node.
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
@ -2990,14 +3041,14 @@ namespace ts {
onSubstituteNode?: (node: Node, isExpression: boolean) => Node;
/**
* Enables before/after emit notifications in the pretty printer for
* the provided SyntaxKind.
* Enables before/after emit notifications in the pretty printer for the provided
* SyntaxKind.
*/
enableEmitNotification(kind: SyntaxKind): void;
/**
* Determines whether before/after emit notifications should be raised
* in the pretty printer when it emits a node.
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;

View File

@ -1830,12 +1830,16 @@ namespace ts {
return node;
}
export function isSourceTreeNode(node: Node): boolean {
return node.original === undefined
&& (node.parent !== undefined || node.kind === SyntaxKind.SourceFile);
}
export function getSourceTreeNode(node: Node): Node {
node = getOriginalNode(node);
if (node) {
if (node.parent || node.kind === SyntaxKind.SourceFile) {
return node;
}
if (node && (node.parent !== undefined || node.kind === SyntaxKind.SourceFile)) {
return node;
}
return undefined;