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