1/*@internal*/ 2namespace ts { 3 /** 4 * Indicates whether to emit type metadata in the new format. 5 */ 6 const USE_NEW_TYPE_METADATA_FORMAT = false; 7 8 const enum TypeScriptSubstitutionFlags { 9 /** Enables substitutions for decorated classes. */ 10 ClassAliases = 1 << 0, 11 /** Enables substitutions for namespace exports. */ 12 NamespaceExports = 1 << 1, 13 /* Enables substitutions for unqualified enum members */ 14 NonQualifiedEnumMembers = 1 << 3 15 } 16 17 const enum ClassFacts { 18 None = 0, 19 HasStaticInitializedProperties = 1 << 0, 20 HasConstructorDecorators = 1 << 1, 21 HasMemberDecorators = 1 << 2, 22 IsExportOfNamespace = 1 << 3, 23 IsNamedExternalExport = 1 << 4, 24 IsDefaultExternalExport = 1 << 5, 25 IsDerivedClass = 1 << 6, 26 UseImmediatelyInvokedFunctionExpression = 1 << 7, 27 28 HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators, 29 NeedsName = HasStaticInitializedProperties | HasMemberDecorators, 30 MayNeedImmediatelyInvokedFunctionExpression = HasAnyDecorators | HasStaticInitializedProperties, 31 IsExported = IsExportOfNamespace | IsDefaultExternalExport | IsNamedExternalExport, 32 } 33 34 export function transformTypeScript(context: TransformationContext) { 35 const { 36 factory, 37 getEmitHelperFactory: emitHelpers, 38 startLexicalEnvironment, 39 resumeLexicalEnvironment, 40 endLexicalEnvironment, 41 hoistVariableDeclaration, 42 } = context; 43 44 const resolver = context.getEmitResolver(); 45 const compilerOptions = context.getCompilerOptions(); 46 const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks"); 47 const languageVersion = getEmitScriptTarget(compilerOptions); 48 const moduleKind = getEmitModuleKind(compilerOptions); 49 50 // Save the previous transformation hooks. 51 const previousOnEmitNode = context.onEmitNode; 52 const previousOnSubstituteNode = context.onSubstituteNode; 53 54 // Set new transformation hooks. 55 context.onEmitNode = onEmitNode; 56 context.onSubstituteNode = onSubstituteNode; 57 58 // Enable substitution for property/element access to emit const enum values. 59 context.enableSubstitution(SyntaxKind.PropertyAccessExpression); 60 context.enableSubstitution(SyntaxKind.ElementAccessExpression); 61 62 // These variables contain state that changes as we descend into the tree. 63 let currentSourceFile: SourceFile; 64 let currentNamespace: ModuleDeclaration; 65 let currentNamespaceContainerName: Identifier; 66 let currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock; 67 let currentNameScope: ClassDeclaration | undefined; 68 let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap<Node> | undefined; 69 let currentClassHasParameterProperties: boolean | undefined; 70 71 /** 72 * Keeps track of whether expression substitution has been enabled for specific edge cases. 73 * They are persisted between each SourceFile transformation and should not be reset. 74 */ 75 let enabledSubstitutions: TypeScriptSubstitutionFlags; 76 77 /** 78 * A map that keeps track of aliases created for classes with decorators to avoid issues 79 * with the double-binding behavior of classes. 80 */ 81 let classAliases: Identifier[]; 82 83 /** 84 * Keeps track of whether we are within any containing namespaces when performing 85 * just-in-time substitution while printing an expression identifier. 86 */ 87 let applicableSubstitutions: TypeScriptSubstitutionFlags; 88 89 return transformSourceFileOrBundle; 90 91 function transformSourceFileOrBundle(node: SourceFile | Bundle) { 92 if (node.kind === SyntaxKind.Bundle) { 93 return transformBundle(node); 94 } 95 return transformSourceFile(node); 96 } 97 98 function transformBundle(node: Bundle) { 99 return factory.createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => { 100 if (prepend.kind === SyntaxKind.InputFiles) { 101 return createUnparsedSourceFile(prepend, "js"); 102 } 103 return prepend; 104 })); 105 } 106 107 /** 108 * Transform TypeScript-specific syntax in a SourceFile. 109 * 110 * @param node A SourceFile node. 111 */ 112 function transformSourceFile(node: SourceFile) { 113 if (node.isDeclarationFile) { 114 return node; 115 } 116 117 currentSourceFile = node; 118 119 const visited = saveStateAndInvoke(node, visitSourceFile); 120 addEmitHelpers(visited, context.readEmitHelpers()); 121 122 currentSourceFile = undefined!; 123 return visited; 124 } 125 126 /** 127 * Visits a node, saving and restoring state variables on the stack. 128 * 129 * @param node The node to visit. 130 */ 131 function saveStateAndInvoke<T>(node: Node, f: (node: Node) => T): T { 132 // Save state 133 const savedCurrentScope = currentLexicalScope; 134 const savedCurrentNameScope = currentNameScope; 135 const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; 136 const savedCurrentClassHasParameterProperties = currentClassHasParameterProperties; 137 138 // Handle state changes before visiting a node. 139 onBeforeVisitNode(node); 140 141 const visited = f(node); 142 143 // Restore state 144 if (currentLexicalScope !== savedCurrentScope) { 145 currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; 146 } 147 148 currentLexicalScope = savedCurrentScope; 149 currentNameScope = savedCurrentNameScope; 150 currentClassHasParameterProperties = savedCurrentClassHasParameterProperties; 151 return visited; 152 } 153 154 /** 155 * Performs actions that should always occur immediately before visiting a node. 156 * 157 * @param node The node to visit. 158 */ 159 function onBeforeVisitNode(node: Node) { 160 switch (node.kind) { 161 case SyntaxKind.SourceFile: 162 case SyntaxKind.CaseBlock: 163 case SyntaxKind.ModuleBlock: 164 case SyntaxKind.Block: 165 currentLexicalScope = <SourceFile | CaseBlock | ModuleBlock | Block>node; 166 currentNameScope = undefined; 167 currentScopeFirstDeclarationsOfName = undefined; 168 break; 169 170 case SyntaxKind.ClassDeclaration: 171 case SyntaxKind.FunctionDeclaration: 172 if (hasSyntacticModifier(node, ModifierFlags.Ambient)) { 173 break; 174 } 175 176 // Record these declarations provided that they have a name. 177 if ((node as ClassDeclaration | FunctionDeclaration).name) { 178 recordEmittedDeclarationInScope(node as ClassDeclaration | FunctionDeclaration); 179 } 180 else { 181 // These nodes should always have names unless they are default-exports; 182 // however, class declaration parsing allows for undefined names, so syntactically invalid 183 // programs may also have an undefined name. 184 Debug.assert(node.kind === SyntaxKind.ClassDeclaration || hasSyntacticModifier(node, ModifierFlags.Default)); 185 } 186 if (isClassDeclaration(node)) { 187 // XXX: should probably also cover interfaces and type aliases that can have type variables? 188 currentNameScope = node; 189 } 190 191 break; 192 } 193 } 194 195 /** 196 * General-purpose node visitor. 197 * 198 * @param node The node to visit. 199 */ 200 function visitor(node: Node): VisitResult<Node> { 201 return saveStateAndInvoke(node, visitorWorker); 202 } 203 204 /** 205 * Visits and possibly transforms any node. 206 * 207 * @param node The node to visit. 208 */ 209 function visitorWorker(node: Node): VisitResult<Node> { 210 if (node.transformFlags & TransformFlags.ContainsTypeScript) { 211 return visitTypeScript(node); 212 } 213 return node; 214 } 215 216 /** 217 * Specialized visitor that visits the immediate children of a SourceFile. 218 * 219 * @param node The node to visit. 220 */ 221 function sourceElementVisitor(node: Node): VisitResult<Node> { 222 return saveStateAndInvoke(node, sourceElementVisitorWorker); 223 } 224 225 /** 226 * Specialized visitor that visits the immediate children of a SourceFile. 227 * 228 * @param node The node to visit. 229 */ 230 function sourceElementVisitorWorker(node: Node): VisitResult<Node> { 231 switch (node.kind) { 232 case SyntaxKind.ImportDeclaration: 233 case SyntaxKind.ImportEqualsDeclaration: 234 case SyntaxKind.ExportAssignment: 235 case SyntaxKind.ExportDeclaration: 236 return visitElidableStatement(<ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration>node); 237 default: 238 return visitorWorker(node); 239 } 240 } 241 242 function visitElidableStatement(node: ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration): VisitResult<Node> { 243 const parsed = getParseTreeNode(node); 244 if (parsed !== node) { 245 // If the node has been transformed by a `before` transformer, perform no ellision on it 246 // As the type information we would attempt to lookup to perform ellision is potentially unavailable for the synthesized nodes 247 // We do not reuse `visitorWorker`, as the ellidable statement syntax kinds are technically unrecognized by the switch-case in `visitTypeScript`, 248 // and will trigger debug failures when debug verbosity is turned up 249 if (node.transformFlags & TransformFlags.ContainsTypeScript) { 250 // This node contains TypeScript, so we should visit its children. 251 return visitEachChild(node, visitor, context); 252 } 253 // Otherwise, we can just return the node 254 return node; 255 } 256 switch (node.kind) { 257 case SyntaxKind.ImportDeclaration: 258 return visitImportDeclaration(node); 259 case SyntaxKind.ImportEqualsDeclaration: 260 return visitImportEqualsDeclaration(node); 261 case SyntaxKind.ExportAssignment: 262 return visitExportAssignment(node); 263 case SyntaxKind.ExportDeclaration: 264 return visitExportDeclaration(node); 265 default: 266 Debug.fail("Unhandled ellided statement"); 267 } 268 } 269 270 /** 271 * Specialized visitor that visits the immediate children of a namespace. 272 * 273 * @param node The node to visit. 274 */ 275 function namespaceElementVisitor(node: Node): VisitResult<Node> { 276 return saveStateAndInvoke(node, namespaceElementVisitorWorker); 277 } 278 279 /** 280 * Specialized visitor that visits the immediate children of a namespace. 281 * 282 * @param node The node to visit. 283 */ 284 function namespaceElementVisitorWorker(node: Node): VisitResult<Node> { 285 if (node.kind === SyntaxKind.ExportDeclaration || 286 node.kind === SyntaxKind.ImportDeclaration || 287 node.kind === SyntaxKind.ImportClause || 288 (node.kind === SyntaxKind.ImportEqualsDeclaration && 289 (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference)) { 290 // do not emit ES6 imports and exports since they are illegal inside a namespace 291 return undefined; 292 } 293 else if (node.transformFlags & TransformFlags.ContainsTypeScript || hasSyntacticModifier(node, ModifierFlags.Export)) { 294 return visitTypeScript(node); 295 } 296 297 return node; 298 } 299 300 /** 301 * Specialized visitor that visits the immediate children of a class with TypeScript syntax. 302 * 303 * @param node The node to visit. 304 */ 305 function classElementVisitor(node: Node): VisitResult<Node> { 306 return saveStateAndInvoke(node, classElementVisitorWorker); 307 } 308 309 /** 310 * Specialized visitor that visits the immediate children of a class with TypeScript syntax. 311 * 312 * @param node The node to visit. 313 */ 314 function classElementVisitorWorker(node: Node): VisitResult<Node> { 315 switch (node.kind) { 316 case SyntaxKind.Constructor: 317 return visitConstructor(node as ConstructorDeclaration); 318 319 case SyntaxKind.PropertyDeclaration: 320 // Property declarations are not TypeScript syntax, but they must be visited 321 // for the decorator transformation. 322 return visitPropertyDeclaration(node as PropertyDeclaration); 323 case SyntaxKind.IndexSignature: 324 case SyntaxKind.GetAccessor: 325 case SyntaxKind.SetAccessor: 326 case SyntaxKind.MethodDeclaration: 327 // Fallback to the default visit behavior. 328 return visitorWorker(node); 329 330 case SyntaxKind.SemicolonClassElement: 331 return node; 332 333 default: 334 return Debug.failBadSyntaxKind(node); 335 } 336 } 337 338 function modifierVisitor(node: Node): VisitResult<Node> { 339 if (modifierToFlag(node.kind) & ModifierFlags.TypeScriptModifier) { 340 return undefined; 341 } 342 else if (currentNamespace && node.kind === SyntaxKind.ExportKeyword) { 343 return undefined; 344 } 345 346 return node; 347 } 348 349 /** 350 * Branching visitor, visits a TypeScript syntax node. 351 * 352 * @param node The node to visit. 353 */ 354 function visitTypeScript(node: Node): VisitResult<Node> { 355 if (isStatement(node) && hasSyntacticModifier(node, ModifierFlags.Ambient)) { 356 // TypeScript ambient declarations are elided, but some comments may be preserved. 357 // See the implementation of `getLeadingComments` in comments.ts for more details. 358 return factory.createNotEmittedStatement(node); 359 } 360 361 switch (node.kind) { 362 case SyntaxKind.ExportKeyword: 363 case SyntaxKind.DefaultKeyword: 364 // ES6 export and default modifiers are elided when inside a namespace. 365 return currentNamespace ? undefined : node; 366 367 case SyntaxKind.PublicKeyword: 368 case SyntaxKind.PrivateKeyword: 369 case SyntaxKind.ProtectedKeyword: 370 case SyntaxKind.AbstractKeyword: 371 case SyntaxKind.ConstKeyword: 372 case SyntaxKind.DeclareKeyword: 373 case SyntaxKind.ReadonlyKeyword: 374 // TypeScript accessibility and readonly modifiers are elided 375 // falls through 376 case SyntaxKind.ArrayType: 377 case SyntaxKind.TupleType: 378 case SyntaxKind.OptionalType: 379 case SyntaxKind.RestType: 380 case SyntaxKind.TypeLiteral: 381 case SyntaxKind.TypePredicate: 382 case SyntaxKind.TypeParameter: 383 case SyntaxKind.AnyKeyword: 384 case SyntaxKind.UnknownKeyword: 385 case SyntaxKind.BooleanKeyword: 386 case SyntaxKind.StringKeyword: 387 case SyntaxKind.NumberKeyword: 388 case SyntaxKind.NeverKeyword: 389 case SyntaxKind.VoidKeyword: 390 case SyntaxKind.SymbolKeyword: 391 case SyntaxKind.ConstructorType: 392 case SyntaxKind.FunctionType: 393 case SyntaxKind.TypeQuery: 394 case SyntaxKind.TypeReference: 395 case SyntaxKind.UnionType: 396 case SyntaxKind.IntersectionType: 397 case SyntaxKind.ConditionalType: 398 case SyntaxKind.ParenthesizedType: 399 case SyntaxKind.ThisType: 400 case SyntaxKind.TypeOperator: 401 case SyntaxKind.IndexedAccessType: 402 case SyntaxKind.MappedType: 403 case SyntaxKind.LiteralType: 404 // TypeScript type nodes are elided. 405 // falls through 406 407 case SyntaxKind.IndexSignature: 408 // TypeScript index signatures are elided. 409 // falls through 410 411 case SyntaxKind.Decorator: 412 // TypeScript decorators are elided. They will be emitted as part of visitClassDeclaration. 413 // falls through 414 415 case SyntaxKind.TypeAliasDeclaration: 416 // TypeScript type-only declarations are elided. 417 return undefined; 418 419 case SyntaxKind.PropertyDeclaration: 420 // TypeScript property declarations are elided. However their names are still visited, and can potentially be retained if they could have sideeffects 421 return visitPropertyDeclaration(node as PropertyDeclaration); 422 423 case SyntaxKind.NamespaceExportDeclaration: 424 // TypeScript namespace export declarations are elided. 425 return undefined; 426 427 case SyntaxKind.Constructor: 428 return visitConstructor(<ConstructorDeclaration>node); 429 430 case SyntaxKind.InterfaceDeclaration: 431 // TypeScript interfaces are elided, but some comments may be preserved. 432 // See the implementation of `getLeadingComments` in comments.ts for more details. 433 return factory.createNotEmittedStatement(node); 434 435 case SyntaxKind.ClassDeclaration: 436 // This may be a class declaration with TypeScript syntax extensions. 437 // 438 // TypeScript class syntax extensions include: 439 // - decorators 440 // - optional `implements` heritage clause 441 // - parameter property assignments in the constructor 442 // - index signatures 443 // - method overload signatures 444 return visitClassDeclaration(<ClassDeclaration>node); 445 446 case SyntaxKind.ClassExpression: 447 // This may be a class expression with TypeScript syntax extensions. 448 // 449 // TypeScript class syntax extensions include: 450 // - decorators 451 // - optional `implements` heritage clause 452 // - parameter property assignments in the constructor 453 // - index signatures 454 // - method overload signatures 455 return visitClassExpression(<ClassExpression>node); 456 457 case SyntaxKind.HeritageClause: 458 // This may be a heritage clause with TypeScript syntax extensions. 459 // 460 // TypeScript heritage clause extensions include: 461 // - `implements` clause 462 return visitHeritageClause(<HeritageClause>node); 463 464 case SyntaxKind.ExpressionWithTypeArguments: 465 // TypeScript supports type arguments on an expression in an `extends` heritage clause. 466 return visitExpressionWithTypeArguments(<ExpressionWithTypeArguments>node); 467 468 case SyntaxKind.MethodDeclaration: 469 // TypeScript method declarations may have decorators, modifiers 470 // or type annotations. 471 return visitMethodDeclaration(<MethodDeclaration>node); 472 473 case SyntaxKind.GetAccessor: 474 // Get Accessors can have TypeScript modifiers, decorators, and type annotations. 475 return visitGetAccessor(<GetAccessorDeclaration>node); 476 477 case SyntaxKind.SetAccessor: 478 // Set Accessors can have TypeScript modifiers and type annotations. 479 return visitSetAccessor(<SetAccessorDeclaration>node); 480 481 case SyntaxKind.FunctionDeclaration: 482 // Typescript function declarations can have modifiers, decorators, and type annotations. 483 return visitFunctionDeclaration(<FunctionDeclaration>node); 484 485 case SyntaxKind.FunctionExpression: 486 // TypeScript function expressions can have modifiers and type annotations. 487 return visitFunctionExpression(<FunctionExpression>node); 488 489 case SyntaxKind.ArrowFunction: 490 // TypeScript arrow functions can have modifiers and type annotations. 491 return visitArrowFunction(<ArrowFunction>node); 492 493 case SyntaxKind.Parameter: 494 // This may be a parameter declaration with TypeScript syntax extensions. 495 // 496 // TypeScript parameter declaration syntax extensions include: 497 // - decorators 498 // - accessibility modifiers 499 // - the question mark (?) token for optional parameters 500 // - type annotations 501 // - this parameters 502 return visitParameter(<ParameterDeclaration>node); 503 504 case SyntaxKind.ParenthesizedExpression: 505 // ParenthesizedExpressions are TypeScript if their expression is a 506 // TypeAssertion or AsExpression 507 return visitParenthesizedExpression(<ParenthesizedExpression>node); 508 509 case SyntaxKind.TypeAssertionExpression: 510 case SyntaxKind.AsExpression: 511 // TypeScript type assertions are removed, but their subtrees are preserved. 512 return visitAssertionExpression(<AssertionExpression>node); 513 514 case SyntaxKind.CallExpression: 515 return visitCallExpression(<CallExpression>node); 516 517 case SyntaxKind.NewExpression: 518 return visitNewExpression(<NewExpression>node); 519 520 case SyntaxKind.TaggedTemplateExpression: 521 return visitTaggedTemplateExpression(<TaggedTemplateExpression>node); 522 523 case SyntaxKind.NonNullExpression: 524 // TypeScript non-null expressions are removed, but their subtrees are preserved. 525 return visitNonNullExpression(<NonNullExpression>node); 526 527 case SyntaxKind.EnumDeclaration: 528 // TypeScript enum declarations do not exist in ES6 and must be rewritten. 529 return visitEnumDeclaration(<EnumDeclaration>node); 530 531 case SyntaxKind.VariableStatement: 532 // TypeScript namespace exports for variable statements must be transformed. 533 return visitVariableStatement(<VariableStatement>node); 534 535 case SyntaxKind.VariableDeclaration: 536 return visitVariableDeclaration(<VariableDeclaration>node); 537 538 case SyntaxKind.ModuleDeclaration: 539 // TypeScript namespace declarations must be transformed. 540 return visitModuleDeclaration(<ModuleDeclaration>node); 541 542 case SyntaxKind.ImportEqualsDeclaration: 543 // TypeScript namespace or external module import. 544 return visitImportEqualsDeclaration(<ImportEqualsDeclaration>node); 545 546 case SyntaxKind.JsxSelfClosingElement: 547 return visitJsxSelfClosingElement(<JsxSelfClosingElement>node); 548 549 case SyntaxKind.JsxOpeningElement: 550 return visitJsxJsxOpeningElement(<JsxOpeningElement>node); 551 552 default: 553 // node contains some other TypeScript syntax 554 return visitEachChild(node, visitor, context); 555 } 556 } 557 558 function visitSourceFile(node: SourceFile) { 559 const alwaysStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") && 560 !(isExternalModule(node) && moduleKind >= ModuleKind.ES2015) && 561 !isJsonSourceFile(node); 562 563 return factory.updateSourceFile( 564 node, 565 visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, alwaysStrict)); 566 } 567 568 /** 569 * Tests whether we should emit a __decorate call for a class declaration. 570 */ 571 function shouldEmitDecorateCallForClass(node: ClassDeclaration) { 572 if (node.decorators && node.decorators.length > 0) { 573 return true; 574 } 575 576 const constructor = getFirstConstructorWithBody(node); 577 if (constructor) { 578 return forEach(constructor.parameters, shouldEmitDecorateCallForParameter); 579 } 580 581 return false; 582 } 583 584 /** 585 * Tests whether we should emit a __decorate call for a parameter declaration. 586 */ 587 function shouldEmitDecorateCallForParameter(parameter: ParameterDeclaration) { 588 return parameter.decorators !== undefined && parameter.decorators.length > 0; 589 } 590 591 function getClassFacts(node: ClassDeclaration, staticProperties: readonly PropertyDeclaration[]) { 592 let facts = ClassFacts.None; 593 if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties; 594 const extendsClauseElement = getEffectiveBaseTypeNode(node); 595 if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass; 596 if (shouldEmitDecorateCallForClass(node)) facts |= ClassFacts.HasConstructorDecorators; 597 if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators; 598 if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace; 599 else if (isDefaultExternalModuleExport(node)) facts |= ClassFacts.IsDefaultExternalExport; 600 else if (isNamedExternalModuleExport(node)) facts |= ClassFacts.IsNamedExternalExport; 601 if (languageVersion <= ScriptTarget.ES5 && (facts & ClassFacts.MayNeedImmediatelyInvokedFunctionExpression)) facts |= ClassFacts.UseImmediatelyInvokedFunctionExpression; 602 return facts; 603 } 604 605 function hasTypeScriptClassSyntax(node: Node) { 606 return !!(node.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax); 607 } 608 609 function isClassLikeDeclarationWithTypeScriptSyntax(node: ClassLikeDeclaration) { 610 return some(node.decorators) 611 || some(node.typeParameters) 612 || some(node.heritageClauses, hasTypeScriptClassSyntax) 613 || some(node.members, hasTypeScriptClassSyntax); 614 } 615 616 function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> { 617 if (!isClassLikeDeclarationWithTypeScriptSyntax(node) && !(currentNamespace && hasSyntacticModifier(node, ModifierFlags.Export))) { 618 return visitEachChild(node, visitor, context); 619 } 620 621 const staticProperties = getProperties(node, /*requireInitializer*/ true, /*isStatic*/ true); 622 const facts = getClassFacts(node, staticProperties); 623 624 if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) { 625 context.startLexicalEnvironment(); 626 } 627 628 const name = node.name || (facts & ClassFacts.NeedsName ? factory.getGeneratedNameForNode(node) : undefined); 629 const classStatement = facts & ClassFacts.HasConstructorDecorators 630 ? createClassDeclarationHeadWithDecorators(node, name) 631 : createClassDeclarationHeadWithoutDecorators(node, name, facts); 632 633 let statements: Statement[] = [classStatement]; 634 635 636 // Write any decorators of the node. 637 addClassElementDecorationStatements(statements, node, /*isStatic*/ false); 638 addClassElementDecorationStatements(statements, node, /*isStatic*/ true); 639 addConstructorDecorationStatement(statements, node); 640 641 if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) { 642 // When we emit a TypeScript class down to ES5, we must wrap it in an IIFE so that the 643 // 'es2015' transformer can properly nest static initializers and decorators. The result 644 // looks something like: 645 // 646 // var C = function () { 647 // class C { 648 // } 649 // C.static_prop = 1; 650 // return C; 651 // }(); 652 // 653 const closingBraceLocation = createTokenRange(skipTrivia(currentSourceFile.text, node.members.end), SyntaxKind.CloseBraceToken); 654 const localName = factory.getInternalName(node); 655 656 // The following partially-emitted expression exists purely to align our sourcemap 657 // emit with the original emitter. 658 const outer = factory.createPartiallyEmittedExpression(localName); 659 setTextRangeEnd(outer, closingBraceLocation.end); 660 setEmitFlags(outer, EmitFlags.NoComments); 661 662 const statement = factory.createReturnStatement(outer); 663 setTextRangePos(statement, closingBraceLocation.pos); 664 setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps); 665 statements.push(statement); 666 667 insertStatementsAfterStandardPrologue(statements, context.endLexicalEnvironment()); 668 669 const iife = factory.createImmediatelyInvokedArrowFunction(statements); 670 setEmitFlags(iife, EmitFlags.TypeScriptClassWrapper); 671 672 const varStatement = factory.createVariableStatement( 673 /*modifiers*/ undefined, 674 factory.createVariableDeclarationList([ 675 factory.createVariableDeclaration( 676 factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ false), 677 /*exclamationToken*/ undefined, 678 /*type*/ undefined, 679 iife 680 ) 681 ]) 682 ); 683 684 setOriginalNode(varStatement, node); 685 setCommentRange(varStatement, node); 686 setSourceMapRange(varStatement, moveRangePastDecorators(node)); 687 startOnNewLine(varStatement); 688 statements = [varStatement]; 689 } 690 691 // If the class is exported as part of a TypeScript namespace, emit the namespace export. 692 // Otherwise, if the class was exported at the top level and was decorated, emit an export 693 // declaration or export default for the class. 694 if (facts & ClassFacts.IsExportOfNamespace) { 695 addExportMemberAssignment(statements, node); 696 } 697 else if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression || facts & ClassFacts.HasConstructorDecorators) { 698 if (facts & ClassFacts.IsDefaultExternalExport) { 699 statements.push(factory.createExportDefault(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true))); 700 } 701 else if (facts & ClassFacts.IsNamedExternalExport) { 702 statements.push(factory.createExternalModuleExport(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true))); 703 } 704 } 705 706 if (statements.length > 1) { 707 // Add a DeclarationMarker as a marker for the end of the declaration 708 statements.push(factory.createEndOfDeclarationMarker(node)); 709 setEmitFlags(classStatement, getEmitFlags(classStatement) | EmitFlags.HasEndOfDeclarationMarker); 710 } 711 712 return singleOrMany(statements); 713 } 714 715 /** 716 * Transforms a non-decorated class declaration and appends the resulting statements. 717 * 718 * @param node A ClassDeclaration node. 719 * @param name The name of the class. 720 * @param facts Precomputed facts about the class. 721 */ 722 function createClassDeclarationHeadWithoutDecorators(node: ClassDeclaration, name: Identifier | undefined, facts: ClassFacts) { 723 // ${modifiers} class ${name} ${heritageClauses} { 724 // ${members} 725 // } 726 727 // we do not emit modifiers on the declaration if we are emitting an IIFE 728 const modifiers = !(facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) 729 ? visitNodes(node.modifiers, modifierVisitor, isModifier) 730 : undefined; 731 732 const classDeclaration = factory.createClassDeclaration( 733 /*decorators*/ undefined, 734 modifiers, 735 name, 736 /*typeParameters*/ undefined, 737 visitNodes(node.heritageClauses, visitor, isHeritageClause), 738 transformClassMembers(node) 739 ); 740 741 // To better align with the old emitter, we should not emit a trailing source map 742 // entry if the class has static properties. 743 let emitFlags = getEmitFlags(node); 744 if (facts & ClassFacts.HasStaticInitializedProperties) { 745 emitFlags |= EmitFlags.NoTrailingSourceMap; 746 } 747 748 setTextRange(classDeclaration, node); 749 setOriginalNode(classDeclaration, node); 750 setEmitFlags(classDeclaration, emitFlags); 751 return classDeclaration; 752 } 753 754 /** 755 * Transforms a decorated class declaration and appends the resulting statements. If 756 * the class requires an alias to avoid issues with double-binding, the alias is returned. 757 */ 758 function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined) { 759 // When we emit an ES6 class that has a class decorator, we must tailor the 760 // emit to certain specific cases. 761 // 762 // In the simplest case, we emit the class declaration as a let declaration, and 763 // evaluate decorators after the close of the class body: 764 // 765 // [Example 1] 766 // --------------------------------------------------------------------- 767 // TypeScript | Javascript 768 // --------------------------------------------------------------------- 769 // @dec | let C = class C { 770 // class C { | } 771 // } | C = __decorate([dec], C); 772 // --------------------------------------------------------------------- 773 // @dec | let C = class C { 774 // export class C { | } 775 // } | C = __decorate([dec], C); 776 // | export { C }; 777 // --------------------------------------------------------------------- 778 // 779 // If a class declaration contains a reference to itself *inside* of the class body, 780 // this introduces two bindings to the class: One outside of the class body, and one 781 // inside of the class body. If we apply decorators as in [Example 1] above, there 782 // is the possibility that the decorator `dec` will return a new value for the 783 // constructor, which would result in the binding inside of the class no longer 784 // pointing to the same reference as the binding outside of the class. 785 // 786 // As a result, we must instead rewrite all references to the class *inside* of the 787 // class body to instead point to a local temporary alias for the class: 788 // 789 // [Example 2] 790 // --------------------------------------------------------------------- 791 // TypeScript | Javascript 792 // --------------------------------------------------------------------- 793 // @dec | let C = C_1 = class C { 794 // class C { | static x() { return C_1.y; } 795 // static x() { return C.y; } | } 796 // static y = 1; | C.y = 1; 797 // } | C = C_1 = __decorate([dec], C); 798 // | var C_1; 799 // --------------------------------------------------------------------- 800 // @dec | let C = class C { 801 // export class C { | static x() { return C_1.y; } 802 // static x() { return C.y; } | } 803 // static y = 1; | C.y = 1; 804 // } | C = C_1 = __decorate([dec], C); 805 // | export { C }; 806 // | var C_1; 807 // --------------------------------------------------------------------- 808 // 809 // If a class declaration is the default export of a module, we instead emit 810 // the export after the decorated declaration: 811 // 812 // [Example 3] 813 // --------------------------------------------------------------------- 814 // TypeScript | Javascript 815 // --------------------------------------------------------------------- 816 // @dec | let default_1 = class { 817 // export default class { | } 818 // } | default_1 = __decorate([dec], default_1); 819 // | export default default_1; 820 // --------------------------------------------------------------------- 821 // @dec | let C = class C { 822 // export default class C { | } 823 // } | C = __decorate([dec], C); 824 // | export default C; 825 // --------------------------------------------------------------------- 826 // 827 // If the class declaration is the default export and a reference to itself 828 // inside of the class body, we must emit both an alias for the class *and* 829 // move the export after the declaration: 830 // 831 // [Example 4] 832 // --------------------------------------------------------------------- 833 // TypeScript | Javascript 834 // --------------------------------------------------------------------- 835 // @dec | let C = class C { 836 // export default class C { | static x() { return C_1.y; } 837 // static x() { return C.y; } | } 838 // static y = 1; | C.y = 1; 839 // } | C = C_1 = __decorate([dec], C); 840 // | export default C; 841 // | var C_1; 842 // --------------------------------------------------------------------- 843 // 844 845 const location = moveRangePastDecorators(node); 846 const classAlias = getClassAliasIfNeeded(node); 847 const declName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 848 849 // ... = class ${name} ${heritageClauses} { 850 // ${members} 851 // } 852 const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); 853 const members = transformClassMembers(node); 854 const classExpression = factory.createClassExpression(/*decorators*/ undefined, /*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members); 855 setOriginalNode(classExpression, node); 856 setTextRange(classExpression, location); 857 858 // let ${name} = ${classExpression} where name is either declaredName if the class doesn't contain self-reference 859 // or decoratedClassAlias if the class contain self-reference. 860 const statement = factory.createVariableStatement( 861 /*modifiers*/ undefined, 862 factory.createVariableDeclarationList([ 863 factory.createVariableDeclaration( 864 declName, 865 /*exclamationToken*/ undefined, 866 /*type*/ undefined, 867 classAlias ? factory.createAssignment(classAlias, classExpression) : classExpression 868 ) 869 ], NodeFlags.Let) 870 ); 871 setOriginalNode(statement, node); 872 setTextRange(statement, location); 873 setCommentRange(statement, node); 874 return statement; 875 } 876 877 function visitClassExpression(node: ClassExpression): Expression { 878 if (!isClassLikeDeclarationWithTypeScriptSyntax(node)) { 879 return visitEachChild(node, visitor, context); 880 } 881 882 const classExpression = factory.createClassExpression( 883 /*decorators*/ undefined, 884 /*modifiers*/ undefined, 885 node.name, 886 /*typeParameters*/ undefined, 887 visitNodes(node.heritageClauses, visitor, isHeritageClause), 888 transformClassMembers(node) 889 ); 890 891 setOriginalNode(classExpression, node); 892 setTextRange(classExpression, node); 893 894 return classExpression; 895 } 896 897 /** 898 * Transforms the members of a class. 899 * 900 * @param node The current class. 901 */ 902 function transformClassMembers(node: ClassDeclaration | ClassExpression) { 903 const members: ClassElement[] = []; 904 const constructor = getFirstConstructorWithBody(node); 905 const parametersWithPropertyAssignments = constructor && 906 filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor)); 907 908 if (parametersWithPropertyAssignments) { 909 for (const parameter of parametersWithPropertyAssignments) { 910 if (isIdentifier(parameter.name)) { 911 members.push(setOriginalNode(factory.createPropertyDeclaration( 912 /*decorators*/ undefined, 913 /*modifiers*/ undefined, 914 parameter.name, 915 /*questionOrExclamationToken*/ undefined, 916 /*type*/ undefined, 917 /*initializer*/ undefined), parameter)); 918 } 919 } 920 } 921 922 addRange(members, visitNodes(node.members, classElementVisitor, isClassElement)); 923 return setTextRange(factory.createNodeArray(members), /*location*/ node.members); 924 } 925 926 927 /** 928 * Gets either the static or instance members of a class that are decorated, or have 929 * parameters that are decorated. 930 * 931 * @param node The class containing the member. 932 * @param isStatic A value indicating whether to retrieve static or instance members of 933 * the class. 934 */ 935 function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): readonly ClassElement[] { 936 return filter(node.members, isStatic ? m => isStaticDecoratedClassElement(m, node) : m => isInstanceDecoratedClassElement(m, node)); 937 } 938 939 /** 940 * Determines whether a class member is a static member of a class that is decorated, or 941 * has parameters that are decorated. 942 * 943 * @param member The class member. 944 */ 945 function isStaticDecoratedClassElement(member: ClassElement, parent: ClassLikeDeclaration) { 946 return isDecoratedClassElement(member, /*isStatic*/ true, parent); 947 } 948 949 /** 950 * Determines whether a class member is an instance member of a class that is decorated, 951 * or has parameters that are decorated. 952 * 953 * @param member The class member. 954 */ 955 function isInstanceDecoratedClassElement(member: ClassElement, parent: ClassLikeDeclaration) { 956 return isDecoratedClassElement(member, /*isStatic*/ false, parent); 957 } 958 959 /** 960 * Determines whether a class member is either a static or an instance member of a class 961 * that is decorated, or has parameters that are decorated. 962 * 963 * @param member The class member. 964 */ 965 function isDecoratedClassElement(member: ClassElement, isStatic: boolean, parent: ClassLikeDeclaration) { 966 return nodeOrChildIsDecorated(member, parent) 967 && isStatic === hasSyntacticModifier(member, ModifierFlags.Static); 968 } 969 970 /** 971 * A structure describing the decorators for a class element. 972 */ 973 interface AllDecorators { 974 decorators: readonly Decorator[] | undefined; 975 parameters?: readonly (readonly Decorator[] | undefined)[]; 976 } 977 978 /** 979 * Gets an array of arrays of decorators for the parameters of a function-like node. 980 * The offset into the result array should correspond to the offset of the parameter. 981 * 982 * @param node The function-like node. 983 */ 984 function getDecoratorsOfParameters(node: FunctionLikeDeclaration | undefined) { 985 let decorators: (readonly Decorator[] | undefined)[] | undefined; 986 if (node) { 987 const parameters = node.parameters; 988 const firstParameterIsThis = parameters.length > 0 && parameterIsThisKeyword(parameters[0]); 989 const firstParameterOffset = firstParameterIsThis ? 1 : 0; 990 const numParameters = firstParameterIsThis ? parameters.length - 1 : parameters.length; 991 for (let i = 0; i < numParameters; i++) { 992 const parameter = parameters[i + firstParameterOffset]; 993 if (decorators || parameter.decorators) { 994 if (!decorators) { 995 decorators = new Array(numParameters); 996 } 997 998 decorators[i] = parameter.decorators; 999 } 1000 } 1001 } 1002 1003 return decorators; 1004 } 1005 1006 /** 1007 * Gets an AllDecorators object containing the decorators for the class and the decorators for the 1008 * parameters of the constructor of the class. 1009 * 1010 * @param node The class node. 1011 */ 1012 function getAllDecoratorsOfConstructor(node: ClassExpression | ClassDeclaration): AllDecorators | undefined { 1013 const decorators = node.decorators; 1014 const parameters = getDecoratorsOfParameters(getFirstConstructorWithBody(node)); 1015 if (!decorators && !parameters) { 1016 return undefined; 1017 } 1018 1019 return { 1020 decorators, 1021 parameters 1022 }; 1023 } 1024 1025 /** 1026 * Gets an AllDecorators object containing the decorators for the member and its parameters. 1027 * 1028 * @param node The class node that contains the member. 1029 * @param member The class member. 1030 */ 1031 function getAllDecoratorsOfClassElement(node: ClassExpression | ClassDeclaration, member: ClassElement): AllDecorators | undefined { 1032 switch (member.kind) { 1033 case SyntaxKind.GetAccessor: 1034 case SyntaxKind.SetAccessor: 1035 return getAllDecoratorsOfAccessors(node, <AccessorDeclaration>member); 1036 1037 case SyntaxKind.MethodDeclaration: 1038 return getAllDecoratorsOfMethod(<MethodDeclaration>member); 1039 1040 case SyntaxKind.PropertyDeclaration: 1041 return getAllDecoratorsOfProperty(<PropertyDeclaration>member); 1042 1043 default: 1044 return undefined; 1045 } 1046 } 1047 1048 /** 1049 * Gets an AllDecorators object containing the decorators for the accessor and its parameters. 1050 * 1051 * @param node The class node that contains the accessor. 1052 * @param accessor The class accessor member. 1053 */ 1054 function getAllDecoratorsOfAccessors(node: ClassExpression | ClassDeclaration, accessor: AccessorDeclaration): AllDecorators | undefined { 1055 if (!accessor.body) { 1056 return undefined; 1057 } 1058 1059 const { firstAccessor, secondAccessor, setAccessor } = getAllAccessorDeclarations(node.members, accessor); 1060 const firstAccessorWithDecorators = firstAccessor.decorators ? firstAccessor : secondAccessor && secondAccessor.decorators ? secondAccessor : undefined; 1061 if (!firstAccessorWithDecorators || accessor !== firstAccessorWithDecorators) { 1062 return undefined; 1063 } 1064 1065 const decorators = firstAccessorWithDecorators.decorators; 1066 const parameters = getDecoratorsOfParameters(setAccessor); 1067 if (!decorators && !parameters) { 1068 return undefined; 1069 } 1070 1071 return { decorators, parameters }; 1072 } 1073 1074 /** 1075 * Gets an AllDecorators object containing the decorators for the method and its parameters. 1076 * 1077 * @param method The class method member. 1078 */ 1079 function getAllDecoratorsOfMethod(method: MethodDeclaration): AllDecorators | undefined { 1080 if (!method.body) { 1081 return undefined; 1082 } 1083 1084 const decorators = method.decorators; 1085 const parameters = getDecoratorsOfParameters(method); 1086 if (!decorators && !parameters) { 1087 return undefined; 1088 } 1089 1090 return { decorators, parameters }; 1091 } 1092 1093 /** 1094 * Gets an AllDecorators object containing the decorators for the property. 1095 * 1096 * @param property The class property member. 1097 */ 1098 function getAllDecoratorsOfProperty(property: PropertyDeclaration): AllDecorators | undefined { 1099 const decorators = property.decorators; 1100 if (!decorators) { 1101 return undefined; 1102 1103 } 1104 1105 return { decorators }; 1106 } 1107 1108 /** 1109 * Transforms all of the decorators for a declaration into an array of expressions. 1110 * 1111 * @param node The declaration node. 1112 * @param allDecorators An object containing all of the decorators for the declaration. 1113 */ 1114 function transformAllDecoratorsOfDeclaration(node: Declaration, container: ClassLikeDeclaration, allDecorators: AllDecorators | undefined) { 1115 if (!allDecorators) { 1116 return undefined; 1117 } 1118 1119 const decoratorExpressions: Expression[] = []; 1120 addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); 1121 addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); 1122 addTypeMetadata(node, container, decoratorExpressions); 1123 return decoratorExpressions; 1124 } 1125 1126 /** 1127 * Generates statements used to apply decorators to either the static or instance members 1128 * of a class. 1129 * 1130 * @param node The class node. 1131 * @param isStatic A value indicating whether to generate statements for static or 1132 * instance members. 1133 */ 1134 function addClassElementDecorationStatements(statements: Statement[], node: ClassDeclaration, isStatic: boolean) { 1135 addRange(statements, map(generateClassElementDecorationExpressions(node, isStatic), expressionToStatement)); 1136 } 1137 1138 /** 1139 * Generates expressions used to apply decorators to either the static or instance members 1140 * of a class. 1141 * 1142 * @param node The class node. 1143 * @param isStatic A value indicating whether to generate expressions for static or 1144 * instance members. 1145 */ 1146 function generateClassElementDecorationExpressions(node: ClassExpression | ClassDeclaration, isStatic: boolean) { 1147 const members = getDecoratedClassElements(node, isStatic); 1148 let expressions: Expression[] | undefined; 1149 for (const member of members) { 1150 const expression = generateClassElementDecorationExpression(node, member); 1151 if (expression) { 1152 if (!expressions) { 1153 expressions = [expression]; 1154 } 1155 else { 1156 expressions.push(expression); 1157 } 1158 } 1159 } 1160 return expressions; 1161 } 1162 1163 /** 1164 * Generates an expression used to evaluate class element decorators at runtime. 1165 * 1166 * @param node The class node that contains the member. 1167 * @param member The class member. 1168 */ 1169 function generateClassElementDecorationExpression(node: ClassExpression | ClassDeclaration, member: ClassElement) { 1170 const allDecorators = getAllDecoratorsOfClassElement(node, member); 1171 const decoratorExpressions = transformAllDecoratorsOfDeclaration(member, node, allDecorators); 1172 if (!decoratorExpressions) { 1173 return undefined; 1174 } 1175 1176 // Emit the call to __decorate. Given the following: 1177 // 1178 // class C { 1179 // @dec method(@dec2 x) {} 1180 // @dec get accessor() {} 1181 // @dec prop; 1182 // } 1183 // 1184 // The emit for a method is: 1185 // 1186 // __decorate([ 1187 // dec, 1188 // __param(0, dec2), 1189 // __metadata("design:type", Function), 1190 // __metadata("design:paramtypes", [Object]), 1191 // __metadata("design:returntype", void 0) 1192 // ], C.prototype, "method", null); 1193 // 1194 // The emit for an accessor is: 1195 // 1196 // __decorate([ 1197 // dec 1198 // ], C.prototype, "accessor", null); 1199 // 1200 // The emit for a property is: 1201 // 1202 // __decorate([ 1203 // dec 1204 // ], C.prototype, "prop"); 1205 // 1206 1207 const prefix = getClassMemberPrefix(node, member); 1208 const memberName = getExpressionForPropertyName(member, /*generateNameForComputedPropertyName*/ true); 1209 const descriptor = languageVersion > ScriptTarget.ES3 1210 ? member.kind === SyntaxKind.PropertyDeclaration 1211 // We emit `void 0` here to indicate to `__decorate` that it can invoke `Object.defineProperty` directly, but that it 1212 // should not invoke `Object.getOwnPropertyDescriptor`. 1213 ? factory.createVoidZero() 1214 1215 // We emit `null` here to indicate to `__decorate` that it can invoke `Object.getOwnPropertyDescriptor` directly. 1216 // We have this extra argument here so that we can inject an explicit property descriptor at a later date. 1217 : factory.createNull() 1218 : undefined; 1219 1220 const helper = emitHelpers().createDecorateHelper( 1221 decoratorExpressions, 1222 prefix, 1223 memberName, 1224 descriptor 1225 ); 1226 1227 setTextRange(helper, moveRangePastDecorators(member)); 1228 setEmitFlags(helper, EmitFlags.NoComments); 1229 return helper; 1230 } 1231 1232 /** 1233 * Generates a __decorate helper call for a class constructor. 1234 * 1235 * @param node The class node. 1236 */ 1237 function addConstructorDecorationStatement(statements: Statement[], node: ClassDeclaration) { 1238 const expression = generateConstructorDecorationExpression(node); 1239 if (expression) { 1240 statements.push(setOriginalNode(factory.createExpressionStatement(expression), node)); 1241 } 1242 } 1243 1244 /** 1245 * Generates a __decorate helper call for a class constructor. 1246 * 1247 * @param node The class node. 1248 */ 1249 function generateConstructorDecorationExpression(node: ClassExpression | ClassDeclaration) { 1250 const allDecorators = getAllDecoratorsOfConstructor(node); 1251 const decoratorExpressions = transformAllDecoratorsOfDeclaration(node, node, allDecorators); 1252 if (!decoratorExpressions) { 1253 return undefined; 1254 } 1255 1256 const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; 1257 const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 1258 const decorate = emitHelpers().createDecorateHelper(decoratorExpressions, localName); 1259 const expression = factory.createAssignment(localName, classAlias ? factory.createAssignment(classAlias, decorate) : decorate); 1260 setEmitFlags(expression, EmitFlags.NoComments); 1261 setSourceMapRange(expression, moveRangePastDecorators(node)); 1262 return expression; 1263 } 1264 1265 /** 1266 * Transforms a decorator into an expression. 1267 * 1268 * @param decorator The decorator node. 1269 */ 1270 function transformDecorator(decorator: Decorator) { 1271 return visitNode(decorator.expression, visitor, isExpression); 1272 } 1273 1274 /** 1275 * Transforms the decorators of a parameter. 1276 * 1277 * @param decorators The decorators for the parameter at the provided offset. 1278 * @param parameterOffset The offset of the parameter. 1279 */ 1280 function transformDecoratorsOfParameter(decorators: Decorator[], parameterOffset: number) { 1281 let expressions: Expression[] | undefined; 1282 if (decorators) { 1283 expressions = []; 1284 for (const decorator of decorators) { 1285 const helper = emitHelpers().createParamHelper( 1286 transformDecorator(decorator), 1287 parameterOffset); 1288 setTextRange(helper, decorator.expression); 1289 setEmitFlags(helper, EmitFlags.NoComments); 1290 expressions.push(helper); 1291 } 1292 } 1293 1294 return expressions; 1295 } 1296 1297 /** 1298 * Adds optional type metadata for a declaration. 1299 * 1300 * @param node The declaration node. 1301 * @param decoratorExpressions The destination array to which to add new decorator expressions. 1302 */ 1303 function addTypeMetadata(node: Declaration, container: ClassLikeDeclaration, decoratorExpressions: Expression[]) { 1304 if (USE_NEW_TYPE_METADATA_FORMAT) { 1305 addNewTypeMetadata(node, container, decoratorExpressions); 1306 } 1307 else { 1308 addOldTypeMetadata(node, container, decoratorExpressions); 1309 } 1310 } 1311 1312 function addOldTypeMetadata(node: Declaration, container: ClassLikeDeclaration, decoratorExpressions: Expression[]) { 1313 if (compilerOptions.emitDecoratorMetadata) { 1314 if (shouldAddTypeMetadata(node)) { 1315 decoratorExpressions.push(emitHelpers().createMetadataHelper("design:type", serializeTypeOfNode(node))); 1316 } 1317 if (shouldAddParamTypesMetadata(node)) { 1318 decoratorExpressions.push(emitHelpers().createMetadataHelper("design:paramtypes", serializeParameterTypesOfNode(node, container))); 1319 } 1320 if (shouldAddReturnTypeMetadata(node)) { 1321 decoratorExpressions.push(emitHelpers().createMetadataHelper("design:returntype", serializeReturnTypeOfNode(node))); 1322 } 1323 } 1324 } 1325 1326 function addNewTypeMetadata(node: Declaration, container: ClassLikeDeclaration, decoratorExpressions: Expression[]) { 1327 if (compilerOptions.emitDecoratorMetadata) { 1328 let properties: ObjectLiteralElementLike[] | undefined; 1329 if (shouldAddTypeMetadata(node)) { 1330 (properties || (properties = [])).push(factory.createPropertyAssignment("type", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), serializeTypeOfNode(node)))); 1331 } 1332 if (shouldAddParamTypesMetadata(node)) { 1333 (properties || (properties = [])).push(factory.createPropertyAssignment("paramTypes", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), serializeParameterTypesOfNode(node, container)))); 1334 } 1335 if (shouldAddReturnTypeMetadata(node)) { 1336 (properties || (properties = [])).push(factory.createPropertyAssignment("returnType", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node)))); 1337 } 1338 if (properties) { 1339 decoratorExpressions.push(emitHelpers().createMetadataHelper("design:typeinfo", factory.createObjectLiteralExpression(properties, /*multiLine*/ true))); 1340 } 1341 } 1342 } 1343 1344 /** 1345 * Determines whether to emit the "design:type" metadata based on the node's kind. 1346 * The caller should have already tested whether the node has decorators and whether the 1347 * emitDecoratorMetadata compiler option is set. 1348 * 1349 * @param node The node to test. 1350 */ 1351 function shouldAddTypeMetadata(node: Declaration): boolean { 1352 const kind = node.kind; 1353 return kind === SyntaxKind.MethodDeclaration 1354 || kind === SyntaxKind.GetAccessor 1355 || kind === SyntaxKind.SetAccessor 1356 || kind === SyntaxKind.PropertyDeclaration; 1357 } 1358 1359 /** 1360 * Determines whether to emit the "design:returntype" metadata based on the node's kind. 1361 * The caller should have already tested whether the node has decorators and whether the 1362 * emitDecoratorMetadata compiler option is set. 1363 * 1364 * @param node The node to test. 1365 */ 1366 function shouldAddReturnTypeMetadata(node: Declaration): boolean { 1367 return node.kind === SyntaxKind.MethodDeclaration; 1368 } 1369 1370 /** 1371 * Determines whether to emit the "design:paramtypes" metadata based on the node's kind. 1372 * The caller should have already tested whether the node has decorators and whether the 1373 * emitDecoratorMetadata compiler option is set. 1374 * 1375 * @param node The node to test. 1376 */ 1377 function shouldAddParamTypesMetadata(node: Declaration): boolean { 1378 switch (node.kind) { 1379 case SyntaxKind.ClassDeclaration: 1380 case SyntaxKind.ClassExpression: 1381 return getFirstConstructorWithBody(<ClassLikeDeclaration>node) !== undefined; 1382 case SyntaxKind.MethodDeclaration: 1383 case SyntaxKind.GetAccessor: 1384 case SyntaxKind.SetAccessor: 1385 return true; 1386 } 1387 return false; 1388 } 1389 1390 type SerializedEntityNameAsExpression = Identifier | BinaryExpression | PropertyAccessExpression; 1391 type SerializedTypeNode = SerializedEntityNameAsExpression | VoidExpression | ConditionalExpression; 1392 1393 function getAccessorTypeNode(node: AccessorDeclaration) { 1394 const accessors = resolver.getAllAccessorDeclarations(node); 1395 return accessors.setAccessor && getSetAccessorTypeAnnotationNode(accessors.setAccessor) 1396 || accessors.getAccessor && getEffectiveReturnTypeNode(accessors.getAccessor); 1397 } 1398 1399 /** 1400 * Serializes the type of a node for use with decorator type metadata. 1401 * 1402 * @param node The node that should have its type serialized. 1403 */ 1404 function serializeTypeOfNode(node: Node): SerializedTypeNode { 1405 switch (node.kind) { 1406 case SyntaxKind.PropertyDeclaration: 1407 case SyntaxKind.Parameter: 1408 return serializeTypeNode((<PropertyDeclaration | ParameterDeclaration | GetAccessorDeclaration>node).type); 1409 case SyntaxKind.SetAccessor: 1410 case SyntaxKind.GetAccessor: 1411 return serializeTypeNode(getAccessorTypeNode(node as AccessorDeclaration)); 1412 case SyntaxKind.ClassDeclaration: 1413 case SyntaxKind.ClassExpression: 1414 case SyntaxKind.MethodDeclaration: 1415 return factory.createIdentifier("Function"); 1416 default: 1417 return factory.createVoidZero(); 1418 } 1419 } 1420 1421 /** 1422 * Serializes the types of the parameters of a node for use with decorator type metadata. 1423 * 1424 * @param node The node that should have its parameter types serialized. 1425 */ 1426 function serializeParameterTypesOfNode(node: Node, container: ClassLikeDeclaration): ArrayLiteralExpression { 1427 const valueDeclaration = 1428 isClassLike(node) 1429 ? getFirstConstructorWithBody(node) 1430 : isFunctionLike(node) && nodeIsPresent((node as FunctionLikeDeclaration).body) 1431 ? node 1432 : undefined; 1433 1434 const expressions: SerializedTypeNode[] = []; 1435 if (valueDeclaration) { 1436 const parameters = getParametersOfDecoratedDeclaration(valueDeclaration, container); 1437 const numParameters = parameters.length; 1438 for (let i = 0; i < numParameters; i++) { 1439 const parameter = parameters[i]; 1440 if (i === 0 && isIdentifier(parameter.name) && parameter.name.escapedText === "this") { 1441 continue; 1442 } 1443 if (parameter.dotDotDotToken) { 1444 expressions.push(serializeTypeNode(getRestParameterElementType(parameter.type))); 1445 } 1446 else { 1447 expressions.push(serializeTypeOfNode(parameter)); 1448 } 1449 } 1450 } 1451 1452 return factory.createArrayLiteralExpression(expressions); 1453 } 1454 1455 function getParametersOfDecoratedDeclaration(node: SignatureDeclaration, container: ClassLikeDeclaration) { 1456 if (container && node.kind === SyntaxKind.GetAccessor) { 1457 const { setAccessor } = getAllAccessorDeclarations(container.members, <AccessorDeclaration>node); 1458 if (setAccessor) { 1459 return setAccessor.parameters; 1460 } 1461 } 1462 return node.parameters; 1463 } 1464 1465 /** 1466 * Serializes the return type of a node for use with decorator type metadata. 1467 * 1468 * @param node The node that should have its return type serialized. 1469 */ 1470 function serializeReturnTypeOfNode(node: Node): SerializedTypeNode { 1471 if (isFunctionLike(node) && node.type) { 1472 return serializeTypeNode(node.type); 1473 } 1474 else if (isAsyncFunction(node)) { 1475 return factory.createIdentifier("Promise"); 1476 } 1477 1478 return factory.createVoidZero(); 1479 } 1480 1481 /** 1482 * Serializes a type node for use with decorator type metadata. 1483 * 1484 * Types are serialized in the following fashion: 1485 * - Void types point to "undefined" (e.g. "void 0") 1486 * - Function and Constructor types point to the global "Function" constructor. 1487 * - Interface types with a call or construct signature types point to the global 1488 * "Function" constructor. 1489 * - Array and Tuple types point to the global "Array" constructor. 1490 * - Type predicates and booleans point to the global "Boolean" constructor. 1491 * - String literal types and strings point to the global "String" constructor. 1492 * - Enum and number types point to the global "Number" constructor. 1493 * - Symbol types point to the global "Symbol" constructor. 1494 * - Type references to classes (or class-like variables) point to the constructor for the class. 1495 * - Anything else points to the global "Object" constructor. 1496 * 1497 * @param node The type node to serialize. 1498 */ 1499 function serializeTypeNode(node: TypeNode | undefined): SerializedTypeNode { 1500 if (node === undefined) { 1501 return factory.createIdentifier("Object"); 1502 } 1503 1504 switch (node.kind) { 1505 case SyntaxKind.VoidKeyword: 1506 case SyntaxKind.UndefinedKeyword: 1507 case SyntaxKind.NeverKeyword: 1508 return factory.createVoidZero(); 1509 1510 case SyntaxKind.ParenthesizedType: 1511 return serializeTypeNode((<ParenthesizedTypeNode>node).type); 1512 1513 case SyntaxKind.FunctionType: 1514 case SyntaxKind.ConstructorType: 1515 return factory.createIdentifier("Function"); 1516 1517 case SyntaxKind.ArrayType: 1518 case SyntaxKind.TupleType: 1519 return factory.createIdentifier("Array"); 1520 1521 case SyntaxKind.TypePredicate: 1522 case SyntaxKind.BooleanKeyword: 1523 return factory.createIdentifier("Boolean"); 1524 1525 case SyntaxKind.StringKeyword: 1526 return factory.createIdentifier("String"); 1527 1528 case SyntaxKind.ObjectKeyword: 1529 return factory.createIdentifier("Object"); 1530 1531 case SyntaxKind.LiteralType: 1532 switch ((<LiteralTypeNode>node).literal.kind) { 1533 case SyntaxKind.StringLiteral: 1534 case SyntaxKind.NoSubstitutionTemplateLiteral: 1535 return factory.createIdentifier("String"); 1536 1537 case SyntaxKind.PrefixUnaryExpression: 1538 case SyntaxKind.NumericLiteral: 1539 return factory.createIdentifier("Number"); 1540 1541 case SyntaxKind.BigIntLiteral: 1542 return getGlobalBigIntNameWithFallback(); 1543 1544 case SyntaxKind.TrueKeyword: 1545 case SyntaxKind.FalseKeyword: 1546 return factory.createIdentifier("Boolean"); 1547 1548 case SyntaxKind.NullKeyword: 1549 return factory.createVoidZero(); 1550 1551 default: 1552 return Debug.failBadSyntaxKind((<LiteralTypeNode>node).literal); 1553 } 1554 1555 case SyntaxKind.NumberKeyword: 1556 return factory.createIdentifier("Number"); 1557 1558 case SyntaxKind.BigIntKeyword: 1559 return getGlobalBigIntNameWithFallback(); 1560 1561 case SyntaxKind.SymbolKeyword: 1562 return languageVersion < ScriptTarget.ES2015 1563 ? getGlobalSymbolNameWithFallback() 1564 : factory.createIdentifier("Symbol"); 1565 1566 case SyntaxKind.TypeReference: 1567 return serializeTypeReferenceNode(<TypeReferenceNode>node); 1568 1569 case SyntaxKind.IntersectionType: 1570 case SyntaxKind.UnionType: 1571 return serializeTypeList((<UnionOrIntersectionTypeNode>node).types); 1572 1573 case SyntaxKind.ConditionalType: 1574 return serializeTypeList([(<ConditionalTypeNode>node).trueType, (<ConditionalTypeNode>node).falseType]); 1575 1576 case SyntaxKind.TypeOperator: 1577 if ((<TypeOperatorNode>node).operator === SyntaxKind.ReadonlyKeyword) { 1578 return serializeTypeNode((<TypeOperatorNode>node).type); 1579 } 1580 break; 1581 1582 case SyntaxKind.TypeQuery: 1583 case SyntaxKind.IndexedAccessType: 1584 case SyntaxKind.MappedType: 1585 case SyntaxKind.TypeLiteral: 1586 case SyntaxKind.AnyKeyword: 1587 case SyntaxKind.UnknownKeyword: 1588 case SyntaxKind.ThisType: 1589 case SyntaxKind.ImportType: 1590 break; 1591 1592 // handle JSDoc types from an invalid parse 1593 case SyntaxKind.JSDocAllType: 1594 case SyntaxKind.JSDocUnknownType: 1595 case SyntaxKind.JSDocFunctionType: 1596 case SyntaxKind.JSDocVariadicType: 1597 case SyntaxKind.JSDocNamepathType: 1598 break; 1599 1600 case SyntaxKind.JSDocNullableType: 1601 case SyntaxKind.JSDocNonNullableType: 1602 case SyntaxKind.JSDocOptionalType: 1603 return serializeTypeNode((<JSDocNullableType | JSDocNonNullableType | JSDocOptionalType>node).type); 1604 default: 1605 return Debug.failBadSyntaxKind(node); 1606 } 1607 1608 return factory.createIdentifier("Object"); 1609 } 1610 1611 function serializeTypeList(types: readonly TypeNode[]): SerializedTypeNode { 1612 // Note when updating logic here also update getEntityNameForDecoratorMetadata 1613 // so that aliases can be marked as referenced 1614 let serializedUnion: SerializedTypeNode | undefined; 1615 for (let typeNode of types) { 1616 while (typeNode.kind === SyntaxKind.ParenthesizedType) { 1617 typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be 1618 } 1619 if (typeNode.kind === SyntaxKind.NeverKeyword) { 1620 continue; // Always elide `never` from the union/intersection if possible 1621 } 1622 if (!strictNullChecks && (typeNode.kind === SyntaxKind.LiteralType && (typeNode as LiteralTypeNode).literal.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { 1623 continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks 1624 } 1625 const serializedIndividual = serializeTypeNode(typeNode); 1626 1627 if (isIdentifier(serializedIndividual) && serializedIndividual.escapedText === "Object") { 1628 // One of the individual is global object, return immediately 1629 return serializedIndividual; 1630 } 1631 // If there exists union that is not void 0 expression, check if the the common type is identifier. 1632 // anything more complex and we will just default to Object 1633 else if (serializedUnion) { 1634 // Different types 1635 if (!isIdentifier(serializedUnion) || 1636 !isIdentifier(serializedIndividual) || 1637 serializedUnion.escapedText !== serializedIndividual.escapedText) { 1638 return factory.createIdentifier("Object"); 1639 } 1640 } 1641 else { 1642 // Initialize the union type 1643 serializedUnion = serializedIndividual; 1644 } 1645 } 1646 1647 // If we were able to find common type, use it 1648 return serializedUnion || factory.createVoidZero(); // Fallback is only hit if all union constituients are null/undefined/never 1649 } 1650 1651 /** 1652 * Serializes a TypeReferenceNode to an appropriate JS constructor value for use with 1653 * decorator type metadata. 1654 * 1655 * @param node The type reference node. 1656 */ 1657 function serializeTypeReferenceNode(node: TypeReferenceNode): SerializedTypeNode { 1658 const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope || currentLexicalScope); 1659 switch (kind) { 1660 case TypeReferenceSerializationKind.Unknown: 1661 // From conditional type type reference that cannot be resolved is Similar to any or unknown 1662 if (findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && (n.parent.trueType === n || n.parent.falseType === n))) { 1663 return factory.createIdentifier("Object"); 1664 } 1665 1666 const serialized = serializeEntityNameAsExpressionFallback(node.typeName); 1667 const temp = factory.createTempVariable(hoistVariableDeclaration); 1668 return factory.createConditionalExpression( 1669 factory.createTypeCheck(factory.createAssignment(temp, serialized), "function"), 1670 /*questionToken*/ undefined, 1671 temp, 1672 /*colonToken*/ undefined, 1673 factory.createIdentifier("Object") 1674 ); 1675 1676 case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue: 1677 return serializeEntityNameAsExpression(node.typeName); 1678 1679 case TypeReferenceSerializationKind.VoidNullableOrNeverType: 1680 return factory.createVoidZero(); 1681 1682 case TypeReferenceSerializationKind.BigIntLikeType: 1683 return getGlobalBigIntNameWithFallback(); 1684 1685 case TypeReferenceSerializationKind.BooleanType: 1686 return factory.createIdentifier("Boolean"); 1687 1688 case TypeReferenceSerializationKind.NumberLikeType: 1689 return factory.createIdentifier("Number"); 1690 1691 case TypeReferenceSerializationKind.StringLikeType: 1692 return factory.createIdentifier("String"); 1693 1694 case TypeReferenceSerializationKind.ArrayLikeType: 1695 return factory.createIdentifier("Array"); 1696 1697 case TypeReferenceSerializationKind.ESSymbolType: 1698 return languageVersion < ScriptTarget.ES2015 1699 ? getGlobalSymbolNameWithFallback() 1700 : factory.createIdentifier("Symbol"); 1701 1702 case TypeReferenceSerializationKind.TypeWithCallSignature: 1703 return factory.createIdentifier("Function"); 1704 1705 case TypeReferenceSerializationKind.Promise: 1706 return factory.createIdentifier("Promise"); 1707 1708 case TypeReferenceSerializationKind.ObjectType: 1709 return factory.createIdentifier("Object"); 1710 default: 1711 return Debug.assertNever(kind); 1712 } 1713 } 1714 1715 function createCheckedValue(left: Expression, right: Expression) { 1716 return factory.createLogicalAnd( 1717 factory.createStrictInequality(factory.createTypeOfExpression(left), factory.createStringLiteral("undefined")), 1718 right 1719 ); 1720 } 1721 1722 /** 1723 * Serializes an entity name which may not exist at runtime, but whose access shouldn't throw 1724 * 1725 * @param node The entity name to serialize. 1726 */ 1727 function serializeEntityNameAsExpressionFallback(node: EntityName): BinaryExpression { 1728 if (node.kind === SyntaxKind.Identifier) { 1729 // A -> typeof A !== undefined && A 1730 const copied = serializeEntityNameAsExpression(node); 1731 return createCheckedValue(copied, copied); 1732 } 1733 if (node.left.kind === SyntaxKind.Identifier) { 1734 // A.B -> typeof A !== undefined && A.B 1735 return createCheckedValue(serializeEntityNameAsExpression(node.left), serializeEntityNameAsExpression(node)); 1736 } 1737 // A.B.C -> typeof A !== undefined && (_a = A.B) !== void 0 && _a.C 1738 const left = serializeEntityNameAsExpressionFallback(node.left); 1739 const temp = factory.createTempVariable(hoistVariableDeclaration); 1740 return factory.createLogicalAnd( 1741 factory.createLogicalAnd( 1742 left.left, 1743 factory.createStrictInequality(factory.createAssignment(temp, left.right), factory.createVoidZero()) 1744 ), 1745 factory.createPropertyAccessExpression(temp, node.right) 1746 ); 1747 } 1748 1749 /** 1750 * Serializes an entity name as an expression for decorator type metadata. 1751 * 1752 * @param node The entity name to serialize. 1753 */ 1754 function serializeEntityNameAsExpression(node: EntityName): SerializedEntityNameAsExpression { 1755 switch (node.kind) { 1756 case SyntaxKind.Identifier: 1757 // Create a clone of the name with a new parent, and treat it as if it were 1758 // a source tree node for the purposes of the checker. 1759 const name = setParent(setTextRange(parseNodeFactory.cloneNode(node), node), node.parent); 1760 name.original = undefined; 1761 setParent(name, getParseTreeNode(currentLexicalScope)); // ensure the parent is set to a parse tree node. 1762 return name; 1763 1764 case SyntaxKind.QualifiedName: 1765 return serializeQualifiedNameAsExpression(node); 1766 } 1767 } 1768 1769 /** 1770 * Serializes an qualified name as an expression for decorator type metadata. 1771 * 1772 * @param node The qualified name to serialize. 1773 * @param useFallback A value indicating whether to use logical operators to test for the 1774 * qualified name at runtime. 1775 */ 1776 function serializeQualifiedNameAsExpression(node: QualifiedName): SerializedEntityNameAsExpression { 1777 return factory.createPropertyAccessExpression(serializeEntityNameAsExpression(node.left), node.right); 1778 } 1779 1780 /** 1781 * Gets an expression that points to the global "Symbol" constructor at runtime if it is 1782 * available. 1783 */ 1784 function getGlobalSymbolNameWithFallback(): ConditionalExpression { 1785 return factory.createConditionalExpression( 1786 factory.createTypeCheck(factory.createIdentifier("Symbol"), "function"), 1787 /*questionToken*/ undefined, 1788 factory.createIdentifier("Symbol"), 1789 /*colonToken*/ undefined, 1790 factory.createIdentifier("Object") 1791 ); 1792 } 1793 1794 /** 1795 * Gets an expression that points to the global "BigInt" constructor at runtime if it is 1796 * available. 1797 */ 1798 function getGlobalBigIntNameWithFallback(): SerializedTypeNode { 1799 return languageVersion < ScriptTarget.ESNext 1800 ? factory.createConditionalExpression( 1801 factory.createTypeCheck(factory.createIdentifier("BigInt"), "function"), 1802 /*questionToken*/ undefined, 1803 factory.createIdentifier("BigInt"), 1804 /*colonToken*/ undefined, 1805 factory.createIdentifier("Object") 1806 ) 1807 : factory.createIdentifier("BigInt"); 1808 } 1809 1810 /** 1811 * Gets an expression that represents a property name (for decorated properties or enums). 1812 * For a computed property, a name is generated for the node. 1813 * 1814 * @param member The member whose name should be converted into an expression. 1815 */ 1816 function getExpressionForPropertyName(member: ClassElement | EnumMember, generateNameForComputedPropertyName: boolean): Expression { 1817 const name = member.name!; 1818 if (isPrivateIdentifier(name)) { 1819 return factory.createIdentifier(""); 1820 } 1821 else if (isComputedPropertyName(name)) { 1822 return generateNameForComputedPropertyName && !isSimpleInlineableExpression(name.expression) 1823 ? factory.getGeneratedNameForNode(name) 1824 : name.expression; 1825 } 1826 else if (isIdentifier(name)) { 1827 return factory.createStringLiteral(idText(name)); 1828 } 1829 else { 1830 return factory.cloneNode(name); 1831 } 1832 } 1833 1834 /** 1835 * Visits the property name of a class element, for use when emitting property 1836 * initializers. For a computed property on a node with decorators, a temporary 1837 * value is stored for later use. 1838 * 1839 * @param member The member whose name should be visited. 1840 */ 1841 function visitPropertyNameOfClassElement(member: ClassElement): PropertyName { 1842 const name = member.name!; 1843 // Computed property names need to be transformed into a hoisted variable when they are used more than once. 1844 // The names are used more than once when: 1845 // - the property is non-static and its initializer is moved to the constructor (when there are parameter property assignments). 1846 // - the property has a decorator. 1847 if (isComputedPropertyName(name) && ((!hasStaticModifier(member) && currentClassHasParameterProperties) || some(member.decorators))) { 1848 const expression = visitNode(name.expression, visitor, isExpression); 1849 const innerExpression = skipPartiallyEmittedExpressions(expression); 1850 if (!isSimpleInlineableExpression(innerExpression)) { 1851 const generatedName = factory.getGeneratedNameForNode(name); 1852 hoistVariableDeclaration(generatedName); 1853 return factory.updateComputedPropertyName(name, factory.createAssignment(generatedName, expression)); 1854 } 1855 } 1856 return visitNode(name, visitor, isPropertyName); 1857 } 1858 1859 /** 1860 * Transforms a HeritageClause with TypeScript syntax. 1861 * 1862 * This function will only be called when one of the following conditions are met: 1863 * - The node is a non-`extends` heritage clause that should be elided. 1864 * - The node is an `extends` heritage clause that should be visited, but only allow a single type. 1865 * 1866 * @param node The HeritageClause to transform. 1867 */ 1868 function visitHeritageClause(node: HeritageClause): HeritageClause | undefined { 1869 if (node.token === SyntaxKind.ImplementsKeyword) { 1870 // implements clauses are elided 1871 return undefined; 1872 } 1873 return visitEachChild(node, visitor, context); 1874 } 1875 1876 /** 1877 * Transforms an ExpressionWithTypeArguments with TypeScript syntax. 1878 * 1879 * This function will only be called when one of the following conditions are met: 1880 * - The node contains type arguments that should be elided. 1881 * 1882 * @param node The ExpressionWithTypeArguments to transform. 1883 */ 1884 function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments): ExpressionWithTypeArguments { 1885 return factory.updateExpressionWithTypeArguments( 1886 node, 1887 visitNode(node.expression, visitor, isLeftHandSideExpression), 1888 /*typeArguments*/ undefined 1889 ); 1890 } 1891 1892 /** 1893 * Determines whether to emit a function-like declaration. We should not emit the 1894 * declaration if it does not have a body. 1895 * 1896 * @param node The declaration node. 1897 */ 1898 function shouldEmitFunctionLikeDeclaration<T extends FunctionLikeDeclaration>(node: T): node is T & { body: NonNullable<T["body"]> } { 1899 return !nodeIsMissing(node.body); 1900 } 1901 1902 function visitPropertyDeclaration(node: PropertyDeclaration) { 1903 if (node.flags & NodeFlags.Ambient || hasSyntacticModifier(node, ModifierFlags.Abstract)) { 1904 return undefined; 1905 } 1906 const updated = factory.updatePropertyDeclaration( 1907 node, 1908 /*decorators*/ undefined, 1909 visitNodes(node.modifiers, visitor, isModifier), 1910 visitPropertyNameOfClassElement(node), 1911 /*questionOrExclamationToken*/ undefined, 1912 /*type*/ undefined, 1913 visitNode(node.initializer, visitor) 1914 ); 1915 if (updated !== node) { 1916 // While we emit the source map for the node after skipping decorators and modifiers, 1917 // we need to emit the comments for the original range. 1918 setCommentRange(updated, node); 1919 setSourceMapRange(updated, moveRangePastDecorators(node)); 1920 } 1921 return updated; 1922 } 1923 1924 function visitConstructor(node: ConstructorDeclaration) { 1925 if (!shouldEmitFunctionLikeDeclaration(node)) { 1926 return undefined; 1927 } 1928 1929 return factory.updateConstructorDeclaration( 1930 node, 1931 /*decorators*/ undefined, 1932 /*modifiers*/ undefined, 1933 visitParameterList(node.parameters, visitor, context), 1934 transformConstructorBody(node.body, node) 1935 ); 1936 } 1937 1938 function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) { 1939 const parametersWithPropertyAssignments = constructor && 1940 filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor)); 1941 if (!some(parametersWithPropertyAssignments)) { 1942 return visitFunctionBody(body, visitor, context); 1943 } 1944 1945 let statements: Statement[] = []; 1946 let indexOfFirstStatement = 0; 1947 1948 resumeLexicalEnvironment(); 1949 1950 indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(factory, constructor, statements, visitor); 1951 1952 // Add parameters with property assignments. Transforms this: 1953 // 1954 // constructor (public x, public y) { 1955 // } 1956 // 1957 // Into this: 1958 // 1959 // constructor (x, y) { 1960 // this.x = x; 1961 // this.y = y; 1962 // } 1963 // 1964 addRange(statements, map(parametersWithPropertyAssignments, transformParameterWithPropertyAssignment)); 1965 1966 // Add the existing statements, skipping the initial super call. 1967 addRange(statements, visitNodes(body.statements, visitor, isStatement, indexOfFirstStatement)); 1968 1969 // End the lexical environment. 1970 statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment()); 1971 const block = factory.createBlock(setTextRange(factory.createNodeArray(statements), body.statements), /*multiLine*/ true); 1972 setTextRange(block, /*location*/ body); 1973 setOriginalNode(block, body); 1974 return block; 1975 } 1976 1977 /** 1978 * Transforms a parameter into a property assignment statement. 1979 * 1980 * @param node The parameter declaration. 1981 */ 1982 function transformParameterWithPropertyAssignment(node: ParameterPropertyDeclaration) { 1983 const name = node.name; 1984 if (!isIdentifier(name)) { 1985 return undefined; 1986 } 1987 1988 // TODO(rbuckton): Does this need to be parented? 1989 const propertyName = setParent(setTextRange(factory.cloneNode(name), name), name.parent); 1990 setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap); 1991 1992 // TODO(rbuckton): Does this need to be parented? 1993 const localName = setParent(setTextRange(factory.cloneNode(name), name), name.parent); 1994 setEmitFlags(localName, EmitFlags.NoComments); 1995 1996 return startOnNewLine( 1997 removeAllComments( 1998 setTextRange( 1999 setOriginalNode( 2000 factory.createExpressionStatement( 2001 factory.createAssignment( 2002 setTextRange( 2003 factory.createPropertyAccessExpression( 2004 factory.createThis(), 2005 propertyName 2006 ), 2007 node.name 2008 ), 2009 localName 2010 ) 2011 ), 2012 node 2013 ), 2014 moveRangePos(node, -1) 2015 ) 2016 ) 2017 ); 2018 } 2019 2020 function visitMethodDeclaration(node: MethodDeclaration) { 2021 if (!shouldEmitFunctionLikeDeclaration(node)) { 2022 return undefined; 2023 } 2024 const updated = factory.updateMethodDeclaration( 2025 node, 2026 /*decorators*/ undefined, 2027 visitNodes(node.modifiers, modifierVisitor, isModifier), 2028 node.asteriskToken, 2029 visitPropertyNameOfClassElement(node), 2030 /*questionToken*/ undefined, 2031 /*typeParameters*/ undefined, 2032 visitParameterList(node.parameters, visitor, context), 2033 /*type*/ undefined, 2034 visitFunctionBody(node.body, visitor, context) 2035 ); 2036 if (updated !== node) { 2037 // While we emit the source map for the node after skipping decorators and modifiers, 2038 // we need to emit the comments for the original range. 2039 setCommentRange(updated, node); 2040 setSourceMapRange(updated, moveRangePastDecorators(node)); 2041 } 2042 return updated; 2043 } 2044 2045 /** 2046 * Determines whether to emit an accessor declaration. We should not emit the 2047 * declaration if it does not have a body and is abstract. 2048 * 2049 * @param node The declaration node. 2050 */ 2051 function shouldEmitAccessorDeclaration(node: AccessorDeclaration) { 2052 return !(nodeIsMissing(node.body) && hasSyntacticModifier(node, ModifierFlags.Abstract)); 2053 } 2054 2055 function visitGetAccessor(node: GetAccessorDeclaration) { 2056 if (!shouldEmitAccessorDeclaration(node)) { 2057 return undefined; 2058 } 2059 const updated = factory.updateGetAccessorDeclaration( 2060 node, 2061 /*decorators*/ undefined, 2062 visitNodes(node.modifiers, modifierVisitor, isModifier), 2063 visitPropertyNameOfClassElement(node), 2064 visitParameterList(node.parameters, visitor, context), 2065 /*type*/ undefined, 2066 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 2067 ); 2068 if (updated !== node) { 2069 // While we emit the source map for the node after skipping decorators and modifiers, 2070 // we need to emit the comments for the original range. 2071 setCommentRange(updated, node); 2072 setSourceMapRange(updated, moveRangePastDecorators(node)); 2073 } 2074 return updated; 2075 } 2076 2077 function visitSetAccessor(node: SetAccessorDeclaration) { 2078 if (!shouldEmitAccessorDeclaration(node)) { 2079 return undefined; 2080 } 2081 const updated = factory.updateSetAccessorDeclaration( 2082 node, 2083 /*decorators*/ undefined, 2084 visitNodes(node.modifiers, modifierVisitor, isModifier), 2085 visitPropertyNameOfClassElement(node), 2086 visitParameterList(node.parameters, visitor, context), 2087 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 2088 ); 2089 if (updated !== node) { 2090 // While we emit the source map for the node after skipping decorators and modifiers, 2091 // we need to emit the comments for the original range. 2092 setCommentRange(updated, node); 2093 setSourceMapRange(updated, moveRangePastDecorators(node)); 2094 } 2095 return updated; 2096 } 2097 2098 function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> { 2099 if (!shouldEmitFunctionLikeDeclaration(node)) { 2100 return factory.createNotEmittedStatement(node); 2101 } 2102 const updated = factory.updateFunctionDeclaration( 2103 node, 2104 /*decorators*/ undefined, 2105 visitNodes(node.modifiers, modifierVisitor, isModifier), 2106 node.asteriskToken, 2107 node.name, 2108 /*typeParameters*/ undefined, 2109 visitParameterList(node.parameters, visitor, context), 2110 /*type*/ undefined, 2111 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 2112 ); 2113 if (isExportOfNamespace(node)) { 2114 const statements: Statement[] = [updated]; 2115 addExportMemberAssignment(statements, node); 2116 return statements; 2117 } 2118 return updated; 2119 } 2120 2121 function visitFunctionExpression(node: FunctionExpression): Expression { 2122 if (!shouldEmitFunctionLikeDeclaration(node)) { 2123 return factory.createOmittedExpression(); 2124 } 2125 const updated = factory.updateFunctionExpression( 2126 node, 2127 visitNodes(node.modifiers, modifierVisitor, isModifier), 2128 node.asteriskToken, 2129 node.name, 2130 /*typeParameters*/ undefined, 2131 visitParameterList(node.parameters, visitor, context), 2132 /*type*/ undefined, 2133 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 2134 ); 2135 return updated; 2136 } 2137 2138 function visitArrowFunction(node: ArrowFunction) { 2139 const updated = factory.updateArrowFunction( 2140 node, 2141 visitNodes(node.modifiers, modifierVisitor, isModifier), 2142 /*typeParameters*/ undefined, 2143 visitParameterList(node.parameters, visitor, context), 2144 /*type*/ undefined, 2145 node.equalsGreaterThanToken, 2146 visitFunctionBody(node.body, visitor, context), 2147 ); 2148 return updated; 2149 } 2150 2151 function visitParameter(node: ParameterDeclaration) { 2152 if (parameterIsThisKeyword(node)) { 2153 return undefined; 2154 } 2155 2156 const updated = factory.updateParameterDeclaration( 2157 node, 2158 /*decorators*/ undefined, 2159 /*modifiers*/ undefined, 2160 node.dotDotDotToken, 2161 visitNode(node.name, visitor, isBindingName), 2162 /*questionToken*/ undefined, 2163 /*type*/ undefined, 2164 visitNode(node.initializer, visitor, isExpression) 2165 ); 2166 if (updated !== node) { 2167 // While we emit the source map for the node after skipping decorators and modifiers, 2168 // we need to emit the comments for the original range. 2169 setCommentRange(updated, node); 2170 setTextRange(updated, moveRangePastModifiers(node)); 2171 setSourceMapRange(updated, moveRangePastModifiers(node)); 2172 setEmitFlags(updated.name, EmitFlags.NoTrailingSourceMap); 2173 } 2174 return updated; 2175 } 2176 2177 function visitVariableStatement(node: VariableStatement): Statement | undefined { 2178 if (isExportOfNamespace(node)) { 2179 const variables = getInitializedVariables(node.declarationList); 2180 if (variables.length === 0) { 2181 // elide statement if there are no initialized variables. 2182 return undefined; 2183 } 2184 2185 return setTextRange( 2186 factory.createExpressionStatement( 2187 factory.inlineExpressions( 2188 map(variables, transformInitializedVariable) 2189 ) 2190 ), 2191 node 2192 ); 2193 } 2194 else { 2195 return visitEachChild(node, visitor, context); 2196 } 2197 } 2198 2199 function transformInitializedVariable(node: InitializedVariableDeclaration): Expression { 2200 const name = node.name; 2201 if (isBindingPattern(name)) { 2202 return flattenDestructuringAssignment( 2203 node, 2204 visitor, 2205 context, 2206 FlattenLevel.All, 2207 /*needsValue*/ false, 2208 createNamespaceExportExpression 2209 ); 2210 } 2211 else { 2212 return setTextRange( 2213 factory.createAssignment( 2214 getNamespaceMemberNameWithSourceMapsAndWithoutComments(name), 2215 visitNode(node.initializer, visitor, isExpression) 2216 ), 2217 /*location*/ node 2218 ); 2219 } 2220 } 2221 2222 function visitVariableDeclaration(node: VariableDeclaration) { 2223 return factory.updateVariableDeclaration( 2224 node, 2225 visitNode(node.name, visitor, isBindingName), 2226 /*exclamationToken*/ undefined, 2227 /*type*/ undefined, 2228 visitNode(node.initializer, visitor, isExpression)); 2229 } 2230 2231 function visitParenthesizedExpression(node: ParenthesizedExpression): Expression { 2232 const innerExpression = skipOuterExpressions(node.expression, ~OuterExpressionKinds.Assertions); 2233 if (isAssertionExpression(innerExpression)) { 2234 // Make sure we consider all nested cast expressions, e.g.: 2235 // (<any><number><any>-A).x; 2236 const expression = visitNode(node.expression, visitor, isExpression); 2237 2238 // We have an expression of the form: (<Type>SubExpr). Emitting this as (SubExpr) 2239 // is really not desirable. We would like to emit the subexpression as-is. Omitting 2240 // the parentheses, however, could cause change in the semantics of the generated 2241 // code if the casted expression has a lower precedence than the rest of the 2242 // expression. 2243 // 2244 // To preserve comments, we return a "PartiallyEmittedExpression" here which will 2245 // preserve the position information of the original expression. 2246 // 2247 // Due to the auto-parenthesization rules used by the visitor and factory functions 2248 // we can safely elide the parentheses here, as a new synthetic 2249 // ParenthesizedExpression will be inserted if we remove parentheses too 2250 // aggressively. 2251 // HOWEVER - if there are leading comments on the expression itself, to handle ASI 2252 // correctly for return and throw, we must keep the parenthesis 2253 if (length(getLeadingCommentRangesOfNode(expression, currentSourceFile))) { 2254 return factory.updateParenthesizedExpression(node, expression); 2255 } 2256 return factory.createPartiallyEmittedExpression(expression, node); 2257 } 2258 2259 return visitEachChild(node, visitor, context); 2260 } 2261 2262 function visitAssertionExpression(node: AssertionExpression): Expression { 2263 const expression = visitNode(node.expression, visitor, isExpression); 2264 return factory.createPartiallyEmittedExpression(expression, node); 2265 } 2266 2267 function visitNonNullExpression(node: NonNullExpression): Expression { 2268 const expression = visitNode(node.expression, visitor, isLeftHandSideExpression); 2269 return factory.createPartiallyEmittedExpression(expression, node); 2270 } 2271 2272 function visitCallExpression(node: CallExpression) { 2273 return factory.updateCallExpression( 2274 node, 2275 visitNode(node.expression, visitor, isExpression), 2276 /*typeArguments*/ undefined, 2277 visitNodes(node.arguments, visitor, isExpression)); 2278 } 2279 2280 function visitNewExpression(node: NewExpression) { 2281 return factory.updateNewExpression( 2282 node, 2283 visitNode(node.expression, visitor, isExpression), 2284 /*typeArguments*/ undefined, 2285 visitNodes(node.arguments, visitor, isExpression)); 2286 } 2287 2288 function visitTaggedTemplateExpression(node: TaggedTemplateExpression) { 2289 return factory.updateTaggedTemplateExpression( 2290 node, 2291 visitNode(node.tag, visitor, isExpression), 2292 /*typeArguments*/ undefined, 2293 visitNode(node.template, visitor, isExpression)); 2294 } 2295 2296 function visitJsxSelfClosingElement(node: JsxSelfClosingElement) { 2297 return factory.updateJsxSelfClosingElement( 2298 node, 2299 visitNode(node.tagName, visitor, isJsxTagNameExpression), 2300 /*typeArguments*/ undefined, 2301 visitNode(node.attributes, visitor, isJsxAttributes)); 2302 } 2303 2304 function visitJsxJsxOpeningElement(node: JsxOpeningElement) { 2305 return factory.updateJsxOpeningElement( 2306 node, 2307 visitNode(node.tagName, visitor, isJsxTagNameExpression), 2308 /*typeArguments*/ undefined, 2309 visitNode(node.attributes, visitor, isJsxAttributes)); 2310 } 2311 2312 /** 2313 * Determines whether to emit an enum declaration. 2314 * 2315 * @param node The enum declaration node. 2316 */ 2317 function shouldEmitEnumDeclaration(node: EnumDeclaration) { 2318 return !isEnumConst(node) 2319 || shouldPreserveConstEnums(compilerOptions); 2320 } 2321 2322 /** 2323 * Visits an enum declaration. 2324 * 2325 * This function will be called any time a TypeScript enum is encountered. 2326 * 2327 * @param node The enum declaration node. 2328 */ 2329 function visitEnumDeclaration(node: EnumDeclaration): VisitResult<Statement> { 2330 if (!shouldEmitEnumDeclaration(node)) { 2331 return factory.createNotEmittedStatement(node); 2332 } 2333 2334 const statements: Statement[] = []; 2335 2336 // We request to be advised when the printer is about to print this node. This allows 2337 // us to set up the correct state for later substitutions. 2338 let emitFlags = EmitFlags.AdviseOnEmitNode; 2339 2340 // If needed, we should emit a variable declaration for the enum. If we emit 2341 // a leading variable declaration, we should not emit leading comments for the 2342 // enum body. 2343 const varAdded = addVarForEnumOrModuleDeclaration(statements, node); 2344 if (varAdded) { 2345 // We should still emit the comments if we are emitting a system module. 2346 if (moduleKind !== ModuleKind.System || currentLexicalScope !== currentSourceFile) { 2347 emitFlags |= EmitFlags.NoLeadingComments; 2348 } 2349 } 2350 2351 // `parameterName` is the declaration name used inside of the enum. 2352 const parameterName = getNamespaceParameterName(node); 2353 2354 // `containerName` is the expression used inside of the enum for assignments. 2355 const containerName = getNamespaceContainerName(node); 2356 2357 // `exportName` is the expression used within this node's container for any exported references. 2358 const exportName = hasSyntacticModifier(node, ModifierFlags.Export) 2359 ? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true) 2360 : factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 2361 2362 // x || (x = {}) 2363 // exports.x || (exports.x = {}) 2364 let moduleArg = 2365 factory.createLogicalOr( 2366 exportName, 2367 factory.createAssignment( 2368 exportName, 2369 factory.createObjectLiteralExpression() 2370 ) 2371 ); 2372 2373 if (hasNamespaceQualifiedExportName(node)) { 2374 // `localName` is the expression used within this node's containing scope for any local references. 2375 const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 2376 2377 // x = (exports.x || (exports.x = {})) 2378 moduleArg = factory.createAssignment(localName, moduleArg); 2379 } 2380 2381 // (function (x) { 2382 // x[x["y"] = 0] = "y"; 2383 // ... 2384 // })(x || (x = {})); 2385 const enumStatement = factory.createExpressionStatement( 2386 factory.createCallExpression( 2387 factory.createFunctionExpression( 2388 /*modifiers*/ undefined, 2389 /*asteriskToken*/ undefined, 2390 /*name*/ undefined, 2391 /*typeParameters*/ undefined, 2392 [factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)], 2393 /*type*/ undefined, 2394 transformEnumBody(node, containerName) 2395 ), 2396 /*typeArguments*/ undefined, 2397 [moduleArg] 2398 ) 2399 ); 2400 2401 setOriginalNode(enumStatement, node); 2402 if (varAdded) { 2403 // If a variable was added, synthetic comments are emitted on it, not on the moduleStatement. 2404 setSyntheticLeadingComments(enumStatement, undefined); 2405 setSyntheticTrailingComments(enumStatement, undefined); 2406 } 2407 setTextRange(enumStatement, node); 2408 addEmitFlags(enumStatement, emitFlags); 2409 statements.push(enumStatement); 2410 2411 // Add a DeclarationMarker for the enum to preserve trailing comments and mark 2412 // the end of the declaration. 2413 statements.push(factory.createEndOfDeclarationMarker(node)); 2414 return statements; 2415 } 2416 2417 /** 2418 * Transforms the body of an enum declaration. 2419 * 2420 * @param node The enum declaration node. 2421 */ 2422 function transformEnumBody(node: EnumDeclaration, localName: Identifier): Block { 2423 const savedCurrentNamespaceLocalName = currentNamespaceContainerName; 2424 currentNamespaceContainerName = localName; 2425 2426 const statements: Statement[] = []; 2427 startLexicalEnvironment(); 2428 const members = map(node.members, transformEnumMember); 2429 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 2430 addRange(statements, members); 2431 2432 currentNamespaceContainerName = savedCurrentNamespaceLocalName; 2433 return factory.createBlock( 2434 setTextRange(factory.createNodeArray(statements), /*location*/ node.members), 2435 /*multiLine*/ true 2436 ); 2437 } 2438 2439 /** 2440 * Transforms an enum member into a statement. 2441 * 2442 * @param member The enum member node. 2443 */ 2444 function transformEnumMember(member: EnumMember): Statement { 2445 // enums don't support computed properties 2446 // we pass false as 'generateNameForComputedPropertyName' for a backward compatibility purposes 2447 // old emitter always generate 'expression' part of the name as-is. 2448 const name = getExpressionForPropertyName(member, /*generateNameForComputedPropertyName*/ false); 2449 const valueExpression = transformEnumMemberDeclarationValue(member); 2450 const innerAssignment = factory.createAssignment( 2451 factory.createElementAccessExpression( 2452 currentNamespaceContainerName, 2453 name 2454 ), 2455 valueExpression 2456 ); 2457 const outerAssignment = valueExpression.kind === SyntaxKind.StringLiteral ? 2458 innerAssignment : 2459 factory.createAssignment( 2460 factory.createElementAccessExpression( 2461 currentNamespaceContainerName, 2462 innerAssignment 2463 ), 2464 name 2465 ); 2466 return setTextRange( 2467 factory.createExpressionStatement( 2468 setTextRange( 2469 outerAssignment, 2470 member 2471 ) 2472 ), 2473 member 2474 ); 2475 } 2476 2477 /** 2478 * Transforms the value of an enum member. 2479 * 2480 * @param member The enum member node. 2481 */ 2482 function transformEnumMemberDeclarationValue(member: EnumMember): Expression { 2483 const value = resolver.getConstantValue(member); 2484 if (value !== undefined) { 2485 return typeof value === "string" ? factory.createStringLiteral(value) : factory.createNumericLiteral(value); 2486 } 2487 else { 2488 enableSubstitutionForNonQualifiedEnumMembers(); 2489 if (member.initializer) { 2490 return visitNode(member.initializer, visitor, isExpression); 2491 } 2492 else { 2493 return factory.createVoidZero(); 2494 } 2495 } 2496 } 2497 2498 /** 2499 * Determines whether to elide a module declaration. 2500 * 2501 * @param node The module declaration node. 2502 */ 2503 function shouldEmitModuleDeclaration(nodeIn: ModuleDeclaration) { 2504 const node = getParseTreeNode(nodeIn, isModuleDeclaration); 2505 if (!node) { 2506 // If we can't find a parse tree node, assume the node is instantiated. 2507 return true; 2508 } 2509 return isInstantiatedModule(node, shouldPreserveConstEnums(compilerOptions)); 2510 } 2511 2512 /** 2513 * Determines whether an exported declaration will have a qualified export name (e.g. `f.x` 2514 * or `exports.x`). 2515 */ 2516 function hasNamespaceQualifiedExportName(node: Node) { 2517 return isExportOfNamespace(node) 2518 || (isExternalModuleExport(node) 2519 && moduleKind !== ModuleKind.ES2015 2520 && moduleKind !== ModuleKind.ES2020 2521 && moduleKind !== ModuleKind.ESNext 2522 && moduleKind !== ModuleKind.System); 2523 } 2524 2525 /** 2526 * Records that a declaration was emitted in the current scope, if it was the first 2527 * declaration for the provided symbol. 2528 */ 2529 function recordEmittedDeclarationInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration) { 2530 if (!currentScopeFirstDeclarationsOfName) { 2531 currentScopeFirstDeclarationsOfName = new Map(); 2532 } 2533 2534 const name = declaredNameInScope(node); 2535 if (!currentScopeFirstDeclarationsOfName.has(name)) { 2536 currentScopeFirstDeclarationsOfName.set(name, node); 2537 } 2538 } 2539 2540 /** 2541 * Determines whether a declaration is the first declaration with 2542 * the same name emitted in the current scope. 2543 */ 2544 function isFirstEmittedDeclarationInScope(node: ModuleDeclaration | EnumDeclaration) { 2545 if (currentScopeFirstDeclarationsOfName) { 2546 const name = declaredNameInScope(node); 2547 return currentScopeFirstDeclarationsOfName.get(name) === node; 2548 } 2549 return true; 2550 } 2551 2552 function declaredNameInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration): __String { 2553 Debug.assertNode(node.name, isIdentifier); 2554 return node.name.escapedText; 2555 } 2556 2557 /** 2558 * Adds a leading VariableStatement for a enum or module declaration. 2559 */ 2560 function addVarForEnumOrModuleDeclaration(statements: Statement[], node: ModuleDeclaration | EnumDeclaration) { 2561 // Emit a variable statement for the module. We emit top-level enums as a `var` 2562 // declaration to avoid static errors in global scripts scripts due to redeclaration. 2563 // enums in any other scope are emitted as a `let` declaration. 2564 const statement = factory.createVariableStatement( 2565 visitNodes(node.modifiers, modifierVisitor, isModifier), 2566 factory.createVariableDeclarationList([ 2567 factory.createVariableDeclaration( 2568 factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) 2569 ) 2570 ], currentLexicalScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let) 2571 ); 2572 2573 setOriginalNode(statement, node); 2574 2575 recordEmittedDeclarationInScope(node); 2576 if (isFirstEmittedDeclarationInScope(node)) { 2577 // Adjust the source map emit to match the old emitter. 2578 if (node.kind === SyntaxKind.EnumDeclaration) { 2579 setSourceMapRange(statement.declarationList, node); 2580 } 2581 else { 2582 setSourceMapRange(statement, node); 2583 } 2584 2585 // Trailing comments for module declaration should be emitted after the function closure 2586 // instead of the variable statement: 2587 // 2588 // /** Module comment*/ 2589 // module m1 { 2590 // function foo4Export() { 2591 // } 2592 // } // trailing comment module 2593 // 2594 // Should emit: 2595 // 2596 // /** Module comment*/ 2597 // var m1; 2598 // (function (m1) { 2599 // function foo4Export() { 2600 // } 2601 // })(m1 || (m1 = {})); // trailing comment module 2602 // 2603 setCommentRange(statement, node); 2604 addEmitFlags(statement, EmitFlags.NoTrailingComments | EmitFlags.HasEndOfDeclarationMarker); 2605 statements.push(statement); 2606 return true; 2607 } 2608 else { 2609 // For an EnumDeclaration or ModuleDeclaration that merges with a preceeding 2610 // declaration we do not emit a leading variable declaration. To preserve the 2611 // begin/end semantics of the declararation and to properly handle exports 2612 // we wrap the leading variable declaration in a `MergeDeclarationMarker`. 2613 const mergeMarker = factory.createMergeDeclarationMarker(statement); 2614 setEmitFlags(mergeMarker, EmitFlags.NoComments | EmitFlags.HasEndOfDeclarationMarker); 2615 statements.push(mergeMarker); 2616 return false; 2617 } 2618 } 2619 2620 /** 2621 * Visits a module declaration node. 2622 * 2623 * This function will be called any time a TypeScript namespace (ModuleDeclaration) is encountered. 2624 * 2625 * @param node The module declaration node. 2626 */ 2627 function visitModuleDeclaration(node: ModuleDeclaration): VisitResult<Statement> { 2628 if (!shouldEmitModuleDeclaration(node)) { 2629 return factory.createNotEmittedStatement(node); 2630 } 2631 2632 Debug.assertNode(node.name, isIdentifier, "A TypeScript namespace should have an Identifier name."); 2633 enableSubstitutionForNamespaceExports(); 2634 2635 const statements: Statement[] = []; 2636 2637 // We request to be advised when the printer is about to print this node. This allows 2638 // us to set up the correct state for later substitutions. 2639 let emitFlags = EmitFlags.AdviseOnEmitNode; 2640 2641 // If needed, we should emit a variable declaration for the module. If we emit 2642 // a leading variable declaration, we should not emit leading comments for the 2643 // module body. 2644 const varAdded = addVarForEnumOrModuleDeclaration(statements, node); 2645 if (varAdded) { 2646 // We should still emit the comments if we are emitting a system module. 2647 if (moduleKind !== ModuleKind.System || currentLexicalScope !== currentSourceFile) { 2648 emitFlags |= EmitFlags.NoLeadingComments; 2649 } 2650 } 2651 2652 // `parameterName` is the declaration name used inside of the namespace. 2653 const parameterName = getNamespaceParameterName(node); 2654 2655 // `containerName` is the expression used inside of the namespace for exports. 2656 const containerName = getNamespaceContainerName(node); 2657 2658 // `exportName` is the expression used within this node's container for any exported references. 2659 const exportName = hasSyntacticModifier(node, ModifierFlags.Export) 2660 ? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true) 2661 : factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 2662 2663 // x || (x = {}) 2664 // exports.x || (exports.x = {}) 2665 let moduleArg = 2666 factory.createLogicalOr( 2667 exportName, 2668 factory.createAssignment( 2669 exportName, 2670 factory.createObjectLiteralExpression() 2671 ) 2672 ); 2673 2674 if (hasNamespaceQualifiedExportName(node)) { 2675 // `localName` is the expression used within this node's containing scope for any local references. 2676 const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 2677 2678 // x = (exports.x || (exports.x = {})) 2679 moduleArg = factory.createAssignment(localName, moduleArg); 2680 } 2681 2682 // (function (x_1) { 2683 // x_1.y = ...; 2684 // })(x || (x = {})); 2685 const moduleStatement = factory.createExpressionStatement( 2686 factory.createCallExpression( 2687 factory.createFunctionExpression( 2688 /*modifiers*/ undefined, 2689 /*asteriskToken*/ undefined, 2690 /*name*/ undefined, 2691 /*typeParameters*/ undefined, 2692 [factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)], 2693 /*type*/ undefined, 2694 transformModuleBody(node, containerName) 2695 ), 2696 /*typeArguments*/ undefined, 2697 [moduleArg] 2698 ) 2699 ); 2700 2701 setOriginalNode(moduleStatement, node); 2702 if (varAdded) { 2703 // If a variable was added, synthetic comments are emitted on it, not on the moduleStatement. 2704 setSyntheticLeadingComments(moduleStatement, undefined); 2705 setSyntheticTrailingComments(moduleStatement, undefined); 2706 } 2707 setTextRange(moduleStatement, node); 2708 addEmitFlags(moduleStatement, emitFlags); 2709 statements.push(moduleStatement); 2710 2711 // Add a DeclarationMarker for the namespace to preserve trailing comments and mark 2712 // the end of the declaration. 2713 statements.push(factory.createEndOfDeclarationMarker(node)); 2714 return statements; 2715 } 2716 2717 /** 2718 * Transforms the body of a module declaration. 2719 * 2720 * @param node The module declaration node. 2721 */ 2722 function transformModuleBody(node: ModuleDeclaration, namespaceLocalName: Identifier): Block { 2723 const savedCurrentNamespaceContainerName = currentNamespaceContainerName; 2724 const savedCurrentNamespace = currentNamespace; 2725 const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; 2726 currentNamespaceContainerName = namespaceLocalName; 2727 currentNamespace = node; 2728 currentScopeFirstDeclarationsOfName = undefined; 2729 2730 const statements: Statement[] = []; 2731 startLexicalEnvironment(); 2732 2733 let statementsLocation: TextRange | undefined; 2734 let blockLocation: TextRange | undefined; 2735 if (node.body) { 2736 if (node.body.kind === SyntaxKind.ModuleBlock) { 2737 saveStateAndInvoke(node.body, body => addRange(statements, visitNodes((<ModuleBlock>body).statements, namespaceElementVisitor, isStatement))); 2738 statementsLocation = node.body.statements; 2739 blockLocation = node.body; 2740 } 2741 else { 2742 const result = visitModuleDeclaration(<ModuleDeclaration>node.body); 2743 if (result) { 2744 if (isArray(result)) { 2745 addRange(statements, result); 2746 } 2747 else { 2748 statements.push(result); 2749 } 2750 } 2751 2752 const moduleBlock = <ModuleBlock>getInnerMostModuleDeclarationFromDottedModule(node)!.body; 2753 statementsLocation = moveRangePos(moduleBlock.statements, -1); 2754 } 2755 } 2756 2757 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 2758 currentNamespaceContainerName = savedCurrentNamespaceContainerName; 2759 currentNamespace = savedCurrentNamespace; 2760 currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; 2761 2762 const block = factory.createBlock( 2763 setTextRange( 2764 factory.createNodeArray(statements), 2765 /*location*/ statementsLocation 2766 ), 2767 /*multiLine*/ true 2768 ); 2769 setTextRange(block, blockLocation); 2770 2771 // namespace hello.hi.world { 2772 // function foo() {} 2773 // 2774 // // TODO, blah 2775 // } 2776 // 2777 // should be emitted as 2778 // 2779 // var hello; 2780 // (function (hello) { 2781 // var hi; 2782 // (function (hi) { 2783 // var world; 2784 // (function (world) { 2785 // function foo() { } 2786 // // TODO, blah 2787 // })(world = hi.world || (hi.world = {})); 2788 // })(hi = hello.hi || (hello.hi = {})); 2789 // })(hello || (hello = {})); 2790 // We only want to emit comment on the namespace which contains block body itself, not the containing namespaces. 2791 if (!node.body || node.body.kind !== SyntaxKind.ModuleBlock) { 2792 setEmitFlags(block, getEmitFlags(block) | EmitFlags.NoComments); 2793 } 2794 return block; 2795 } 2796 2797 function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration | undefined { 2798 if (moduleDeclaration.body!.kind === SyntaxKind.ModuleDeclaration) { 2799 const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(<ModuleDeclaration>moduleDeclaration.body); 2800 return recursiveInnerModule || <ModuleDeclaration>moduleDeclaration.body; 2801 } 2802 } 2803 2804 /** 2805 * Visits an import declaration, eliding it if it is not referenced and `importsNotUsedAsValues` is not 'preserve'. 2806 * 2807 * @param node The import declaration node. 2808 */ 2809 function visitImportDeclaration(node: ImportDeclaration): VisitResult<Statement> { 2810 if (!node.importClause) { 2811 // Do not elide a side-effect only import declaration. 2812 // import "foo"; 2813 return node; 2814 } 2815 if (node.importClause.isTypeOnly) { 2816 // Always elide type-only imports 2817 return undefined; 2818 } 2819 2820 // Elide the declaration if the import clause was elided. 2821 const importClause = visitNode(node.importClause, visitImportClause, isImportClause); 2822 return importClause || 2823 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 2824 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error 2825 ? factory.updateImportDeclaration( 2826 node, 2827 /*decorators*/ undefined, 2828 /*modifiers*/ undefined, 2829 importClause, 2830 node.moduleSpecifier) 2831 : undefined; 2832 } 2833 2834 /** 2835 * Visits an import clause, eliding it if it is not referenced. 2836 * 2837 * @param node The import clause node. 2838 */ 2839 function visitImportClause(node: ImportClause): VisitResult<ImportClause> { 2840 if (node.isTypeOnly) { 2841 return undefined; 2842 } 2843 // Elide the import clause if we elide both its name and its named bindings. 2844 const name = resolver.isReferencedAliasDeclaration(node) ? node.name : undefined; 2845 const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings); 2846 return (name || namedBindings) ? factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings) : undefined; 2847 } 2848 2849 /** 2850 * Visits named import bindings, eliding it if it is not referenced. 2851 * 2852 * @param node The named import bindings node. 2853 */ 2854 function visitNamedImportBindings(node: NamedImportBindings): VisitResult<NamedImportBindings> { 2855 if (node.kind === SyntaxKind.NamespaceImport) { 2856 // Elide a namespace import if it is not referenced. 2857 return resolver.isReferencedAliasDeclaration(node) ? node : undefined; 2858 } 2859 else { 2860 // Elide named imports if all of its import specifiers are elided. 2861 const elements = visitNodes(node.elements, visitImportSpecifier, isImportSpecifier); 2862 return some(elements) ? factory.updateNamedImports(node, elements) : undefined; 2863 } 2864 } 2865 2866 /** 2867 * Visits an import specifier, eliding it if it is not referenced. 2868 * 2869 * @param node The import specifier node. 2870 */ 2871 function visitImportSpecifier(node: ImportSpecifier): VisitResult<ImportSpecifier> { 2872 // Elide an import specifier if it is not referenced. 2873 return resolver.isReferencedAliasDeclaration(node) ? node : undefined; 2874 } 2875 2876 /** 2877 * Visits an export assignment, eliding it if it does not contain a clause that resolves 2878 * to a value. 2879 * 2880 * @param node The export assignment node. 2881 */ 2882 function visitExportAssignment(node: ExportAssignment): VisitResult<Statement> { 2883 // Elide the export assignment if it does not reference a value. 2884 return resolver.isValueAliasDeclaration(node) 2885 ? visitEachChild(node, visitor, context) 2886 : undefined; 2887 } 2888 2889 /** 2890 * Visits an export declaration, eliding it if it does not contain a clause that resolves 2891 * to a value. 2892 * 2893 * @param node The export declaration node. 2894 */ 2895 function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> { 2896 if (node.isTypeOnly) { 2897 return undefined; 2898 } 2899 2900 if (!node.exportClause || isNamespaceExport(node.exportClause)) { 2901 // never elide `export <whatever> from <whereever>` declarations - 2902 // they should be kept for sideffects/untyped exports, even when the 2903 // type checker doesn't know about any exports 2904 return node; 2905 } 2906 2907 if (!resolver.isValueAliasDeclaration(node)) { 2908 // Elide the export declaration if it does not export a value. 2909 return undefined; 2910 } 2911 2912 // Elide the export declaration if all of its named exports are elided. 2913 const exportClause = visitNode(node.exportClause, visitNamedExportBindings, isNamedExportBindings); 2914 return exportClause 2915 ? factory.updateExportDeclaration( 2916 node, 2917 /*decorators*/ undefined, 2918 /*modifiers*/ undefined, 2919 node.isTypeOnly, 2920 exportClause, 2921 node.moduleSpecifier) 2922 : undefined; 2923 } 2924 2925 /** 2926 * Visits named exports, eliding it if it does not contain an export specifier that 2927 * resolves to a value. 2928 * 2929 * @param node The named exports node. 2930 */ 2931 function visitNamedExports(node: NamedExports): VisitResult<NamedExports> { 2932 // Elide the named exports if all of its export specifiers were elided. 2933 const elements = visitNodes(node.elements, visitExportSpecifier, isExportSpecifier); 2934 return some(elements) ? factory.updateNamedExports(node, elements) : undefined; 2935 } 2936 2937 function visitNamespaceExports(node: NamespaceExport): VisitResult<NamespaceExport> { 2938 return factory.updateNamespaceExport(node, visitNode(node.name, visitor, isIdentifier)); 2939 } 2940 2941 function visitNamedExportBindings(node: NamedExportBindings): VisitResult<NamedExportBindings> { 2942 return isNamespaceExport(node) ? visitNamespaceExports(node) : visitNamedExports(node); 2943 } 2944 2945 /** 2946 * Visits an export specifier, eliding it if it does not resolve to a value. 2947 * 2948 * @param node The export specifier node. 2949 */ 2950 function visitExportSpecifier(node: ExportSpecifier): VisitResult<ExportSpecifier> { 2951 // Elide an export specifier if it does not reference a value. 2952 return resolver.isValueAliasDeclaration(node) ? node : undefined; 2953 } 2954 2955 /** 2956 * Determines whether to emit an import equals declaration. 2957 * 2958 * @param node The import equals declaration node. 2959 */ 2960 function shouldEmitImportEqualsDeclaration(node: ImportEqualsDeclaration) { 2961 // preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when 2962 // - current file is not external module 2963 // - import declaration is top level and target is value imported by entity name 2964 return resolver.isReferencedAliasDeclaration(node) 2965 || (!isExternalModule(currentSourceFile) 2966 && resolver.isTopLevelValueImportEqualsWithEntityName(node)); 2967 } 2968 2969 /** 2970 * Visits an import equals declaration. 2971 * 2972 * @param node The import equals declaration node. 2973 */ 2974 function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> { 2975 // Always elide type-only imports 2976 if (node.isTypeOnly) { 2977 return undefined; 2978 } 2979 2980 if (isExternalModuleImportEqualsDeclaration(node)) { 2981 const isReferenced = resolver.isReferencedAliasDeclaration(node); 2982 // If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'. 2983 if (!isReferenced && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) { 2984 return setOriginalNode( 2985 setTextRange( 2986 factory.createImportDeclaration( 2987 /*decorators*/ undefined, 2988 /*modifiers*/ undefined, 2989 /*importClause*/ undefined, 2990 node.moduleReference.expression, 2991 ), 2992 node, 2993 ), 2994 node, 2995 ); 2996 } 2997 2998 return isReferenced ? visitEachChild(node, visitor, context) : undefined; 2999 } 3000 3001 if (!shouldEmitImportEqualsDeclaration(node)) { 3002 return undefined; 3003 } 3004 3005 const moduleReference = createExpressionFromEntityName(factory, <EntityName>node.moduleReference); 3006 setEmitFlags(moduleReference, EmitFlags.NoComments | EmitFlags.NoNestedComments); 3007 3008 if (isNamedExternalModuleExport(node) || !isExportOfNamespace(node)) { 3009 // export var ${name} = ${moduleReference}; 3010 // var ${name} = ${moduleReference}; 3011 return setOriginalNode( 3012 setTextRange( 3013 factory.createVariableStatement( 3014 visitNodes(node.modifiers, modifierVisitor, isModifier), 3015 factory.createVariableDeclarationList([ 3016 setOriginalNode( 3017 factory.createVariableDeclaration( 3018 node.name, 3019 /*exclamationToken*/ undefined, 3020 /*type*/ undefined, 3021 moduleReference 3022 ), 3023 node 3024 ) 3025 ]) 3026 ), 3027 node 3028 ), 3029 node 3030 ); 3031 } 3032 else { 3033 // exports.${name} = ${moduleReference}; 3034 return setOriginalNode( 3035 createNamespaceExport( 3036 node.name, 3037 moduleReference, 3038 node 3039 ), 3040 node 3041 ); 3042 } 3043 } 3044 3045 /** 3046 * Gets a value indicating whether the node is exported from a namespace. 3047 * 3048 * @param node The node to test. 3049 */ 3050 function isExportOfNamespace(node: Node) { 3051 return currentNamespace !== undefined && hasSyntacticModifier(node, ModifierFlags.Export); 3052 } 3053 3054 /** 3055 * Gets a value indicating whether the node is exported from an external module. 3056 * 3057 * @param node The node to test. 3058 */ 3059 function isExternalModuleExport(node: Node) { 3060 return currentNamespace === undefined && hasSyntacticModifier(node, ModifierFlags.Export); 3061 } 3062 3063 /** 3064 * Gets a value indicating whether the node is a named export from an external module. 3065 * 3066 * @param node The node to test. 3067 */ 3068 function isNamedExternalModuleExport(node: Node) { 3069 return isExternalModuleExport(node) 3070 && !hasSyntacticModifier(node, ModifierFlags.Default); 3071 } 3072 3073 /** 3074 * Gets a value indicating whether the node is the default export of an external module. 3075 * 3076 * @param node The node to test. 3077 */ 3078 function isDefaultExternalModuleExport(node: Node) { 3079 return isExternalModuleExport(node) 3080 && hasSyntacticModifier(node, ModifierFlags.Default); 3081 } 3082 3083 /** 3084 * Creates a statement for the provided expression. This is used in calls to `map`. 3085 */ 3086 function expressionToStatement(expression: Expression) { 3087 return factory.createExpressionStatement(expression); 3088 } 3089 3090 function addExportMemberAssignment(statements: Statement[], node: ClassDeclaration | FunctionDeclaration) { 3091 const expression = factory.createAssignment( 3092 factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true), 3093 factory.getLocalName(node) 3094 ); 3095 setSourceMapRange(expression, createRange(node.name ? node.name.pos : node.pos, node.end)); 3096 3097 const statement = factory.createExpressionStatement(expression); 3098 setSourceMapRange(statement, createRange(-1, node.end)); 3099 statements.push(statement); 3100 } 3101 3102 function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) { 3103 return setTextRange( 3104 factory.createExpressionStatement( 3105 factory.createAssignment( 3106 factory.getNamespaceMemberName(currentNamespaceContainerName, exportName, /*allowComments*/ false, /*allowSourceMaps*/ true), 3107 exportValue 3108 ) 3109 ), 3110 location 3111 ); 3112 } 3113 3114 function createNamespaceExportExpression(exportName: Identifier, exportValue: Expression, location?: TextRange) { 3115 return setTextRange(factory.createAssignment(getNamespaceMemberNameWithSourceMapsAndWithoutComments(exportName), exportValue), location); 3116 } 3117 3118 function getNamespaceMemberNameWithSourceMapsAndWithoutComments(name: Identifier) { 3119 return factory.getNamespaceMemberName(currentNamespaceContainerName, name, /*allowComments*/ false, /*allowSourceMaps*/ true); 3120 } 3121 3122 /** 3123 * Gets the declaration name used inside of a namespace or enum. 3124 */ 3125 function getNamespaceParameterName(node: ModuleDeclaration | EnumDeclaration) { 3126 const name = factory.getGeneratedNameForNode(node); 3127 setSourceMapRange(name, node.name); 3128 return name; 3129 } 3130 3131 /** 3132 * Gets the expression used to refer to a namespace or enum within the body 3133 * of its declaration. 3134 */ 3135 function getNamespaceContainerName(node: ModuleDeclaration | EnumDeclaration) { 3136 return factory.getGeneratedNameForNode(node); 3137 } 3138 3139 /** 3140 * Gets a local alias for a class declaration if it is a decorated class with an internal 3141 * reference to the static side of the class. This is necessary to avoid issues with 3142 * double-binding semantics for the class name. 3143 */ 3144 function getClassAliasIfNeeded(node: ClassDeclaration) { 3145 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { 3146 enableSubstitutionForClassAliases(); 3147 const classAlias = factory.createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? idText(node.name) : "default"); 3148 classAliases[getOriginalNodeId(node)] = classAlias; 3149 hoistVariableDeclaration(classAlias); 3150 return classAlias; 3151 } 3152 } 3153 3154 function getClassPrototype(node: ClassExpression | ClassDeclaration) { 3155 return factory.createPropertyAccessExpression(factory.getDeclarationName(node), "prototype"); 3156 } 3157 3158 function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { 3159 return hasSyntacticModifier(member, ModifierFlags.Static) 3160 ? factory.getDeclarationName(node) 3161 : getClassPrototype(node); 3162 } 3163 3164 function enableSubstitutionForNonQualifiedEnumMembers() { 3165 if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers) === 0) { 3166 enabledSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; 3167 context.enableSubstitution(SyntaxKind.Identifier); 3168 } 3169 } 3170 3171 function enableSubstitutionForClassAliases() { 3172 if ((enabledSubstitutions & TypeScriptSubstitutionFlags.ClassAliases) === 0) { 3173 enabledSubstitutions |= TypeScriptSubstitutionFlags.ClassAliases; 3174 3175 // We need to enable substitutions for identifiers. This allows us to 3176 // substitute class names inside of a class declaration. 3177 context.enableSubstitution(SyntaxKind.Identifier); 3178 3179 // Keep track of class aliases. 3180 classAliases = []; 3181 } 3182 } 3183 3184 function enableSubstitutionForNamespaceExports() { 3185 if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) { 3186 enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; 3187 3188 // We need to enable substitutions for identifiers and shorthand property assignments. This allows us to 3189 // substitute the names of exported members of a namespace. 3190 context.enableSubstitution(SyntaxKind.Identifier); 3191 context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); 3192 3193 // We need to be notified when entering and exiting namespaces. 3194 context.enableEmitNotification(SyntaxKind.ModuleDeclaration); 3195 } 3196 } 3197 3198 function isTransformedModuleDeclaration(node: Node): boolean { 3199 return getOriginalNode(node).kind === SyntaxKind.ModuleDeclaration; 3200 } 3201 3202 function isTransformedEnumDeclaration(node: Node): boolean { 3203 return getOriginalNode(node).kind === SyntaxKind.EnumDeclaration; 3204 } 3205 3206 /** 3207 * Hook for node emit. 3208 * 3209 * @param hint A hint as to the intended usage of the node. 3210 * @param node The node to emit. 3211 * @param emit A callback used to emit the node in the printer. 3212 */ 3213 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { 3214 const savedApplicableSubstitutions = applicableSubstitutions; 3215 const savedCurrentSourceFile = currentSourceFile; 3216 3217 if (isSourceFile(node)) { 3218 currentSourceFile = node; 3219 } 3220 3221 if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) { 3222 applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; 3223 } 3224 3225 if (enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) { 3226 applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; 3227 } 3228 3229 previousOnEmitNode(hint, node, emitCallback); 3230 3231 applicableSubstitutions = savedApplicableSubstitutions; 3232 currentSourceFile = savedCurrentSourceFile; 3233 } 3234 3235 /** 3236 * Hooks node substitutions. 3237 * 3238 * @param hint A hint as to the intended usage of the node. 3239 * @param node The node to substitute. 3240 */ 3241 function onSubstituteNode(hint: EmitHint, node: Node) { 3242 node = previousOnSubstituteNode(hint, node); 3243 if (hint === EmitHint.Expression) { 3244 return substituteExpression(<Expression>node); 3245 } 3246 else if (isShorthandPropertyAssignment(node)) { 3247 return substituteShorthandPropertyAssignment(node); 3248 } 3249 3250 return node; 3251 } 3252 3253 function substituteShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElementLike { 3254 if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) { 3255 const name = node.name; 3256 const exportedName = trySubstituteNamespaceExportedName(name); 3257 if (exportedName) { 3258 // A shorthand property with an assignment initializer is probably part of a 3259 // destructuring assignment 3260 if (node.objectAssignmentInitializer) { 3261 const initializer = factory.createAssignment(exportedName, node.objectAssignmentInitializer); 3262 return setTextRange(factory.createPropertyAssignment(name, initializer), node); 3263 } 3264 return setTextRange(factory.createPropertyAssignment(name, exportedName), node); 3265 } 3266 } 3267 return node; 3268 } 3269 3270 function substituteExpression(node: Expression) { 3271 switch (node.kind) { 3272 case SyntaxKind.Identifier: 3273 return substituteExpressionIdentifier(<Identifier>node); 3274 case SyntaxKind.PropertyAccessExpression: 3275 return substitutePropertyAccessExpression(<PropertyAccessExpression>node); 3276 case SyntaxKind.ElementAccessExpression: 3277 return substituteElementAccessExpression(<ElementAccessExpression>node); 3278 } 3279 3280 return node; 3281 } 3282 3283 function substituteExpressionIdentifier(node: Identifier): Expression { 3284 return trySubstituteClassAlias(node) 3285 || trySubstituteNamespaceExportedName(node) 3286 || node; 3287 } 3288 3289 function trySubstituteClassAlias(node: Identifier): Expression | undefined { 3290 if (enabledSubstitutions & TypeScriptSubstitutionFlags.ClassAliases) { 3291 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ConstructorReferenceInClass) { 3292 // Due to the emit for class decorators, any reference to the class from inside of the class body 3293 // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind 3294 // behavior of class names in ES6. 3295 // Also, when emitting statics for class expressions, we must substitute a class alias for 3296 // constructor references in static property initializers. 3297 const declaration = resolver.getReferencedValueDeclaration(node); 3298 if (declaration) { 3299 const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 3300 if (classAlias) { 3301 const clone = factory.cloneNode(classAlias); 3302 setSourceMapRange(clone, node); 3303 setCommentRange(clone, node); 3304 return clone; 3305 } 3306 } 3307 } 3308 } 3309 3310 return undefined; 3311 } 3312 3313 function trySubstituteNamespaceExportedName(node: Identifier): Expression | undefined { 3314 // If this is explicitly a local name, do not substitute. 3315 if (enabledSubstitutions & applicableSubstitutions && !isGeneratedIdentifier(node) && !isLocalName(node)) { 3316 // If we are nested within a namespace declaration, we may need to qualifiy 3317 // an identifier that is exported from a merged namespace. 3318 const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false); 3319 if (container && container.kind !== SyntaxKind.SourceFile) { 3320 const substitute = 3321 (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || 3322 (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration); 3323 if (substitute) { 3324 return setTextRange( 3325 factory.createPropertyAccessExpression(factory.getGeneratedNameForNode(container), node), 3326 /*location*/ node 3327 ); 3328 } 3329 } 3330 } 3331 3332 return undefined; 3333 } 3334 3335 function substitutePropertyAccessExpression(node: PropertyAccessExpression) { 3336 return substituteConstantValue(node); 3337 } 3338 3339 function substituteElementAccessExpression(node: ElementAccessExpression) { 3340 return substituteConstantValue(node); 3341 } 3342 3343 function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression { 3344 const constantValue = tryGetConstEnumValue(node); 3345 if (constantValue !== undefined) { 3346 // track the constant value on the node for the printer in needsDotDotForPropertyAccess 3347 setConstantValue(node, constantValue); 3348 3349 const substitute = typeof constantValue === "string" ? factory.createStringLiteral(constantValue) : factory.createNumericLiteral(constantValue); 3350 if (!compilerOptions.removeComments) { 3351 const originalNode = getOriginalNode(node, isAccessExpression); 3352 const propertyName = isPropertyAccessExpression(originalNode) 3353 ? declarationNameToString(originalNode.name) 3354 : getTextOfNode(originalNode.argumentExpression); 3355 3356 addSyntheticTrailingComment(substitute, SyntaxKind.MultiLineCommentTrivia, ` ${propertyName} `); 3357 } 3358 3359 return substitute; 3360 } 3361 3362 return node; 3363 } 3364 3365 function tryGetConstEnumValue(node: Node): string | number | undefined { 3366 if (compilerOptions.isolatedModules) { 3367 return undefined; 3368 } 3369 3370 return isPropertyAccessExpression(node) || isElementAccessExpression(node) ? resolver.getConstantValue(node) : undefined; 3371 } 3372 } 3373} 3374