PR Feedback
This commit is contained in:
parent
fe7ad5fde3
commit
a0dbe7601c
|
@ -4846,7 +4846,7 @@ namespace ts {
|
|||
const parent = container && container.parent;
|
||||
if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) {
|
||||
if (!(container.flags & NodeFlags.Static) &&
|
||||
(container.kind !== SyntaxKind.Constructor || isNodeDescendentOf(node, (<ConstructorDeclaration>container).body))) {
|
||||
(container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (<ConstructorDeclaration>container).body))) {
|
||||
return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType;
|
||||
}
|
||||
}
|
||||
|
@ -7206,8 +7206,8 @@ namespace ts {
|
|||
let container = getContainingClass(node);
|
||||
while (container !== undefined) {
|
||||
if (container === localOrExportSymbol.valueDeclaration && container.name !== node) {
|
||||
getNodeLinks(container).flags |= NodeCheckFlags.ClassWithBodyScopedClassBinding;
|
||||
getNodeLinks(node).flags |= NodeCheckFlags.BodyScopedClassBinding;
|
||||
getNodeLinks(container).flags |= NodeCheckFlags.DecoratedClassWithSelfReference;
|
||||
getNodeLinks(node).flags |= NodeCheckFlags.SelfReferenceInDecoratedClass;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||
};
|
||||
|
||||
function isUniqueLocalName(name: string, container: Node): boolean {
|
||||
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
|
||||
for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) {
|
||||
if (node.locals && hasProperty(node.locals, name)) {
|
||||
// We conservatively include alias symbols to cover cases where they're emitted as locals
|
||||
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
|
||||
|
@ -1529,7 +1529,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
|
||||
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.SelfReferenceInDecoratedClass) {
|
||||
// Due to the emit for class decorators, any reference to the class from inside of the class body
|
||||
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
|
||||
// behavior of class names in ES6.
|
||||
|
@ -5203,7 +5203,7 @@ const _super = (function (geti, seti) {
|
|||
// [Example 4]
|
||||
//
|
||||
|
||||
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
|
||||
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.DecoratedClassWithSelfReference) {
|
||||
decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default"));
|
||||
decoratedClassAliases[getNodeId(node)] = decoratedClassAlias;
|
||||
write(`let ${decoratedClassAlias};`);
|
||||
|
|
|
@ -2368,7 +2368,7 @@ const _super = (function (geti, seti) {
|
|||
}
|
||||
|
||||
function isUniqueLocalName(name: string, container: Node): boolean {
|
||||
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
|
||||
for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) {
|
||||
if (node.locals && hasProperty(node.locals, name)) {
|
||||
// We conservatively include alias symbols to cover cases where they're emitted as locals
|
||||
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
|
||||
|
|
|
@ -6,6 +6,15 @@
|
|||
namespace ts {
|
||||
type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration;
|
||||
|
||||
const enum TypeScriptSubstitutionFlags {
|
||||
/** Enables substitutions for decorated classes. */
|
||||
DecoratedClasses = 1 << 0,
|
||||
/** Enables substitutions for namespace exports. */
|
||||
NamespaceExports = 1 << 1,
|
||||
/** Enables substitutions for async methods with `super` calls. */
|
||||
AsyncMethodsWithSuper = 1 << 2,
|
||||
}
|
||||
|
||||
export function transformTypeScript(context: TransformationContext) {
|
||||
const {
|
||||
setNodeEmitFlags,
|
||||
|
@ -19,14 +28,14 @@ namespace ts {
|
|||
const languageVersion = getEmitScriptTarget(compilerOptions);
|
||||
|
||||
// Save the previous transformation hooks.
|
||||
const previousExpressionSubstitution = context.expressionSubstitution;
|
||||
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
|
||||
const previousOnAfterEmitNode = context.onAfterEmitNode;
|
||||
const previousExpressionSubstitution = context.expressionSubstitution;
|
||||
|
||||
// Set new transformation hooks.
|
||||
context.expressionSubstitution = substituteExpression;
|
||||
context.onBeforeEmitNode = onBeforeEmitNode;
|
||||
context.onAfterEmitNode = onAfterEmitNode;
|
||||
context.expressionSubstitution = substituteExpression;
|
||||
|
||||
// These variables contain state that changes as we descend into the tree.
|
||||
let currentSourceFile: SourceFile;
|
||||
|
@ -36,31 +45,46 @@ namespace ts {
|
|||
let currentParent: Node;
|
||||
let currentNode: Node;
|
||||
|
||||
// These variables keep track of whether expression substitution has been enabled for
|
||||
// specific edge cases. They are persisted between each SourceFile transformation and
|
||||
// should not be reset.
|
||||
let hasEnabledExpressionSubstitutionForDecoratedClasses = false;
|
||||
let hasEnabledExpressionSubstitutionForNamespaceExports = false;
|
||||
let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false;
|
||||
/**
|
||||
* Keeps track of whether expression substitution has been enabled for specific edge cases.
|
||||
* They are persisted between each SourceFile transformation and should not be reset.
|
||||
*/
|
||||
let enabledSubstitutions: TypeScriptSubstitutionFlags;
|
||||
|
||||
// This map keeps track of aliases created for classes with decorators to avoid issues
|
||||
// with the double-binding behavior of classes.
|
||||
/**
|
||||
* A map that keeps track of aliases created for classes with decorators to avoid issues
|
||||
* with the double-binding behavior of classes.
|
||||
*/
|
||||
let decoratedClassAliases: Map<Identifier>;
|
||||
|
||||
// This map keeps track of currently active aliases defined in `decoratedClassAliases`
|
||||
// when just-in-time substitution occurs while printing an expression identifier.
|
||||
/**
|
||||
* A map that keeps track of currently active aliases defined in `decoratedClassAliases`
|
||||
* when just-in-time substitution occurs while printing an expression identifier.
|
||||
*/
|
||||
let currentDecoratedClassAliases: Map<Identifier>;
|
||||
|
||||
// This value keeps track of how deeply nested we are within any containing namespaces
|
||||
// when performing just-in-time substitution while printing an expression identifier.
|
||||
/**
|
||||
* Keeps track of how deeply nested we are within any containing namespaces
|
||||
* when performing just-in-time substitution while printing an expression identifier.
|
||||
* If the nest level is greater than zero, then we are performing a substitution
|
||||
* inside of a namespace and we should perform the more costly checks to determine
|
||||
* whether the identifier points to an exported declaration.
|
||||
*/
|
||||
let namespaceNestLevel: number;
|
||||
|
||||
// This array keeps track of containers where `super` is valid, for use with
|
||||
// just-in-time substitution for `super` expressions inside of async methods.
|
||||
/**
|
||||
* This array keeps track of containers where `super` is valid, for use with
|
||||
* just-in-time substitution for `super` expressions inside of async methods.
|
||||
*/
|
||||
let superContainerStack: SuperContainer[];
|
||||
|
||||
return transformSourceFile;
|
||||
|
||||
/**
|
||||
* Transform TypeScript-specific syntax in a SourceFile.
|
||||
*
|
||||
* @param node A SourceFile node.
|
||||
*/
|
||||
function transformSourceFile(node: SourceFile) {
|
||||
currentSourceFile = node;
|
||||
node = visitEachChild(node, visitor, context);
|
||||
|
@ -136,8 +160,7 @@ namespace ts {
|
|||
* @param node The node to visit.
|
||||
*/
|
||||
function namespaceElementVisitorWorker(node: Node): Node {
|
||||
if (node.transformFlags & TransformFlags.TypeScript
|
||||
|| node.flags & NodeFlags.Export) {
|
||||
if (node.transformFlags & TransformFlags.TypeScript || isExported(node)) {
|
||||
// This node is explicitly marked as TypeScript, or is exported at the namespace
|
||||
// level, so we should transform the node.
|
||||
node = visitTypeScript(node);
|
||||
|
@ -165,12 +188,25 @@ namespace ts {
|
|||
* @param node The node to visit.
|
||||
*/
|
||||
function classElementVisitorWorker(node: Node) {
|
||||
if (node.kind === SyntaxKind.Constructor) {
|
||||
// TypeScript constructors are elided.
|
||||
return undefined;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
// TypeScript constructors are transformed in `transformClassDeclaration`.
|
||||
// We elide them here as `visitorWorker` checks transform flags, which could
|
||||
// erronously include an ES6 constructor without TypeScript syntax.
|
||||
return undefined;
|
||||
|
||||
return visitorWorker(node);
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.IndexSignature:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// Fallback to the default visit behavior.
|
||||
return visitorWorker(node);
|
||||
|
||||
default:
|
||||
Debug.fail("Unexpected node.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,9 +215,9 @@ namespace ts {
|
|||
* @param node The node to visit.
|
||||
*/
|
||||
function visitTypeScript(node: Node): Node {
|
||||
// TypeScript ambient declarations are elided.
|
||||
if (node.flags & NodeFlags.Ambient) {
|
||||
return;
|
||||
// TypeScript ambient declarations are elided.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
|
@ -233,8 +269,7 @@ namespace ts {
|
|||
// TypeScript property declarations are elided.
|
||||
|
||||
case SyntaxKind.Constructor:
|
||||
// TypeScript constructors are elided. The constructor of a class will be
|
||||
// transformed as part of `transformClassDeclaration`.
|
||||
// TypeScript constructors are transformed in `transformClassDeclaration`.
|
||||
return undefined;
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
|
@ -376,11 +411,7 @@ namespace ts {
|
|||
*/
|
||||
function visitClassDeclaration(node: ClassDeclaration): NodeArrayNode<Statement> {
|
||||
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
|
||||
const statements: Statement[] = [];
|
||||
const modifiers = visitNodes(node.modifiers, visitor, isModifier);
|
||||
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
|
||||
const members = transformClassMembers(node, heritageClauses !== undefined);
|
||||
let decoratedClassAlias: Identifier;
|
||||
const hasExtendsClause = getClassExtendsHeritageClauseElement(node) !== undefined;
|
||||
|
||||
// emit name if
|
||||
// - node has a name
|
||||
|
@ -391,166 +422,28 @@ namespace ts {
|
|||
name = getGeneratedNameForNode(node);
|
||||
}
|
||||
|
||||
if (node.decorators) {
|
||||
// When we emit an ES6 class that has a class decorator, we must tailor the
|
||||
// emit to certain specific cases.
|
||||
//
|
||||
// In the simplest case, we emit the class declaration as a let declaration, and
|
||||
// evaluate decorators after the close of the class body:
|
||||
//
|
||||
// [Example 1]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C = class C {
|
||||
// class C { | }
|
||||
// } | C = __decorate([dec], C);
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C = class C {
|
||||
// export class C { | }
|
||||
// } | C = __decorate([dec], C);
|
||||
// | export { C };
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// If a class declaration contains a reference to itself *inside* of the class body,
|
||||
// this introduces two bindings to the class: One outside of the class body, and one
|
||||
// inside of the class body. If we apply decorators as in [Example 1] above, there
|
||||
// is the possibility that the decorator `dec` will return a new value for the
|
||||
// constructor, which would result in the binding inside of the class no longer
|
||||
// pointing to the same reference as the binding outside of the class.
|
||||
//
|
||||
// As a result, we must instead rewrite all references to the class *inside* of the
|
||||
// class body to instead point to a local temporary alias for the class:
|
||||
//
|
||||
// [Example 2]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C_1;
|
||||
// class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C_1;
|
||||
// export class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// | export { C };
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// If a class declaration is the default export of a module, we instead emit
|
||||
// the export after the decorated declaration:
|
||||
//
|
||||
// [Example 3]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let default_1 = class {
|
||||
// export default class { | }
|
||||
// } | default_1 = __decorate([dec], default_1);
|
||||
// | export default default_1;
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C = class C {
|
||||
// export default class C { | }
|
||||
// } | C = __decorate([dec], C);
|
||||
// | export default C;
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// If the class declaration is the default export and a reference to itself
|
||||
// inside of the class body, we must emit both an alias for the class *and*
|
||||
// move the export after the declaration:
|
||||
//
|
||||
// [Example 4]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C_1;
|
||||
// export default class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// | export default C;
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
|
||||
// class ${name} ${heritageClauses} {
|
||||
// ${members}
|
||||
// }
|
||||
let classExpression: Expression = setOriginalNode(
|
||||
createClassExpression(
|
||||
name,
|
||||
heritageClauses,
|
||||
members,
|
||||
/*location*/ node
|
||||
),
|
||||
node
|
||||
);
|
||||
|
||||
// Record an alias to avoid class double-binding.
|
||||
if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
|
||||
enableExpressionSubstitutionForDecoratedClasses();
|
||||
decoratedClassAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default");
|
||||
decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias;
|
||||
|
||||
// We emit the class alias as a `let` declaration here so that it has the same
|
||||
// TDZ as the class.
|
||||
|
||||
// let ${decoratedClassAlias};
|
||||
addNode(statements,
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(decoratedClassAlias)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
NodeFlags.Let)
|
||||
)
|
||||
);
|
||||
|
||||
// ${decoratedClassAlias} = ${classExpression}
|
||||
classExpression = createAssignment(
|
||||
cloneNode(decoratedClassAlias),
|
||||
classExpression,
|
||||
/*location*/ node);
|
||||
}
|
||||
|
||||
// let ${name} = ${classExpression};
|
||||
addNode(statements,
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
name,
|
||||
classExpression
|
||||
)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
NodeFlags.Let)
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
let decoratedClassAlias: Identifier;
|
||||
const statements: Statement[] = [];
|
||||
if (!node.decorators) {
|
||||
// ${modifiers} class ${name} ${heritageClauses} {
|
||||
// ${members}
|
||||
// }
|
||||
addNode(statements,
|
||||
setOriginalNode(
|
||||
createClassDeclaration(
|
||||
modifiers,
|
||||
visitNodes(node.modifiers, visitor, isModifier),
|
||||
name,
|
||||
heritageClauses,
|
||||
members,
|
||||
visitNodes(node.heritageClauses, visitor, isHeritageClause),
|
||||
transformClassMembers(node, hasExtendsClause),
|
||||
/*location*/ node
|
||||
),
|
||||
node
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
decoratedClassAlias = addClassDeclarationHeadWithDecorators(statements, node, name, hasExtendsClause);
|
||||
}
|
||||
|
||||
// Emit static property assignment. Because classDeclaration is lexically evaluated,
|
||||
// it is safe to emit static property assignment after classDeclaration
|
||||
|
@ -575,13 +468,168 @@ namespace ts {
|
|||
addNode(statements, createExportDefault(name));
|
||||
}
|
||||
else if (isNamedExternalModuleExport(node)) {
|
||||
addNode(statements, createModuleExport(name));
|
||||
addNode(statements, createExternalModuleExport(name));
|
||||
}
|
||||
}
|
||||
|
||||
return createNodeArrayNode(statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a decorated class declaration and appends the resulting statements. If
|
||||
* the class requires an alias to avoid issues with double-binding, the alias is returned.
|
||||
*
|
||||
* @param node A ClassDeclaration node.
|
||||
* @param name The name of the class.
|
||||
* @param hasExtendsClause A value indicating whether
|
||||
*/
|
||||
function addClassDeclarationHeadWithDecorators(statements: Statement[], node: ClassDeclaration, name: Identifier, hasExtendsClause: boolean) {
|
||||
// When we emit an ES6 class that has a class decorator, we must tailor the
|
||||
// emit to certain specific cases.
|
||||
//
|
||||
// In the simplest case, we emit the class declaration as a let declaration, and
|
||||
// evaluate decorators after the close of the class body:
|
||||
//
|
||||
// [Example 1]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C = class C {
|
||||
// class C { | }
|
||||
// } | C = __decorate([dec], C);
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C = class C {
|
||||
// export class C { | }
|
||||
// } | C = __decorate([dec], C);
|
||||
// | export { C };
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// If a class declaration contains a reference to itself *inside* of the class body,
|
||||
// this introduces two bindings to the class: One outside of the class body, and one
|
||||
// inside of the class body. If we apply decorators as in [Example 1] above, there
|
||||
// is the possibility that the decorator `dec` will return a new value for the
|
||||
// constructor, which would result in the binding inside of the class no longer
|
||||
// pointing to the same reference as the binding outside of the class.
|
||||
//
|
||||
// As a result, we must instead rewrite all references to the class *inside* of the
|
||||
// class body to instead point to a local temporary alias for the class:
|
||||
//
|
||||
// [Example 2]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C_1;
|
||||
// class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C_1;
|
||||
// export class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// | export { C };
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// If a class declaration is the default export of a module, we instead emit
|
||||
// the export after the decorated declaration:
|
||||
//
|
||||
// [Example 3]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let default_1 = class {
|
||||
// export default class { | }
|
||||
// } | default_1 = __decorate([dec], default_1);
|
||||
// | export default default_1;
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C = class C {
|
||||
// export default class C { | }
|
||||
// } | C = __decorate([dec], C);
|
||||
// | export default C;
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// If the class declaration is the default export and a reference to itself
|
||||
// inside of the class body, we must emit both an alias for the class *and*
|
||||
// move the export after the declaration:
|
||||
//
|
||||
// [Example 4]
|
||||
// ---------------------------------------------------------------------
|
||||
// TypeScript | Javascript
|
||||
// ---------------------------------------------------------------------
|
||||
// @dec | let C_1;
|
||||
// export default class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// | export default C;
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
|
||||
// ... = class ${name} ${heritageClauses} {
|
||||
// ${members}
|
||||
// }
|
||||
let classExpression: Expression = setOriginalNode(
|
||||
createClassExpression(
|
||||
name,
|
||||
visitNodes(node.heritageClauses, visitor, isHeritageClause),
|
||||
transformClassMembers(node, hasExtendsClause),
|
||||
/*location*/ node
|
||||
),
|
||||
node
|
||||
);
|
||||
|
||||
// Record an alias to avoid class double-binding.
|
||||
let decoratedClassAlias: Identifier;
|
||||
if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.DecoratedClassWithSelfReference) {
|
||||
enableExpressionSubstitutionForDecoratedClasses();
|
||||
decoratedClassAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default");
|
||||
decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias;
|
||||
|
||||
// We emit the class alias as a `let` declaration here so that it has the same
|
||||
// TDZ as the class.
|
||||
|
||||
// let ${decoratedClassAlias};
|
||||
addNode(statements,
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(decoratedClassAlias)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
NodeFlags.Let)
|
||||
)
|
||||
);
|
||||
|
||||
// ${decoratedClassAlias} = ${classExpression}
|
||||
classExpression = createAssignment(
|
||||
decoratedClassAlias,
|
||||
classExpression,
|
||||
/*location*/ node);
|
||||
}
|
||||
|
||||
// let ${name} = ${classExpression};
|
||||
addNode(statements,
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
name,
|
||||
classExpression
|
||||
)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
NodeFlags.Let)
|
||||
)
|
||||
);
|
||||
|
||||
return decoratedClassAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a class expression with TypeScript syntax into compatible ES6.
|
||||
*
|
||||
|
@ -638,7 +686,7 @@ namespace ts {
|
|||
const members: ClassElement[] = [];
|
||||
addNode(members, transformConstructor(node, hasExtendsClause));
|
||||
addNodes(members, visitNodes(node.members, classElementVisitor, isClassElement));
|
||||
return members;
|
||||
return createNodeArray(members, /*location*/ node.members);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2489,9 +2537,15 @@ namespace ts {
|
|||
&& (node.flags & NodeFlags.Default) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether a node is the first declaration of its kind.
|
||||
*
|
||||
* @param node A Declaration node.
|
||||
* @param kind The SyntaxKind to find among related declarations.
|
||||
*/
|
||||
function isFirstDeclarationOfKind(node: Declaration, kind: SyntaxKind) {
|
||||
const original = getOriginalNode(node);
|
||||
return !forEach(original.symbol && original.symbol.declarations, declaration => declaration.kind === kind && declaration.pos < original.pos);
|
||||
return original.symbol && getDeclarationOfKind(original.symbol, kind) === original;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2511,7 +2565,7 @@ namespace ts {
|
|||
);
|
||||
}
|
||||
|
||||
function createModuleExport(exportName: Identifier) {
|
||||
function createExternalModuleExport(exportName: Identifier) {
|
||||
return createExportDeclaration(
|
||||
createNamedExports([
|
||||
createExportSpecifier(exportName)
|
||||
|
@ -2544,12 +2598,13 @@ namespace ts {
|
|||
previousOnBeforeEmitNode(node);
|
||||
|
||||
const kind = node.kind;
|
||||
if (hasEnabledExpressionSubstitutionForDecoratedClasses
|
||||
&& kind === SyntaxKind.ClassDeclaration && node.decorators) {
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses
|
||||
&& kind === SyntaxKind.ClassDeclaration
|
||||
&& node.decorators) {
|
||||
currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)];
|
||||
}
|
||||
|
||||
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports
|
||||
&& (kind === SyntaxKind.ClassDeclaration
|
||||
|| kind === SyntaxKind.Constructor
|
||||
|| kind === SyntaxKind.MethodDeclaration
|
||||
|
@ -2563,7 +2618,7 @@ namespace ts {
|
|||
superContainerStack.push(<SuperContainer>node);
|
||||
}
|
||||
|
||||
if (hasEnabledExpressionSubstitutionForNamespaceExports
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports
|
||||
&& kind === SyntaxKind.ModuleDeclaration) {
|
||||
namespaceNestLevel++;
|
||||
}
|
||||
|
@ -2573,12 +2628,13 @@ namespace ts {
|
|||
previousOnAfterEmitNode(node);
|
||||
|
||||
const kind = node.kind;
|
||||
if (hasEnabledExpressionSubstitutionForDecoratedClasses
|
||||
&& kind === SyntaxKind.ClassDeclaration && node.decorators) {
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses
|
||||
&& kind === SyntaxKind.ClassDeclaration
|
||||
&& node.decorators) {
|
||||
currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined;
|
||||
}
|
||||
|
||||
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports
|
||||
&& (kind === SyntaxKind.ClassDeclaration
|
||||
|| kind === SyntaxKind.Constructor
|
||||
|| kind === SyntaxKind.MethodDeclaration
|
||||
|
@ -2590,7 +2646,7 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
if (hasEnabledExpressionSubstitutionForNamespaceExports
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports
|
||||
&& kind === SyntaxKind.ModuleDeclaration) {
|
||||
namespaceNestLevel--;
|
||||
}
|
||||
|
@ -2604,7 +2660,7 @@ namespace ts {
|
|||
return substituteExpressionIdentifier(<Identifier>node);
|
||||
}
|
||||
|
||||
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) {
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.CallExpression:
|
||||
return substituteCallExpression(<CallExpression>node);
|
||||
|
@ -2619,9 +2675,9 @@ namespace ts {
|
|||
}
|
||||
|
||||
function substituteExpressionIdentifier(node: Identifier): Expression {
|
||||
if (hasEnabledExpressionSubstitutionForDecoratedClasses
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses
|
||||
&& !nodeIsSynthesized(node)
|
||||
&& resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
|
||||
&& resolver.getNodeCheckFlags(node) & NodeCheckFlags.SelfReferenceInDecoratedClass) {
|
||||
// Due to the emit for class decorators, any reference to the class from inside of the class body
|
||||
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
|
||||
// behavior of class names in ES6.
|
||||
|
@ -2635,7 +2691,7 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
if (hasEnabledExpressionSubstitutionForNamespaceExports
|
||||
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports
|
||||
&& namespaceNestLevel > 0) {
|
||||
// If we are nested within a namespace declaration, we may need to qualifiy
|
||||
// an identifier that is exported from a merged namespace.
|
||||
|
@ -2702,8 +2758,8 @@ namespace ts {
|
|||
}
|
||||
|
||||
function enableExpressionSubstitutionForAsyncMethodsWithSuper() {
|
||||
if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) {
|
||||
hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true;
|
||||
if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) {
|
||||
enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
|
||||
|
||||
// We need to enable substitutions for call, property access, and element access
|
||||
// if we need to rewrite super calls.
|
||||
|
@ -2721,8 +2777,8 @@ namespace ts {
|
|||
}
|
||||
|
||||
function enableExpressionSubstitutionForDecoratedClasses() {
|
||||
if (!hasEnabledExpressionSubstitutionForDecoratedClasses) {
|
||||
hasEnabledExpressionSubstitutionForDecoratedClasses = true;
|
||||
if ((enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses) === 0) {
|
||||
enabledSubstitutions |= TypeScriptSubstitutionFlags.DecoratedClasses;
|
||||
|
||||
// We need to enable substitutions for identifiers. This allows us to
|
||||
// substitute class names inside of a class declaration.
|
||||
|
@ -2735,8 +2791,8 @@ namespace ts {
|
|||
}
|
||||
|
||||
function enableExpressionSubstitutionForNamespaceExports() {
|
||||
if (!hasEnabledExpressionSubstitutionForNamespaceExports) {
|
||||
hasEnabledExpressionSubstitutionForNamespaceExports = true;
|
||||
if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) {
|
||||
enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
|
||||
|
||||
// We need to enable substitutions for identifiers. This allows us to
|
||||
// substitute the names of exported members of a namespace.
|
||||
|
|
|
@ -2091,8 +2091,8 @@ namespace ts {
|
|||
CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function
|
||||
BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement
|
||||
HasSeenSuperCall = 0x00080000, // Set during the binding when encounter 'super'
|
||||
ClassWithBodyScopedClassBinding = 0x00100000, // Decorated class that contains a binding to itself inside of the class body.
|
||||
BodyScopedClassBinding = 0x00200000, // Binding to a decorated class inside of the class's body.
|
||||
DecoratedClassWithSelfReference = 0x00100000, // Decorated class that contains a binding to itself inside of the class body.
|
||||
SelfReferenceInDecoratedClass = 0x00200000, // Binding to a decorated class inside of the class's body.
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
|
|
@ -1352,7 +1352,7 @@ namespace ts {
|
|||
|
||||
|
||||
|
||||
export function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
|
||||
export function isNodeDescendantOf(node: Node, ancestor: Node): boolean {
|
||||
while (node) {
|
||||
if (node === ancestor) return true;
|
||||
node = node.parent;
|
||||
|
@ -2866,7 +2866,8 @@ namespace ts {
|
|||
}
|
||||
|
||||
export function isGeneratedIdentifier(node: Node): node is Identifier {
|
||||
return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.Node;
|
||||
// Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`.
|
||||
return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None;
|
||||
}
|
||||
|
||||
// Keywords
|
||||
|
|
Loading…
Reference in a new issue