1import { 2 addEmitHelpers, addRange, AllDecorators, append, Bundle, canHaveDecorators, chainBundle, childIsDecorated, ClassDeclaration, 3 ClassElement, ClassExpression, ClassLikeDeclaration, classOrConstructorParameterIsDecorated, ConstructorDeclaration, 4 Decorator, elideNodes, EmitFlags, EmitHint, EnumMember, Expression, filter, flatMap, GetAccessorDeclaration, 5 getAllDecoratorsOfClass, getAllDecoratorsOfClassElement, getEmitFlags, getEmitScriptTarget, getOriginalNodeId, 6 hasAccessorModifier, hasDecorators, hasSyntacticModifier, Identifier, idText, isBindingName, isBlock, isAnnotation, 7 isClassElement, isComputedPropertyName, isDecorator, isExpression, isGeneratedIdentifier, isHeritageClause, 8 isIdentifier, isModifier, isParameterDeclaration, isPrivateIdentifier, isPropertyDeclaration, isPropertyName, 9 isSimpleInlineableExpression, isStatic, map, MethodDeclaration, ModifierFlags, moveRangePastModifiers, Node, 10 NodeArray, NodeCheckFlags, NodeFlags, nodeOrChildIsDecorated, ParameterDeclaration, PropertyDeclaration, 11 ScriptTarget, SetAccessorDeclaration, setCommentRange, setEmitFlags, setOriginalNode, setSourceMapRange, 12 setTextRange, singleOrMany, some, SourceFile, Statement, SyntaxKind, TransformationContext, TransformFlags, 13 visitEachChild, visitNode, visitNodes, VisitResult, 14} from "../_namespaces/ts"; 15 16/** @internal */ 17export function transformLegacyDecorators(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { 18 const { 19 factory, 20 getEmitHelperFactory: emitHelpers, 21 hoistVariableDeclaration, 22 } = context; 23 24 const resolver = context.getEmitResolver(); 25 const compilerOptions = context.getCompilerOptions(); 26 const languageVersion = getEmitScriptTarget(compilerOptions); 27 28 // Save the previous transformation hooks. 29 const previousOnSubstituteNode = context.onSubstituteNode; 30 31 // Set new transformation hooks. 32 context.onSubstituteNode = onSubstituteNode; 33 34 /** 35 * A map that keeps track of aliases created for classes with decorators to avoid issues 36 * with the double-binding behavior of classes. 37 */ 38 let classAliases: Identifier[]; 39 40 return chainBundle(context, transformSourceFile); 41 42 function transformSourceFile(node: SourceFile) { 43 const visited = visitEachChild(node, visitor, context); 44 addEmitHelpers(visited, context.readEmitHelpers()); 45 return visited; 46 } 47 48 function modifierVisitor(node: Node): VisitResult<Node> { 49 return isDecorator(node) || isAnnotation(node) ? undefined : node; 50 } 51 52 function visitor(node: Node): VisitResult<Node> { 53 if (!(node.transformFlags & TransformFlags.ContainsDecorators)) { 54 return node; 55 } 56 57 switch (node.kind) { 58 case SyntaxKind.Decorator: 59 // Decorators are elided. They will be emitted as part of `visitClassDeclaration`. 60 return undefined; 61 case SyntaxKind.ClassDeclaration: 62 return visitClassDeclaration(node as ClassDeclaration); 63 case SyntaxKind.ClassExpression: 64 return visitClassExpression(node as ClassExpression); 65 case SyntaxKind.Constructor: 66 return visitConstructorDeclaration(node as ConstructorDeclaration); 67 case SyntaxKind.MethodDeclaration: 68 return visitMethodDeclaration(node as MethodDeclaration); 69 case SyntaxKind.SetAccessor: 70 return visitSetAccessorDeclaration(node as SetAccessorDeclaration); 71 case SyntaxKind.GetAccessor: 72 return visitGetAccessorDeclaration(node as GetAccessorDeclaration); 73 case SyntaxKind.PropertyDeclaration: 74 return visitPropertyDeclaration(node as PropertyDeclaration); 75 case SyntaxKind.Parameter: 76 return visitParameterDeclaration(node as ParameterDeclaration); 77 default: 78 return visitEachChild(node, visitor, context); 79 } 80 } 81 82 function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> { 83 if (!(classOrConstructorParameterIsDecorated(node) || childIsDecorated(node))) return visitEachChild(node, visitor, context); 84 85 const statements = hasDecorators(node) ? 86 transformClassDeclarationWithClassDecorators(node, node.name) : 87 transformClassDeclarationWithoutClassDecorators(node, node.name); 88 89 if (statements.length > 1) { 90 // Add a DeclarationMarker as a marker for the end of the declaration 91 statements.push(factory.createEndOfDeclarationMarker(node)); 92 setEmitFlags(statements[0], getEmitFlags(statements[0]) | EmitFlags.HasEndOfDeclarationMarker); 93 } 94 95 return singleOrMany(statements); 96 } 97 98 function decoratorContainsPrivateIdentifierInExpression(decorator: Decorator) { 99 return !!(decorator.transformFlags & TransformFlags.ContainsPrivateIdentifierInExpression); 100 } 101 102 function parameterDecoratorsContainPrivateIdentifierInExpression(parameterDecorators: readonly Decorator[] | undefined) { 103 return some(parameterDecorators, decoratorContainsPrivateIdentifierInExpression); 104 } 105 106 function hasClassElementWithDecoratorContainingPrivateIdentifierInExpression(node: ClassDeclaration) { 107 for (const member of node.members) { 108 if (!canHaveDecorators(member)) continue; 109 const allDecorators = getAllDecoratorsOfClassElement(member, node); 110 if (some(allDecorators?.decorators, decoratorContainsPrivateIdentifierInExpression)) return true; 111 if (some(allDecorators?.parameters, parameterDecoratorsContainPrivateIdentifierInExpression)) return true; 112 } 113 return false; 114 } 115 116 function transformDecoratorsOfClassElements(node: ClassDeclaration, members: NodeArray<ClassElement>) { 117 let decorationStatements: Statement[] | undefined = []; 118 addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ false); 119 addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ true); 120 if (hasClassElementWithDecoratorContainingPrivateIdentifierInExpression(node)) { 121 members = setTextRange(factory.createNodeArray([ 122 ...members, 123 factory.createClassStaticBlockDeclaration( 124 factory.createBlock(decorationStatements, /*multiLine*/ true) 125 ) 126 ]), members); 127 decorationStatements = undefined; 128 } 129 return { decorationStatements, members }; 130 } 131 132 /** 133 * Transforms a non-decorated class declaration. 134 * 135 * @param node A ClassDeclaration node. 136 * @param name The name of the class. 137 */ 138 function transformClassDeclarationWithoutClassDecorators(node: ClassDeclaration, name: Identifier | undefined) { 139 // ${modifiers} class ${name} ${heritageClauses} { 140 // ${members} 141 // } 142 143 const modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier); 144 const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); 145 let members = visitNodes(node.members, visitor, isClassElement); 146 147 let decorationStatements: Statement[] | undefined = []; 148 ({ members, decorationStatements } = transformDecoratorsOfClassElements(node, members)); 149 150 const updated = factory.updateClassDeclaration( 151 node, 152 modifiers, 153 name, 154 /*typeParameters*/ undefined, 155 heritageClauses, 156 members 157 ); 158 159 return addRange([updated], decorationStatements); 160 } 161 162 /** 163 * Transforms a decorated class declaration and appends the resulting statements. If 164 * the class requires an alias to avoid issues with double-binding, the alias is returned. 165 */ 166 function transformClassDeclarationWithClassDecorators(node: ClassDeclaration, name: Identifier | undefined) { 167 // When we emit an ES6 class that has a class decorator, we must tailor the 168 // emit to certain specific cases. 169 // 170 // In the simplest case, we emit the class declaration as a let declaration, and 171 // evaluate decorators after the close of the class body: 172 // 173 // [Example 1] 174 // --------------------------------------------------------------------- 175 // TypeScript | Javascript 176 // --------------------------------------------------------------------- 177 // @dec | let C = class C { 178 // class C { | } 179 // } | C = __decorate([dec], C); 180 // --------------------------------------------------------------------- 181 // @dec | let C = class C { 182 // export class C { | } 183 // } | C = __decorate([dec], C); 184 // | export { C }; 185 // --------------------------------------------------------------------- 186 // 187 // If a class declaration contains a reference to itself *inside* of the class body, 188 // this introduces two bindings to the class: One outside of the class body, and one 189 // inside of the class body. If we apply decorators as in [Example 1] above, there 190 // is the possibility that the decorator `dec` will return a new value for the 191 // constructor, which would result in the binding inside of the class no longer 192 // pointing to the same reference as the binding outside of the class. 193 // 194 // As a result, we must instead rewrite all references to the class *inside* of the 195 // class body to instead point to a local temporary alias for the class: 196 // 197 // [Example 2] 198 // --------------------------------------------------------------------- 199 // TypeScript | Javascript 200 // --------------------------------------------------------------------- 201 // @dec | let C = C_1 = class C { 202 // class C { | static x() { return C_1.y; } 203 // static x() { return C.y; } | } 204 // static y = 1; | C.y = 1; 205 // } | C = C_1 = __decorate([dec], C); 206 // | var C_1; 207 // --------------------------------------------------------------------- 208 // @dec | let C = class C { 209 // export class C { | static x() { return C_1.y; } 210 // static x() { return C.y; } | } 211 // static y = 1; | C.y = 1; 212 // } | C = C_1 = __decorate([dec], C); 213 // | export { C }; 214 // | var C_1; 215 // --------------------------------------------------------------------- 216 // 217 // If a class declaration is the default export of a module, we instead emit 218 // the export after the decorated declaration: 219 // 220 // [Example 3] 221 // --------------------------------------------------------------------- 222 // TypeScript | Javascript 223 // --------------------------------------------------------------------- 224 // @dec | let default_1 = class { 225 // export default class { | } 226 // } | default_1 = __decorate([dec], default_1); 227 // | export default default_1; 228 // --------------------------------------------------------------------- 229 // @dec | let C = class C { 230 // export default class C { | } 231 // } | C = __decorate([dec], C); 232 // | export default C; 233 // --------------------------------------------------------------------- 234 // 235 // If the class declaration is the default export and a reference to itself 236 // inside of the class body, we must emit both an alias for the class *and* 237 // move the export after the declaration: 238 // 239 // [Example 4] 240 // --------------------------------------------------------------------- 241 // TypeScript | Javascript 242 // --------------------------------------------------------------------- 243 // @dec | let C = class C { 244 // export default class C { | static x() { return C_1.y; } 245 // static x() { return C.y; } | } 246 // static y = 1; | C.y = 1; 247 // } | C = C_1 = __decorate([dec], C); 248 // | export default C; 249 // | var C_1; 250 // --------------------------------------------------------------------- 251 // 252 253 const location = moveRangePastModifiers(node); 254 const classAlias = getClassAliasIfNeeded(node); 255 256 // When we transform to ES5/3 this will be moved inside an IIFE and should reference the name 257 // without any block-scoped variable collision handling 258 const declName = languageVersion <= ScriptTarget.ES2015 ? 259 factory.getInternalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) : 260 factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 261 262 // ... = class ${name} ${heritageClauses} { 263 // ${members} 264 // } 265 const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); 266 let members = visitNodes(node.members, visitor, isClassElement); 267 268 let decorationStatements: Statement[] | undefined = []; 269 ({ members, decorationStatements } = transformDecoratorsOfClassElements(node, members)); 270 271 const classExpression = factory.createClassExpression( 272 /*modifiers*/ undefined, 273 name, 274 /*typeParameters*/ undefined, 275 heritageClauses, 276 members); 277 278 setOriginalNode(classExpression, node); 279 setTextRange(classExpression, location); 280 281 // let ${name} = ${classExpression} where name is either declaredName if the class doesn't contain self-reference 282 // or decoratedClassAlias if the class contain self-reference. 283 const statement = factory.createVariableStatement( 284 /*modifiers*/ undefined, 285 factory.createVariableDeclarationList([ 286 factory.createVariableDeclaration( 287 declName, 288 /*exclamationToken*/ undefined, 289 /*type*/ undefined, 290 classAlias ? factory.createAssignment(classAlias, classExpression) : classExpression 291 ) 292 ], NodeFlags.Let) 293 ); 294 setOriginalNode(statement, node); 295 setTextRange(statement, location); 296 setCommentRange(statement, node); 297 298 const statements: Statement[] = [statement]; 299 addRange(statements, decorationStatements); 300 addConstructorDecorationStatement(statements, node); 301 return statements; 302 } 303 304 function visitClassExpression(node: ClassExpression) { 305 // Legacy decorators were not supported on class expressions 306 return factory.updateClassExpression( 307 node, 308 visitNodes(node.modifiers, modifierVisitor, isModifier), 309 node.name, 310 /*typeParameters*/ undefined, 311 visitNodes(node.heritageClauses, visitor, isHeritageClause), 312 visitNodes(node.members, visitor, isClassElement) 313 ); 314 } 315 316 function visitConstructorDeclaration(node: ConstructorDeclaration) { 317 return factory.updateConstructorDeclaration( 318 node, 319 visitNodes(node.modifiers, modifierVisitor, isModifier), 320 visitNodes(node.parameters, visitor, isParameterDeclaration), 321 visitNode(node.body, visitor, isBlock)); 322 } 323 324 function finishClassElement(updated: ClassElement, original: ClassElement) { 325 if (updated !== original) { 326 // While we emit the source map for the node after skipping decorators and modifiers, 327 // we need to emit the comments for the original range. 328 setCommentRange(updated, original); 329 setSourceMapRange(updated, moveRangePastModifiers(original)); 330 } 331 return updated; 332 } 333 334 function visitMethodDeclaration(node: MethodDeclaration) { 335 return finishClassElement(factory.updateMethodDeclaration( 336 node, 337 visitNodes(node.modifiers, modifierVisitor, isModifier), 338 node.asteriskToken, 339 visitNode(node.name, visitor, isPropertyName), 340 /*questionToken*/ undefined, 341 /*typeParameters*/ undefined, 342 visitNodes(node.parameters, visitor, isParameterDeclaration), 343 /*type*/ undefined, 344 visitNode(node.body, visitor, isBlock) 345 ), node); 346 } 347 348 function visitGetAccessorDeclaration(node: GetAccessorDeclaration) { 349 return finishClassElement(factory.updateGetAccessorDeclaration( 350 node, 351 visitNodes(node.modifiers, modifierVisitor, isModifier), 352 visitNode(node.name, visitor, isPropertyName), 353 visitNodes(node.parameters, visitor, isParameterDeclaration), 354 /*type*/ undefined, 355 visitNode(node.body, visitor, isBlock) 356 ), node); 357 } 358 359 function visitSetAccessorDeclaration(node: SetAccessorDeclaration) { 360 return finishClassElement(factory.updateSetAccessorDeclaration( 361 node, 362 visitNodes(node.modifiers, modifierVisitor, isModifier), 363 visitNode(node.name, visitor, isPropertyName), 364 visitNodes(node.parameters, visitor, isParameterDeclaration), 365 visitNode(node.body, visitor, isBlock) 366 ), node); 367 } 368 369 function visitPropertyDeclaration(node: PropertyDeclaration) { 370 if (node.flags & NodeFlags.Ambient || hasSyntacticModifier(node, ModifierFlags.Ambient)) { 371 return undefined; 372 } 373 374 return finishClassElement(factory.updatePropertyDeclaration( 375 node, 376 visitNodes(node.modifiers, modifierVisitor, isModifier), 377 visitNode(node.name, visitor, isPropertyName), 378 /*questionOrExclamationToken*/ undefined, 379 /*type*/ undefined, 380 visitNode(node.initializer, visitor, isExpression) 381 ), node); 382 } 383 384 function visitParameterDeclaration(node: ParameterDeclaration) { 385 const updated = factory.updateParameterDeclaration( 386 node, 387 elideNodes(factory, node.modifiers), 388 node.dotDotDotToken, 389 visitNode(node.name, visitor, isBindingName), 390 /*questionToken*/ undefined, 391 /*type*/ undefined, 392 visitNode(node.initializer, visitor, isExpression) 393 ); 394 if (updated !== node) { 395 // While we emit the source map for the node after skipping decorators and modifiers, 396 // we need to emit the comments for the original range. 397 setCommentRange(updated, node); 398 setTextRange(updated, moveRangePastModifiers(node)); 399 setSourceMapRange(updated, moveRangePastModifiers(node)); 400 setEmitFlags(updated.name, EmitFlags.NoTrailingSourceMap); 401 } 402 return updated; 403 } 404 405 /** 406 * Transforms all of the decorators for a declaration into an array of expressions. 407 * 408 * @param allDecorators An object containing all of the decorators for the declaration. 409 */ 410 function transformAllDecoratorsOfDeclaration(allDecorators: AllDecorators | undefined) { 411 if (!allDecorators) { 412 return undefined; 413 } 414 415 const decoratorExpressions: Expression[] = []; 416 addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); 417 addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); 418 return decoratorExpressions; 419 } 420 421 /** 422 * Generates statements used to apply decorators to either the static or instance members 423 * of a class. 424 * 425 * @param node The class node. 426 * @param isStatic A value indicating whether to generate statements for static or 427 * instance members. 428 */ 429 function addClassElementDecorationStatements(statements: Statement[], node: ClassDeclaration, isStatic: boolean) { 430 addRange(statements, map(generateClassElementDecorationExpressions(node, isStatic), expr => factory.createExpressionStatement(expr))); 431 } 432 433 /** 434 * Determines whether a class member is either a static or an instance member of a class 435 * that is decorated, or has parameters that are decorated. 436 * 437 * @param member The class member. 438 */ 439 function isDecoratedClassElement(member: ClassElement, isStaticElement: boolean, parent: ClassLikeDeclaration) { 440 return nodeOrChildIsDecorated(member, parent) 441 && isStaticElement === isStatic(member); 442 } 443 444 /** 445 * Gets either the static or instance members of a class that are decorated, or have 446 * parameters that are decorated. 447 * 448 * @param node The class containing the member. 449 * @param isStatic A value indicating whether to retrieve static or instance members of 450 * the class. 451 */ 452 function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): readonly ClassElement[] { 453 return filter(node.members, m => isDecoratedClassElement(m, isStatic, node)); 454 } 455 456 /** 457 * Generates expressions used to apply decorators to either the static or instance members 458 * of a class. 459 * 460 * @param node The class node. 461 * @param isStatic A value indicating whether to generate expressions for static or 462 * instance members. 463 */ 464 function generateClassElementDecorationExpressions(node: ClassExpression | ClassDeclaration, isStatic: boolean) { 465 const members = getDecoratedClassElements(node, isStatic); 466 let expressions: Expression[] | undefined; 467 for (const member of members) { 468 expressions = append(expressions, generateClassElementDecorationExpression(node, member)); 469 } 470 return expressions; 471 } 472 473 /** 474 * Generates an expression used to evaluate class element decorators at runtime. 475 * 476 * @param node The class node that contains the member. 477 * @param member The class member. 478 */ 479 function generateClassElementDecorationExpression(node: ClassExpression | ClassDeclaration, member: ClassElement) { 480 const allDecorators = getAllDecoratorsOfClassElement(member, node); 481 const decoratorExpressions = transformAllDecoratorsOfDeclaration(allDecorators); 482 if (!decoratorExpressions) { 483 return undefined; 484 } 485 486 // Emit the call to __decorate. Given the following: 487 // 488 // class C { 489 // @dec method(@dec2 x) {} 490 // @dec get accessor() {} 491 // @dec prop; 492 // } 493 // 494 // The emit for a method is: 495 // 496 // __decorate([ 497 // dec, 498 // __param(0, dec2), 499 // __metadata("design:type", Function), 500 // __metadata("design:paramtypes", [Object]), 501 // __metadata("design:returntype", void 0) 502 // ], C.prototype, "method", null); 503 // 504 // The emit for an accessor is: 505 // 506 // __decorate([ 507 // dec 508 // ], C.prototype, "accessor", null); 509 // 510 // The emit for a property is: 511 // 512 // __decorate([ 513 // dec 514 // ], C.prototype, "prop"); 515 // 516 517 const prefix = getClassMemberPrefix(node, member); 518 const memberName = getExpressionForPropertyName(member, /*generateNameForComputedPropertyName*/ !hasSyntacticModifier(member, ModifierFlags.Ambient)); 519 const descriptor = languageVersion > ScriptTarget.ES3 520 ? isPropertyDeclaration(member) && !hasAccessorModifier(member) 521 // We emit `void 0` here to indicate to `__decorate` that it can invoke `Object.defineProperty` directly, but that it 522 // should not invoke `Object.getOwnPropertyDescriptor`. 523 ? factory.createVoidZero() 524 525 // We emit `null` here to indicate to `__decorate` that it can invoke `Object.getOwnPropertyDescriptor` directly. 526 // We have this extra argument here so that we can inject an explicit property descriptor at a later date. 527 : factory.createNull() 528 : undefined; 529 530 const helper = emitHelpers().createDecorateHelper( 531 decoratorExpressions, 532 prefix, 533 memberName, 534 descriptor 535 ); 536 537 setEmitFlags(helper, EmitFlags.NoComments); 538 setSourceMapRange(helper, moveRangePastModifiers(member)); 539 return helper; 540 } 541 542 /** 543 * Generates a __decorate helper call for a class constructor. 544 * 545 * @param node The class node. 546 */ 547 function addConstructorDecorationStatement(statements: Statement[], node: ClassDeclaration) { 548 const expression = generateConstructorDecorationExpression(node); 549 if (expression) { 550 statements.push(setOriginalNode(factory.createExpressionStatement(expression), node)); 551 } 552 } 553 554 /** 555 * Generates a __decorate helper call for a class constructor. 556 * 557 * @param node The class node. 558 */ 559 function generateConstructorDecorationExpression(node: ClassExpression | ClassDeclaration) { 560 const allDecorators = getAllDecoratorsOfClass(node); 561 const decoratorExpressions = transformAllDecoratorsOfDeclaration(allDecorators); 562 if (!decoratorExpressions) { 563 return undefined; 564 } 565 566 const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; 567 568 // When we transform to ES5/3 this will be moved inside an IIFE and should reference the name 569 // without any block-scoped variable collision handling 570 const localName = languageVersion <= ScriptTarget.ES2015 ? 571 factory.getInternalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) : 572 factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 573 const decorate = emitHelpers().createDecorateHelper(decoratorExpressions, localName); 574 const expression = factory.createAssignment(localName, classAlias ? factory.createAssignment(classAlias, decorate) : decorate); 575 setEmitFlags(expression, EmitFlags.NoComments); 576 setSourceMapRange(expression, moveRangePastModifiers(node)); 577 return expression; 578 } 579 580 /** 581 * Transforms a decorator into an expression. 582 * 583 * @param decorator The decorator node. 584 */ 585 function transformDecorator(decorator: Decorator) { 586 return visitNode(decorator.expression, visitor, isExpression); 587 } 588 589 /** 590 * Transforms the decorators of a parameter. 591 * 592 * @param decorators The decorators for the parameter at the provided offset. 593 * @param parameterOffset The offset of the parameter. 594 */ 595 function transformDecoratorsOfParameter(decorators: Decorator[], parameterOffset: number) { 596 let expressions: Expression[] | undefined; 597 if (decorators) { 598 expressions = []; 599 for (const decorator of decorators) { 600 const helper = emitHelpers().createParamHelper( 601 transformDecorator(decorator), 602 parameterOffset); 603 setTextRange(helper, decorator.expression); 604 setEmitFlags(helper, EmitFlags.NoComments); 605 expressions.push(helper); 606 } 607 } 608 609 return expressions; 610 } 611 612 /** 613 * Gets an expression that represents a property name (for decorated properties or enums). 614 * For a computed property, a name is generated for the node. 615 * 616 * @param member The member whose name should be converted into an expression. 617 */ 618 function getExpressionForPropertyName(member: ClassElement | EnumMember, generateNameForComputedPropertyName: boolean): Expression { 619 const name = member.name!; 620 if (isPrivateIdentifier(name)) { 621 return factory.createIdentifier(""); 622 } 623 else if (isComputedPropertyName(name)) { 624 return generateNameForComputedPropertyName && !isSimpleInlineableExpression(name.expression) 625 ? factory.getGeneratedNameForNode(name) 626 : name.expression; 627 } 628 else if (isIdentifier(name)) { 629 return factory.createStringLiteral(idText(name)); 630 } 631 else { 632 return factory.cloneNode(name); 633 } 634 } 635 636 function enableSubstitutionForClassAliases() { 637 if (!classAliases) { 638 // We need to enable substitutions for identifiers. This allows us to 639 // substitute class names inside of a class declaration. 640 context.enableSubstitution(SyntaxKind.Identifier); 641 642 // Keep track of class aliases. 643 classAliases = []; 644 } 645 } 646 647 /** 648 * Gets a local alias for a class declaration if it is a decorated class with an internal 649 * reference to the static side of the class. This is necessary to avoid issues with 650 * double-binding semantics for the class name. 651 */ 652 function getClassAliasIfNeeded(node: ClassDeclaration) { 653 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { 654 enableSubstitutionForClassAliases(); 655 const classAlias = factory.createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? idText(node.name) : "default"); 656 classAliases[getOriginalNodeId(node)] = classAlias; 657 hoistVariableDeclaration(classAlias); 658 return classAlias; 659 } 660 } 661 662 function getClassPrototype(node: ClassExpression | ClassDeclaration) { 663 return factory.createPropertyAccessExpression(factory.getDeclarationName(node), "prototype"); 664 } 665 666 function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { 667 return isStatic(member) 668 ? factory.getDeclarationName(node) 669 : getClassPrototype(node); 670 } 671 672 /** 673 * Hooks node substitutions. 674 * 675 * @param hint A hint as to the intended usage of the node. 676 * @param node The node to substitute. 677 */ 678 function onSubstituteNode(hint: EmitHint, node: Node) { 679 node = previousOnSubstituteNode(hint, node); 680 if (hint === EmitHint.Expression) { 681 return substituteExpression(node as Expression); 682 } 683 return node; 684 } 685 686 function substituteExpression(node: Expression) { 687 switch (node.kind) { 688 case SyntaxKind.Identifier: 689 return substituteExpressionIdentifier(node as Identifier); 690 } 691 692 return node; 693 } 694 695 function substituteExpressionIdentifier(node: Identifier): Expression { 696 return trySubstituteClassAlias(node) 697 ?? node; 698 } 699 700 function trySubstituteClassAlias(node: Identifier): Expression | undefined { 701 if (classAliases) { 702 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ConstructorReferenceInClass) { 703 // Due to the emit for class decorators, any reference to the class from inside of the class body 704 // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind 705 // behavior of class names in ES6. 706 // Also, when emitting statics for class expressions, we must substitute a class alias for 707 // constructor references in static property initializers. 708 const declaration = resolver.getReferencedValueDeclaration(node); 709 if (declaration) { 710 const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 711 if (classAlias) { 712 const clone = factory.cloneNode(classAlias); 713 setSourceMapRange(clone, node); 714 setCommentRange(clone, node); 715 return clone; 716 } 717 } 718 } 719 } 720 721 return undefined; 722 } 723}