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 namespace exports. */ 10 NamespaceExports = 1 << 1, 11 /* Enables substitutions for unqualified enum members */ 12 NonQualifiedEnumMembers = 1 << 3 13 } 14 15 const enum ClassFacts { 16 None = 0, 17 HasStaticInitializedProperties = 1 << 0, 18 HasConstructorDecorators = 1 << 1, 19 HasMemberDecorators = 1 << 2, 20 IsExportOfNamespace = 1 << 3, 21 IsNamedExternalExport = 1 << 4, 22 IsDefaultExternalExport = 1 << 5, 23 IsDerivedClass = 1 << 6, 24 UseImmediatelyInvokedFunctionExpression = 1 << 7, 25 26 HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators, 27 NeedsName = HasStaticInitializedProperties | HasMemberDecorators, 28 MayNeedImmediatelyInvokedFunctionExpression = HasAnyDecorators | HasStaticInitializedProperties, 29 IsExported = IsExportOfNamespace | IsDefaultExternalExport | IsNamedExternalExport, 30 } 31 32 export function transformTypeScript(context: TransformationContext) { 33 const { 34 factory, 35 getEmitHelperFactory: emitHelpers, 36 startLexicalEnvironment, 37 resumeLexicalEnvironment, 38 endLexicalEnvironment, 39 hoistVariableDeclaration, 40 } = context; 41 42 const resolver = context.getEmitResolver(); 43 const compilerOptions = context.getCompilerOptions(); 44 const languageVersion = getEmitScriptTarget(compilerOptions); 45 const moduleKind = getEmitModuleKind(compilerOptions); 46 const typeSerializer = compilerOptions.emitDecoratorMetadata ? createRuntimeTypeSerializer(context) : undefined; 47 48 // Save the previous transformation hooks. 49 const previousOnEmitNode = context.onEmitNode; 50 const previousOnSubstituteNode = context.onSubstituteNode; 51 52 // Set new transformation hooks. 53 context.onEmitNode = onEmitNode; 54 context.onSubstituteNode = onSubstituteNode; 55 56 // Enable substitution for property/element access to emit const enum values. 57 context.enableSubstitution(SyntaxKind.PropertyAccessExpression); 58 context.enableSubstitution(SyntaxKind.ElementAccessExpression); 59 60 // These variables contain state that changes as we descend into the tree. 61 let currentSourceFile: SourceFile; 62 let currentNamespace: ModuleDeclaration; 63 let currentNamespaceContainerName: Identifier; 64 let currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock; 65 let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap<Node> | undefined; 66 let currentClassHasParameterProperties: boolean | undefined; 67 68 /** 69 * Keeps track of whether expression substitution has been enabled for specific edge cases. 70 * They are persisted between each SourceFile transformation and should not be reset. 71 */ 72 let enabledSubstitutions: TypeScriptSubstitutionFlags; 73 74 /** 75 * Keeps track of whether we are within any containing namespaces when performing 76 * just-in-time substitution while printing an expression identifier. 77 */ 78 let applicableSubstitutions: TypeScriptSubstitutionFlags; 79 80 return transformSourceFileOrBundle; 81 82 function transformSourceFileOrBundle(node: SourceFile | Bundle) { 83 if (node.kind === SyntaxKind.Bundle) { 84 return transformBundle(node); 85 } 86 return transformSourceFile(node); 87 } 88 89 function transformBundle(node: Bundle) { 90 return factory.createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => { 91 if (prepend.kind === SyntaxKind.InputFiles) { 92 return createUnparsedSourceFile(prepend, "js"); 93 } 94 return prepend; 95 })); 96 } 97 98 /** 99 * Transform TypeScript-specific syntax in a SourceFile. 100 * 101 * @param node A SourceFile node. 102 */ 103 function transformSourceFile(node: SourceFile) { 104 if (node.isDeclarationFile) { 105 return node; 106 } 107 108 currentSourceFile = node; 109 110 const visited = saveStateAndInvoke(node, visitSourceFile); 111 addEmitHelpers(visited, context.readEmitHelpers()); 112 113 currentSourceFile = undefined!; 114 return visited; 115 } 116 117 /** 118 * Visits a node, saving and restoring state variables on the stack. 119 * 120 * @param node The node to visit. 121 */ 122 function saveStateAndInvoke<T>(node: Node, f: (node: Node) => T): T { 123 // Save state 124 const savedCurrentScope = currentLexicalScope; 125 const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; 126 const savedCurrentClassHasParameterProperties = currentClassHasParameterProperties; 127 128 // Handle state changes before visiting a node. 129 onBeforeVisitNode(node); 130 131 const visited = f(node); 132 133 // Restore state 134 if (currentLexicalScope !== savedCurrentScope) { 135 currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; 136 } 137 138 currentLexicalScope = savedCurrentScope; 139 currentClassHasParameterProperties = savedCurrentClassHasParameterProperties; 140 return visited; 141 } 142 143 /** 144 * Performs actions that should always occur immediately before visiting a node. 145 * 146 * @param node The node to visit. 147 */ 148 function onBeforeVisitNode(node: Node) { 149 switch (node.kind) { 150 case SyntaxKind.SourceFile: 151 case SyntaxKind.CaseBlock: 152 case SyntaxKind.ModuleBlock: 153 case SyntaxKind.Block: 154 currentLexicalScope = node as SourceFile | CaseBlock | ModuleBlock | Block; 155 currentScopeFirstDeclarationsOfName = undefined; 156 break; 157 158 case SyntaxKind.ClassDeclaration: 159 case SyntaxKind.FunctionDeclaration: 160 if (hasSyntacticModifier(node, ModifierFlags.Ambient)) { 161 break; 162 } 163 164 // Record these declarations provided that they have a name. 165 if ((node as ClassDeclaration | FunctionDeclaration).name) { 166 recordEmittedDeclarationInScope(node as ClassDeclaration | FunctionDeclaration); 167 } 168 else { 169 // These nodes should always have names unless they are default-exports; 170 // however, class declaration parsing allows for undefined names, so syntactically invalid 171 // programs may also have an undefined name. 172 Debug.assert(node.kind === SyntaxKind.ClassDeclaration || hasSyntacticModifier(node, ModifierFlags.Default)); 173 } 174 175 break; 176 } 177 } 178 179 /** 180 * General-purpose node visitor. 181 * 182 * @param node The node to visit. 183 */ 184 function visitor(node: Node): VisitResult<Node> { 185 return saveStateAndInvoke(node, visitorWorker); 186 } 187 188 /** 189 * Visits and possibly transforms any node. 190 * 191 * @param node The node to visit. 192 */ 193 function visitorWorker(node: Node): VisitResult<Node> { 194 if (node.transformFlags & TransformFlags.ContainsTypeScript) { 195 return visitTypeScript(node); 196 } 197 return node; 198 } 199 200 /** 201 * Specialized visitor that visits the immediate children of a SourceFile. 202 * 203 * @param node The node to visit. 204 */ 205 function sourceElementVisitor(node: Node): VisitResult<Node> { 206 return saveStateAndInvoke(node, sourceElementVisitorWorker); 207 } 208 209 /** 210 * Specialized visitor that visits the immediate children of a SourceFile. 211 * 212 * @param node The node to visit. 213 */ 214 function sourceElementVisitorWorker(node: Node): VisitResult<Node> { 215 switch (node.kind) { 216 case SyntaxKind.ImportDeclaration: 217 case SyntaxKind.ImportEqualsDeclaration: 218 case SyntaxKind.ExportAssignment: 219 case SyntaxKind.ExportDeclaration: 220 return visitElidableStatement(node as ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration); 221 default: 222 return visitorWorker(node); 223 } 224 } 225 226 function visitElidableStatement(node: ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration): VisitResult<Node> { 227 const parsed = getParseTreeNode(node); 228 if (parsed !== node) { 229 // If the node has been transformed by a `before` transformer, perform no ellision on it 230 // As the type information we would attempt to lookup to perform ellision is potentially unavailable for the synthesized nodes 231 // We do not reuse `visitorWorker`, as the ellidable statement syntax kinds are technically unrecognized by the switch-case in `visitTypeScript`, 232 // and will trigger debug failures when debug verbosity is turned up 233 if (node.transformFlags & TransformFlags.ContainsTypeScript) { 234 // This node contains TypeScript, so we should visit its children. 235 return visitEachChild(node, visitor, context); 236 } 237 // Otherwise, we can just return the node 238 return node; 239 } 240 switch (node.kind) { 241 case SyntaxKind.ImportDeclaration: 242 return visitImportDeclaration(node); 243 case SyntaxKind.ImportEqualsDeclaration: 244 return visitImportEqualsDeclaration(node); 245 case SyntaxKind.ExportAssignment: 246 return visitExportAssignment(node); 247 case SyntaxKind.ExportDeclaration: 248 return visitExportDeclaration(node); 249 default: 250 Debug.fail("Unhandled ellided statement"); 251 } 252 } 253 254 /** 255 * Specialized visitor that visits the immediate children of a namespace. 256 * 257 * @param node The node to visit. 258 */ 259 function namespaceElementVisitor(node: Node): VisitResult<Node> { 260 return saveStateAndInvoke(node, namespaceElementVisitorWorker); 261 } 262 263 /** 264 * Specialized visitor that visits the immediate children of a namespace. 265 * 266 * @param node The node to visit. 267 */ 268 function namespaceElementVisitorWorker(node: Node): VisitResult<Node> { 269 if (node.kind === SyntaxKind.ExportDeclaration || 270 node.kind === SyntaxKind.ImportDeclaration || 271 node.kind === SyntaxKind.ImportClause || 272 (node.kind === SyntaxKind.ImportEqualsDeclaration && 273 (node as ImportEqualsDeclaration).moduleReference.kind === SyntaxKind.ExternalModuleReference)) { 274 // do not emit ES6 imports and exports since they are illegal inside a namespace 275 return undefined; 276 } 277 else if (node.transformFlags & TransformFlags.ContainsTypeScript || hasSyntacticModifier(node, ModifierFlags.Export)) { 278 return visitTypeScript(node); 279 } 280 281 return node; 282 } 283 284 /** 285 * Gets a specialized visitor that visits the immediate children of a class with TypeScript syntax. 286 * 287 * @param parent The class containing the elements to visit. 288 */ 289 function getClassElementVisitor(parent: ClassLikeDeclaration): (node: Node) => VisitResult<Node> { 290 return node => saveStateAndInvoke(node, n => classElementVisitorWorker(n, parent)); 291 } 292 293 /** 294 * Specialized visitor that visits the immediate children of a class with TypeScript syntax. 295 * 296 * @param node The node to visit. 297 */ 298 function classElementVisitorWorker(node: Node, parent: ClassLikeDeclaration): VisitResult<Node> { 299 switch (node.kind) { 300 case SyntaxKind.Constructor: 301 return visitConstructor(node as ConstructorDeclaration); 302 303 case SyntaxKind.PropertyDeclaration: 304 // Property declarations are not TypeScript syntax, but they must be visited 305 // for the decorator transformation. 306 return visitPropertyDeclaration(node as PropertyDeclaration, parent); 307 308 case SyntaxKind.GetAccessor: 309 // Get Accessors can have TypeScript modifiers, decorators, and type annotations. 310 return visitGetAccessor(node as GetAccessorDeclaration, parent); 311 312 case SyntaxKind.SetAccessor: 313 // Set Accessors can have TypeScript modifiers and type annotations. 314 return visitSetAccessor(node as SetAccessorDeclaration, parent); 315 316 case SyntaxKind.MethodDeclaration: 317 // TypeScript method declarations may have decorators, modifiers 318 // or type annotations. 319 return visitMethodDeclaration(node as MethodDeclaration, parent); 320 321 case SyntaxKind.ClassStaticBlockDeclaration: 322 return visitEachChild(node, visitor, context); 323 324 case SyntaxKind.SemicolonClassElement: 325 return node; 326 327 case SyntaxKind.IndexSignature: 328 // Index signatures are elided 329 return; 330 331 default: 332 return Debug.failBadSyntaxKind(node); 333 } 334 } 335 336 function getObjectLiteralElementVisitor(parent: ObjectLiteralExpression): (node: Node) => VisitResult<Node> { 337 return node => saveStateAndInvoke(node, n => objectLiteralElementVisitorWorker(n, parent)); 338 } 339 340 function objectLiteralElementVisitorWorker(node: Node, parent: ObjectLiteralExpression): VisitResult<Node> { 341 switch (node.kind) { 342 case SyntaxKind.PropertyAssignment: 343 case SyntaxKind.ShorthandPropertyAssignment: 344 case SyntaxKind.SpreadAssignment: 345 return visitor(node); 346 347 case SyntaxKind.GetAccessor: 348 // Get Accessors can have TypeScript modifiers, decorators, and type annotations. 349 return visitGetAccessor(node as GetAccessorDeclaration, parent); 350 351 case SyntaxKind.SetAccessor: 352 // Set Accessors can have TypeScript modifiers and type annotations. 353 return visitSetAccessor(node as SetAccessorDeclaration, parent); 354 355 case SyntaxKind.MethodDeclaration: 356 // TypeScript method declarations may have decorators, modifiers 357 // or type annotations. 358 return visitMethodDeclaration(node as MethodDeclaration, parent); 359 360 default: 361 return Debug.failBadSyntaxKind(node); 362 } 363 } 364 365 function modifierVisitor(node: Node): VisitResult<Node> { 366 if (isDecorator(node)) return undefined; 367 if (modifierToFlag(node.kind) & ModifierFlags.TypeScriptModifier) { 368 return undefined; 369 } 370 else if (currentNamespace && node.kind === SyntaxKind.ExportKeyword) { 371 return undefined; 372 } 373 374 return node; 375 } 376 377 /** 378 * Branching visitor, visits a TypeScript syntax node. 379 * 380 * @param node The node to visit. 381 */ 382 function visitTypeScript(node: Node): VisitResult<Node> { 383 if (isStatement(node) && hasSyntacticModifier(node, ModifierFlags.Ambient)) { 384 // TypeScript ambient declarations are elided, but some comments may be preserved. 385 // See the implementation of `getLeadingComments` in comments.ts for more details. 386 return factory.createNotEmittedStatement(node); 387 } 388 389 switch (node.kind) { 390 case SyntaxKind.ExportKeyword: 391 case SyntaxKind.DefaultKeyword: 392 // ES6 export and default modifiers are elided when inside a namespace. 393 return currentNamespace ? undefined : node; 394 395 case SyntaxKind.PublicKeyword: 396 case SyntaxKind.PrivateKeyword: 397 case SyntaxKind.ProtectedKeyword: 398 case SyntaxKind.AbstractKeyword: 399 case SyntaxKind.OverrideKeyword: 400 case SyntaxKind.ConstKeyword: 401 case SyntaxKind.DeclareKeyword: 402 case SyntaxKind.ReadonlyKeyword: 403 case SyntaxKind.InKeyword: 404 case SyntaxKind.OutKeyword: 405 // TypeScript accessibility and readonly modifiers are elided 406 // falls through 407 case SyntaxKind.ArrayType: 408 case SyntaxKind.TupleType: 409 case SyntaxKind.OptionalType: 410 case SyntaxKind.RestType: 411 case SyntaxKind.TypeLiteral: 412 case SyntaxKind.TypePredicate: 413 case SyntaxKind.TypeParameter: 414 case SyntaxKind.AnyKeyword: 415 case SyntaxKind.UnknownKeyword: 416 case SyntaxKind.BooleanKeyword: 417 case SyntaxKind.StringKeyword: 418 case SyntaxKind.NumberKeyword: 419 case SyntaxKind.NeverKeyword: 420 case SyntaxKind.VoidKeyword: 421 case SyntaxKind.SymbolKeyword: 422 case SyntaxKind.ConstructorType: 423 case SyntaxKind.FunctionType: 424 case SyntaxKind.TypeQuery: 425 case SyntaxKind.TypeReference: 426 case SyntaxKind.UnionType: 427 case SyntaxKind.IntersectionType: 428 case SyntaxKind.ConditionalType: 429 case SyntaxKind.ParenthesizedType: 430 case SyntaxKind.ThisType: 431 case SyntaxKind.TypeOperator: 432 case SyntaxKind.IndexedAccessType: 433 case SyntaxKind.MappedType: 434 case SyntaxKind.LiteralType: 435 // TypeScript type nodes are elided. 436 // falls through 437 438 case SyntaxKind.IndexSignature: 439 // TypeScript index signatures are elided. 440 return undefined; 441 442 case SyntaxKind.TypeAliasDeclaration: 443 // TypeScript type-only declarations are elided. 444 return factory.createNotEmittedStatement(node); 445 446 case SyntaxKind.NamespaceExportDeclaration: 447 // TypeScript namespace export declarations are elided. 448 return undefined; 449 450 case SyntaxKind.InterfaceDeclaration: 451 // TypeScript interfaces are elided, but some comments may be preserved. 452 // See the implementation of `getLeadingComments` in comments.ts for more details. 453 return factory.createNotEmittedStatement(node); 454 455 case SyntaxKind.ClassDeclaration: 456 // This may be a class declaration with TypeScript syntax extensions. 457 // 458 // TypeScript class syntax extensions include: 459 // - decorators 460 // - optional `implements` heritage clause 461 // - parameter property assignments in the constructor 462 // - index signatures 463 // - method overload signatures 464 return visitClassDeclaration(node as ClassDeclaration); 465 466 case SyntaxKind.ClassExpression: 467 // This may be a class expression with TypeScript syntax extensions. 468 // 469 // TypeScript class syntax extensions include: 470 // - decorators 471 // - optional `implements` heritage clause 472 // - parameter property assignments in the constructor 473 // - index signatures 474 // - method overload signatures 475 return visitClassExpression(node as ClassExpression); 476 477 case SyntaxKind.AnnotationDeclaration: 478 return factory.createNotEmittedStatement(node); 479 480 case SyntaxKind.HeritageClause: 481 // This may be a heritage clause with TypeScript syntax extensions. 482 // 483 // TypeScript heritage clause extensions include: 484 // - `implements` clause 485 return visitHeritageClause(node as HeritageClause); 486 487 case SyntaxKind.ExpressionWithTypeArguments: 488 // TypeScript supports type arguments on an expression in an `extends` heritage clause. 489 return visitExpressionWithTypeArguments(node as ExpressionWithTypeArguments); 490 491 case SyntaxKind.ObjectLiteralExpression: 492 return visitObjectLiteralExpression(node as ObjectLiteralExpression); 493 494 case SyntaxKind.Constructor: 495 case SyntaxKind.PropertyDeclaration: 496 case SyntaxKind.AnnotationPropertyDeclaration: 497 case SyntaxKind.MethodDeclaration: 498 case SyntaxKind.GetAccessor: 499 case SyntaxKind.SetAccessor: 500 case SyntaxKind.ClassStaticBlockDeclaration: 501 return Debug.fail("Class and object literal elements must be visited with their respective visitors"); 502 503 case SyntaxKind.FunctionDeclaration: 504 // Typescript function declarations can have modifiers, decorators, and type annotations. 505 return visitFunctionDeclaration(node as FunctionDeclaration); 506 507 case SyntaxKind.FunctionExpression: 508 // TypeScript function expressions can have modifiers and type annotations. 509 return visitFunctionExpression(node as FunctionExpression); 510 511 case SyntaxKind.ArrowFunction: 512 // TypeScript arrow functions can have modifiers and type annotations. 513 return visitArrowFunction(node as ArrowFunction); 514 515 case SyntaxKind.Parameter: 516 // This may be a parameter declaration with TypeScript syntax extensions. 517 // 518 // TypeScript parameter declaration syntax extensions include: 519 // - decorators 520 // - accessibility modifiers 521 // - the question mark (?) token for optional parameters 522 // - type annotations 523 // - this parameters 524 return visitParameter(node as ParameterDeclaration); 525 526 case SyntaxKind.ParenthesizedExpression: 527 // ParenthesizedExpressions are TypeScript if their expression is a 528 // TypeAssertion or AsExpression 529 return visitParenthesizedExpression(node as ParenthesizedExpression); 530 531 case SyntaxKind.TypeAssertionExpression: 532 case SyntaxKind.AsExpression: 533 // TypeScript type assertions are removed, but their subtrees are preserved. 534 return visitAssertionExpression(node as AssertionExpression); 535 536 case SyntaxKind.SatisfiesExpression: 537 return visitSatisfiesExpression(node as SatisfiesExpression); 538 539 case SyntaxKind.CallExpression: 540 return visitCallExpression(node as CallExpression); 541 542 case SyntaxKind.NewExpression: 543 return visitNewExpression(node as NewExpression); 544 545 case SyntaxKind.TaggedTemplateExpression: 546 return visitTaggedTemplateExpression(node as TaggedTemplateExpression); 547 548 case SyntaxKind.NonNullExpression: 549 // TypeScript non-null expressions are removed, but their subtrees are preserved. 550 return visitNonNullExpression(node as NonNullExpression); 551 552 case SyntaxKind.EnumDeclaration: 553 // TypeScript enum declarations do not exist in ES6 and must be rewritten. 554 return visitEnumDeclaration(node as EnumDeclaration); 555 556 case SyntaxKind.VariableStatement: 557 // TypeScript namespace exports for variable statements must be transformed. 558 return visitVariableStatement(node as VariableStatement); 559 560 case SyntaxKind.VariableDeclaration: 561 return visitVariableDeclaration(node as VariableDeclaration); 562 563 case SyntaxKind.ModuleDeclaration: 564 // TypeScript namespace declarations must be transformed. 565 return visitModuleDeclaration(node as ModuleDeclaration); 566 567 case SyntaxKind.ImportEqualsDeclaration: 568 // TypeScript namespace or external module import. 569 return visitImportEqualsDeclaration(node as ImportEqualsDeclaration); 570 571 case SyntaxKind.JsxSelfClosingElement: 572 return visitJsxSelfClosingElement(node as JsxSelfClosingElement); 573 574 case SyntaxKind.JsxOpeningElement: 575 return visitJsxJsxOpeningElement(node as JsxOpeningElement); 576 577 default: 578 // node contains some other TypeScript syntax 579 return visitEachChild(node, visitor, context); 580 } 581 } 582 583 function visitSourceFile(node: SourceFile) { 584 const alwaysStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") && 585 !(isExternalModule(node) && moduleKind >= ModuleKind.ES2015) && 586 !isJsonSourceFile(node); 587 588 return factory.updateSourceFile( 589 node, 590 visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, alwaysStrict)); 591 } 592 593 function visitObjectLiteralExpression(node: ObjectLiteralExpression) { 594 return factory.updateObjectLiteralExpression( 595 node, 596 visitNodes(node.properties, getObjectLiteralElementVisitor(node), isObjectLiteralElement) 597 ); 598 } 599 600 function getClassFacts(node: ClassDeclaration, staticProperties: readonly PropertyDeclaration[]) { 601 let facts = ClassFacts.None; 602 if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties; 603 const extendsClauseElement = getEffectiveBaseTypeNode(node); 604 if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass; 605 if (classOrConstructorParameterIsDecorated(node)) facts |= ClassFacts.HasConstructorDecorators; 606 if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators; 607 if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace; 608 else if (isDefaultExternalModuleExport(node)) facts |= ClassFacts.IsDefaultExternalExport; 609 else if (isNamedExternalModuleExport(node)) facts |= ClassFacts.IsNamedExternalExport; 610 if (languageVersion <= ScriptTarget.ES5 && (facts & ClassFacts.MayNeedImmediatelyInvokedFunctionExpression)) facts |= ClassFacts.UseImmediatelyInvokedFunctionExpression; 611 return facts; 612 } 613 614 function hasTypeScriptClassSyntax(node: Node) { 615 return !!(node.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax); 616 } 617 618 function isClassLikeDeclarationWithTypeScriptSyntax(node: ClassLikeDeclaration) { 619 return hasDecorators(node) 620 || some(node.typeParameters) 621 || some(node.heritageClauses, hasTypeScriptClassSyntax) 622 || some(node.members, hasTypeScriptClassSyntax); 623 } 624 625 function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> { 626 if (!isClassLikeDeclarationWithTypeScriptSyntax(node) && !(currentNamespace && hasSyntacticModifier(node, ModifierFlags.Export))) { 627 return factory.updateClassDeclaration( 628 node, 629 visitNodes(node.modifiers, modifierVisitor, isModifier), 630 node.name, 631 /*typeParameters*/ undefined, 632 visitNodes(node.heritageClauses, visitor, isHeritageClause), 633 visitNodes(node.members, getClassElementVisitor(node), isClassElement) 634 ); 635 } 636 637 const staticProperties = getProperties(node, /*requireInitializer*/ true, /*isStatic*/ true); 638 const facts = getClassFacts(node, staticProperties); 639 640 if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) { 641 context.startLexicalEnvironment(); 642 } 643 644 const name = node.name || (facts & ClassFacts.NeedsName ? factory.getGeneratedNameForNode(node) : undefined); 645 const allDecorators = getAllDecoratorsOfClass(node); 646 const decorators = transformAllDecoratorsOfDeclaration(node, node, allDecorators); 647 648 // we do not emit modifiers on the declaration if we are emitting an IIFE 649 const modifiers = !(facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) 650 ? visitNodes(node.modifiers, modifierVisitor, isModifier) 651 : elideNodes(factory, node.modifiers); // preserve positions, if available 652 653 // ${modifiers} class ${name} ${heritageClauses} { 654 // ${members} 655 // } 656 const classStatement = factory.updateClassDeclaration( 657 node, 658 concatenate<ModifierLike>(decorators, modifiers), 659 name, 660 /*typeParameters*/ undefined, 661 visitNodes(node.heritageClauses, visitor, isHeritageClause), 662 transformClassMembers(node) 663 ); 664 665 // To better align with the old emitter, we should not emit a trailing source map 666 // entry if the class has static properties. 667 let emitFlags = getEmitFlags(node); 668 if (facts & ClassFacts.HasStaticInitializedProperties) { 669 emitFlags |= EmitFlags.NoTrailingSourceMap; 670 } 671 672 setEmitFlags(classStatement, emitFlags); 673 674 let statements: Statement[] = [classStatement]; 675 676 if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) { 677 // When we emit a TypeScript class down to ES5, we must wrap it in an IIFE so that the 678 // 'es2015' transformer can properly nest static initializers and decorators. The result 679 // looks something like: 680 // 681 // var C = function () { 682 // class C { 683 // } 684 // C.static_prop = 1; 685 // return C; 686 // }(); 687 // 688 const closingBraceLocation = createTokenRange(skipTrivia(currentSourceFile.text, node.members.end), SyntaxKind.CloseBraceToken); 689 const localName = factory.getInternalName(node); 690 691 // The following partially-emitted expression exists purely to align our sourcemap 692 // emit with the original emitter. 693 const outer = factory.createPartiallyEmittedExpression(localName); 694 setTextRangeEnd(outer, closingBraceLocation.end); 695 setEmitFlags(outer, EmitFlags.NoComments); 696 697 const statement = factory.createReturnStatement(outer); 698 setTextRangePos(statement, closingBraceLocation.pos); 699 setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps); 700 statements.push(statement); 701 702 insertStatementsAfterStandardPrologue(statements, context.endLexicalEnvironment()); 703 704 const iife = factory.createImmediatelyInvokedArrowFunction(statements); 705 setEmitFlags(iife, EmitFlags.TypeScriptClassWrapper); 706 707 const varStatement = factory.createVariableStatement( 708 /*modifiers*/ undefined, 709 factory.createVariableDeclarationList([ 710 factory.createVariableDeclaration( 711 factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ false), 712 /*exclamationToken*/ undefined, 713 /*type*/ undefined, 714 iife 715 ) 716 ]) 717 ); 718 719 setOriginalNode(varStatement, node); 720 setCommentRange(varStatement, node); 721 setSourceMapRange(varStatement, moveRangePastDecorators(node)); 722 startOnNewLine(varStatement); 723 statements = [varStatement]; 724 } 725 726 // If the class is exported as part of a TypeScript namespace, emit the namespace export. 727 // Otherwise, if the class was exported at the top level and was decorated, emit an export 728 // declaration or export default for the class. 729 if (facts & ClassFacts.IsExportOfNamespace) { 730 addExportMemberAssignment(statements, node); 731 } 732 else if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression || facts & ClassFacts.HasConstructorDecorators) { 733 if (facts & ClassFacts.IsDefaultExternalExport) { 734 statements.push(factory.createExportDefault(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true))); 735 } 736 else if (facts & ClassFacts.IsNamedExternalExport) { 737 statements.push(factory.createExternalModuleExport(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true))); 738 } 739 } 740 741 if (statements.length > 1) { 742 // Add a DeclarationMarker as a marker for the end of the declaration 743 statements.push(factory.createEndOfDeclarationMarker(node)); 744 setEmitFlags(classStatement, getEmitFlags(classStatement) | EmitFlags.HasEndOfDeclarationMarker); 745 } 746 747 return singleOrMany(statements); 748 } 749 750 function visitClassExpression(node: ClassExpression): Expression { 751 const allDecorators = getAllDecoratorsOfClass(node); 752 const decorators = transformAllDecoratorsOfDeclaration(node, node, allDecorators); 753 return factory.updateClassExpression( 754 node, 755 decorators, 756 node.name, 757 /*typeParameters*/ undefined, 758 visitNodes(node.heritageClauses, visitor, isHeritageClause), 759 isClassLikeDeclarationWithTypeScriptSyntax(node) ? 760 transformClassMembers(node) : 761 visitNodes(node.members, getClassElementVisitor(node), isClassElement) 762 ); 763 } 764 765 /** 766 * Transforms the members of a class. 767 * 768 * @param node The current class. 769 */ 770 function transformClassMembers(node: ClassDeclaration | ClassExpression) { 771 const members: ClassElement[] = []; 772 const constructor = getFirstConstructorWithBody(node); 773 const parametersWithPropertyAssignments = constructor && 774 filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor)); 775 776 if (parametersWithPropertyAssignments) { 777 for (const parameter of parametersWithPropertyAssignments) { 778 if (isIdentifier(parameter.name)) { 779 members.push(setOriginalNode(factory.createPropertyDeclaration( 780 /*modifiers*/ undefined, 781 parameter.name, 782 /*questionOrExclamationToken*/ undefined, 783 /*type*/ undefined, 784 /*initializer*/ undefined), parameter)); 785 } 786 } 787 } 788 789 addRange(members, visitNodes(node.members, getClassElementVisitor(node), isClassElement)); 790 return setTextRange(factory.createNodeArray(members), /*location*/ node.members); 791 } 792 793 /** 794 * Transforms all of the decorators for a declaration into an array of expressions. 795 * 796 * @param node The declaration node. 797 * @param allDecorators An object containing all of the decorators for the declaration. 798 */ 799 function transformAllDecoratorsOfDeclaration(node: Declaration, container: ClassLikeDeclaration, allDecorators: AllDecorators | undefined) { 800 if (!allDecorators) { 801 return undefined; 802 } 803 804 const decorators = visitArray(allDecorators.decorators, visitor, isDecorator); 805 const parameterDecorators = flatMap(allDecorators.parameters, transformDecoratorsOfParameter); 806 const metadataDecorators = some(decorators) || some(parameterDecorators) ? getTypeMetadata(node, container) : undefined; 807 const result = factory.createNodeArray(concatenate(concatenate(decorators, parameterDecorators), metadataDecorators)); 808 const pos = firstOrUndefined(allDecorators.decorators)?.pos ?? -1; 809 const end = lastOrUndefined(allDecorators.decorators)?.end ?? -1; 810 setTextRangePosEnd(result, pos, end); 811 return result; 812 } 813 814 /** 815 * Transforms the decorators of a parameter into decorators of the class/method. 816 * 817 * @param parameterDecorators The decorators for the parameter at the provided offset. 818 * @param parameterOffset The offset of the parameter. 819 */ 820 function transformDecoratorsOfParameter(parameterDecorators: Decorator[], parameterOffset: number) { 821 if (parameterDecorators) { 822 const decorators: Decorator[] = []; 823 for (const parameterDecorator of parameterDecorators) { 824 const expression = visitNode(parameterDecorator.expression, visitor, isExpression); 825 const helper = emitHelpers().createParamHelper(expression, parameterOffset); 826 setTextRange(helper, parameterDecorator.expression); 827 setEmitFlags(helper, EmitFlags.NoComments); 828 829 const decorator = factory.createDecorator(helper); 830 setSourceMapRange(decorator, parameterDecorator.expression); 831 setCommentRange(decorator, parameterDecorator.expression); 832 setEmitFlags(decorator, EmitFlags.NoComments); 833 decorators.push(decorator); 834 } 835 return decorators; 836 } 837 } 838 839 /** 840 * Gets optional type metadata for a declaration. 841 * 842 * @param node The declaration node. 843 */ 844 function getTypeMetadata(node: Declaration, container: ClassLikeDeclaration) { 845 return USE_NEW_TYPE_METADATA_FORMAT ? 846 getNewTypeMetadata(node, container) : 847 getOldTypeMetadata(node, container); 848 } 849 850 function getOldTypeMetadata(node: Declaration, container: ClassLikeDeclaration) { 851 if (typeSerializer) { 852 let decorators: Decorator[] | undefined; 853 if (shouldAddTypeMetadata(node)) { 854 const typeMetadata = emitHelpers().createMetadataHelper("design:type", typeSerializer.serializeTypeOfNode({ currentLexicalScope, currentNameScope: container }, node)); 855 decorators = append(decorators, factory.createDecorator(typeMetadata)); 856 } 857 if (shouldAddParamTypesMetadata(node)) { 858 const paramTypesMetadata = emitHelpers().createMetadataHelper("design:paramtypes", typeSerializer.serializeParameterTypesOfNode({ currentLexicalScope, currentNameScope: container }, node, container)); 859 decorators = append(decorators, factory.createDecorator(paramTypesMetadata)); 860 } 861 if (shouldAddReturnTypeMetadata(node)) { 862 const returnTypeMetadata = emitHelpers().createMetadataHelper("design:returntype", typeSerializer.serializeReturnTypeOfNode({ currentLexicalScope, currentNameScope: container }, node)); 863 decorators = append(decorators, factory.createDecorator(returnTypeMetadata)); 864 } 865 return decorators; 866 } 867 } 868 869 function getNewTypeMetadata(node: Declaration, container: ClassLikeDeclaration) { 870 if (typeSerializer) { 871 let properties: ObjectLiteralElementLike[] | undefined; 872 if (shouldAddTypeMetadata(node)) { 873 const typeProperty = factory.createPropertyAssignment("type", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), typeSerializer.serializeTypeOfNode({ currentLexicalScope, currentNameScope: container }, node))); 874 properties = append(properties, typeProperty); 875 } 876 if (shouldAddParamTypesMetadata(node)) { 877 const paramTypeProperty = factory.createPropertyAssignment("paramTypes", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), typeSerializer.serializeParameterTypesOfNode({ currentLexicalScope, currentNameScope: container }, node, container))); 878 properties = append(properties, paramTypeProperty); 879 } 880 if (shouldAddReturnTypeMetadata(node)) { 881 const returnTypeProperty = factory.createPropertyAssignment("returnType", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), typeSerializer.serializeReturnTypeOfNode({ currentLexicalScope, currentNameScope: container }, node))); 882 properties = append(properties, returnTypeProperty); 883 } 884 if (properties) { 885 const typeInfoMetadata = emitHelpers().createMetadataHelper("design:typeinfo", factory.createObjectLiteralExpression(properties, /*multiLine*/ true)); 886 return [factory.createDecorator(typeInfoMetadata)]; 887 } 888 } 889 } 890 891 /** 892 * Determines whether to emit the "design:type" metadata based on the node's kind. 893 * The caller should have already tested whether the node has decorators and whether the 894 * emitDecoratorMetadata compiler option is set. 895 * 896 * @param node The node to test. 897 */ 898 function shouldAddTypeMetadata(node: Declaration): node is MethodDeclaration | AccessorDeclaration | PropertyDeclaration { 899 const kind = node.kind; 900 return kind === SyntaxKind.MethodDeclaration 901 || kind === SyntaxKind.GetAccessor 902 || kind === SyntaxKind.SetAccessor 903 || kind === SyntaxKind.PropertyDeclaration; 904 } 905 906 /** 907 * Determines whether to emit the "design:returntype" metadata based on the node's kind. 908 * The caller should have already tested whether the node has decorators and whether the 909 * emitDecoratorMetadata compiler option is set. 910 * 911 * @param node The node to test. 912 */ 913 function shouldAddReturnTypeMetadata(node: Declaration): node is MethodDeclaration { 914 return node.kind === SyntaxKind.MethodDeclaration; 915 } 916 917 /** 918 * Determines whether to emit the "design:paramtypes" metadata based on the node's kind. 919 * The caller should have already tested whether the node has decorators and whether the 920 * emitDecoratorMetadata compiler option is set. 921 * 922 * @param node The node to test. 923 */ 924 function shouldAddParamTypesMetadata(node: Declaration): node is ClassLikeDeclaration & { _hasConstructorBrand: never } | MethodDeclaration | AccessorDeclaration { 925 switch (node.kind) { 926 case SyntaxKind.ClassDeclaration: 927 case SyntaxKind.ClassExpression: 928 return getFirstConstructorWithBody(node as ClassLikeDeclaration) !== undefined; 929 case SyntaxKind.MethodDeclaration: 930 case SyntaxKind.GetAccessor: 931 case SyntaxKind.SetAccessor: 932 return true; 933 } 934 return false; 935 } 936 937 /** 938 * Gets an expression that represents a property name (for decorated properties or enums). 939 * For a computed property, a name is generated for the node. 940 * 941 * @param member The member whose name should be converted into an expression. 942 */ 943 function getExpressionForPropertyName(member: ClassElement | EnumMember, generateNameForComputedPropertyName: boolean): Expression { 944 const name = member.name!; 945 if (isPrivateIdentifier(name)) { 946 return factory.createIdentifier(""); 947 } 948 else if (isComputedPropertyName(name)) { 949 return generateNameForComputedPropertyName && !isSimpleInlineableExpression(name.expression) 950 ? factory.getGeneratedNameForNode(name) 951 : name.expression; 952 } 953 else if (isIdentifier(name)) { 954 return factory.createStringLiteral(idText(name)); 955 } 956 else { 957 return factory.cloneNode(name); 958 } 959 } 960 961 /** 962 * Visits the property name of a class element, for use when emitting property 963 * initializers. For a computed property on a node with decorators, a temporary 964 * value is stored for later use. 965 * 966 * @param member The member whose name should be visited. 967 */ 968 function visitPropertyNameOfClassElement(member: ClassElement): PropertyName { 969 const name = member.name!; 970 // Computed property names need to be transformed into a hoisted variable when they are used more than once. 971 // The names are used more than once when: 972 // - the property is non-static and its initializer is moved to the constructor (when there are parameter property assignments). 973 // - the property has a decorator. 974 if (isComputedPropertyName(name) && ((!hasStaticModifier(member) && currentClassHasParameterProperties) || hasDecorators(member))) { 975 const expression = visitNode(name.expression, visitor, isExpression); 976 const innerExpression = skipPartiallyEmittedExpressions(expression); 977 if (!isSimpleInlineableExpression(innerExpression)) { 978 const generatedName = factory.getGeneratedNameForNode(name); 979 hoistVariableDeclaration(generatedName); 980 return factory.updateComputedPropertyName(name, factory.createAssignment(generatedName, expression)); 981 } 982 } 983 return visitNode(name, visitor, isPropertyName); 984 } 985 986 /** 987 * Transforms a HeritageClause with TypeScript syntax. 988 * 989 * This function will only be called when one of the following conditions are met: 990 * - The node is a non-`extends` heritage clause that should be elided. 991 * - The node is an `extends` heritage clause that should be visited, but only allow a single type. 992 * 993 * @param node The HeritageClause to transform. 994 */ 995 function visitHeritageClause(node: HeritageClause): HeritageClause | undefined { 996 if (node.token === SyntaxKind.ImplementsKeyword) { 997 // implements clauses are elided 998 return undefined; 999 } 1000 return visitEachChild(node, visitor, context); 1001 } 1002 1003 /** 1004 * Transforms an ExpressionWithTypeArguments with TypeScript syntax. 1005 * 1006 * This function will only be called when one of the following conditions are met: 1007 * - The node contains type arguments that should be elided. 1008 * 1009 * @param node The ExpressionWithTypeArguments to transform. 1010 */ 1011 function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments): ExpressionWithTypeArguments { 1012 return factory.updateExpressionWithTypeArguments( 1013 node, 1014 visitNode(node.expression, visitor, isLeftHandSideExpression), 1015 /*typeArguments*/ undefined 1016 ); 1017 } 1018 1019 /** 1020 * Determines whether to emit a function-like declaration. We should not emit the 1021 * declaration if it does not have a body. 1022 * 1023 * @param node The declaration node. 1024 */ 1025 function shouldEmitFunctionLikeDeclaration<T extends FunctionLikeDeclaration>(node: T): node is T & { body: NonNullable<T["body"]> } { 1026 return !nodeIsMissing(node.body); 1027 } 1028 1029 function visitPropertyDeclaration(node: PropertyDeclaration, parent: ClassLikeDeclaration) { 1030 const isAmbient = node.flags & NodeFlags.Ambient || hasSyntacticModifier(node, ModifierFlags.Abstract); 1031 if (isAmbient && !hasDecorators(node)) { 1032 return undefined; 1033 } 1034 1035 const allDecorators = getAllDecoratorsOfClassElement(node, parent); 1036 const decorators = transformAllDecoratorsOfDeclaration(node, parent, allDecorators); 1037 1038 // Preserve a `declare x` property with decorators to be handled by the decorators transform 1039 if (isAmbient) { 1040 return factory.updatePropertyDeclaration( 1041 node, 1042 concatenate<ModifierLike>(decorators, factory.createModifiersFromModifierFlags(ModifierFlags.Ambient)), 1043 visitNode(node.name, visitor, isPropertyName), 1044 /*questionOrExclamationToken*/ undefined, 1045 /*type*/ undefined, 1046 /*initializer*/ undefined 1047 ); 1048 } 1049 1050 return factory.updatePropertyDeclaration( 1051 node, 1052 concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)), 1053 visitPropertyNameOfClassElement(node), 1054 /*questionOrExclamationToken*/ undefined, 1055 /*type*/ undefined, 1056 visitNode(node.initializer, visitor) 1057 ); 1058 } 1059 1060 function visitConstructor(node: ConstructorDeclaration) { 1061 if (!shouldEmitFunctionLikeDeclaration(node)) { 1062 return undefined; 1063 } 1064 1065 return factory.updateConstructorDeclaration( 1066 node, 1067 /*modifiers*/ undefined, 1068 visitParameterList(node.parameters, visitor, context), 1069 transformConstructorBody(node.body, node) 1070 ); 1071 } 1072 1073 function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) { 1074 const parametersWithPropertyAssignments = constructor && 1075 filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor)); 1076 if (!some(parametersWithPropertyAssignments)) { 1077 return visitFunctionBody(body, visitor, context); 1078 } 1079 1080 let statements: Statement[] = []; 1081 1082 resumeLexicalEnvironment(); 1083 1084 const prologueStatementCount = factory.copyPrologue(body.statements, statements, /*ensureUseStrict*/ false, visitor); 1085 const superStatementIndex = findSuperStatementIndex(body.statements, prologueStatementCount); 1086 1087 // If there was a super call, visit existing statements up to and including it 1088 if (superStatementIndex >= 0) { 1089 addRange( 1090 statements, 1091 visitNodes(body.statements, visitor, isStatement, prologueStatementCount, superStatementIndex + 1 - prologueStatementCount), 1092 ); 1093 } 1094 1095 // Transform parameters into property assignments. Transforms this: 1096 // 1097 // constructor (public x, public y) { 1098 // } 1099 // 1100 // Into this: 1101 // 1102 // constructor (x, y) { 1103 // this.x = x; 1104 // this.y = y; 1105 // } 1106 // 1107 const parameterPropertyAssignments = mapDefined(parametersWithPropertyAssignments, transformParameterWithPropertyAssignment); 1108 1109 // If there is a super() call, the parameter properties go immediately after it 1110 if (superStatementIndex >= 0) { 1111 addRange(statements, parameterPropertyAssignments); 1112 } 1113 // Since there was no super() call, parameter properties are the first statements in the constructor after any prologue statements 1114 else { 1115 statements = [ 1116 ...statements.slice(0, prologueStatementCount), 1117 ...parameterPropertyAssignments, 1118 ...statements.slice(prologueStatementCount), 1119 ]; 1120 } 1121 1122 // Add remaining statements from the body, skipping the super() call if it was found and any (already added) prologue statements 1123 const start = superStatementIndex >= 0 ? superStatementIndex + 1 : prologueStatementCount; 1124 addRange(statements, visitNodes(body.statements, visitor, isStatement, start)); 1125 1126 // End the lexical environment. 1127 statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment()); 1128 const block = factory.createBlock(setTextRange(factory.createNodeArray(statements), body.statements), /*multiLine*/ true); 1129 setTextRange(block, /*location*/ body); 1130 setOriginalNode(block, body); 1131 return block; 1132 } 1133 1134 /** 1135 * Transforms a parameter into a property assignment statement. 1136 * 1137 * @param node The parameter declaration. 1138 */ 1139 function transformParameterWithPropertyAssignment(node: ParameterPropertyDeclaration) { 1140 const name = node.name; 1141 if (!isIdentifier(name)) { 1142 return undefined; 1143 } 1144 1145 // TODO(rbuckton): Does this need to be parented? 1146 const propertyName = setParent(setTextRange(factory.cloneNode(name), name), name.parent); 1147 setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap); 1148 1149 // TODO(rbuckton): Does this need to be parented? 1150 const localName = setParent(setTextRange(factory.cloneNode(name), name), name.parent); 1151 setEmitFlags(localName, EmitFlags.NoComments); 1152 1153 return startOnNewLine( 1154 removeAllComments( 1155 setTextRange( 1156 setOriginalNode( 1157 factory.createExpressionStatement( 1158 factory.createAssignment( 1159 setTextRange( 1160 factory.createPropertyAccessExpression( 1161 factory.createThis(), 1162 propertyName 1163 ), 1164 node.name 1165 ), 1166 localName 1167 ) 1168 ), 1169 node 1170 ), 1171 moveRangePos(node, -1) 1172 ) 1173 ) 1174 ); 1175 } 1176 1177 function visitMethodDeclaration(node: MethodDeclaration, parent: ClassLikeDeclaration | ObjectLiteralExpression) { 1178 if (!(node.transformFlags & TransformFlags.ContainsTypeScript)) { 1179 return node; 1180 } 1181 1182 if (!shouldEmitFunctionLikeDeclaration(node)) { 1183 return undefined; 1184 } 1185 1186 const allDecorators = isClassLike(parent) ? getAllDecoratorsOfClassElement(node, parent) : undefined; 1187 const decorators = isClassLike(parent) ? transformAllDecoratorsOfDeclaration(node, parent, allDecorators) : undefined; 1188 return factory.updateMethodDeclaration( 1189 node, 1190 concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)), 1191 node.asteriskToken, 1192 visitPropertyNameOfClassElement(node), 1193 /*questionToken*/ undefined, 1194 /*typeParameters*/ undefined, 1195 visitParameterList(node.parameters, visitor, context), 1196 /*type*/ undefined, 1197 visitFunctionBody(node.body, visitor, context) 1198 ); 1199 } 1200 1201 /** 1202 * Determines whether to emit an accessor declaration. We should not emit the 1203 * declaration if it does not have a body and is abstract. 1204 * 1205 * @param node The declaration node. 1206 */ 1207 function shouldEmitAccessorDeclaration(node: AccessorDeclaration) { 1208 return !(nodeIsMissing(node.body) && hasSyntacticModifier(node, ModifierFlags.Abstract)); 1209 } 1210 1211 function visitGetAccessor(node: GetAccessorDeclaration, parent: ClassLikeDeclaration | ObjectLiteralExpression) { 1212 if (!(node.transformFlags & TransformFlags.ContainsTypeScript)) { 1213 return node; 1214 } 1215 1216 if (!shouldEmitAccessorDeclaration(node)) { 1217 return undefined; 1218 } 1219 1220 const decorators = isClassLike(parent) ? 1221 transformAllDecoratorsOfDeclaration(node, parent, getAllDecoratorsOfClassElement(node, parent)) : 1222 undefined; 1223 1224 return factory.updateGetAccessorDeclaration( 1225 node, 1226 concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)), 1227 visitPropertyNameOfClassElement(node), 1228 visitParameterList(node.parameters, visitor, context), 1229 /*type*/ undefined, 1230 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 1231 ); 1232 } 1233 1234 function visitSetAccessor(node: SetAccessorDeclaration, parent: ClassLikeDeclaration | ObjectLiteralExpression) { 1235 if (!(node.transformFlags & TransformFlags.ContainsTypeScript)) { 1236 return node; 1237 } 1238 1239 if (!shouldEmitAccessorDeclaration(node)) { 1240 return undefined; 1241 } 1242 1243 const decorators = isClassLike(parent) ? 1244 transformAllDecoratorsOfDeclaration(node, parent, getAllDecoratorsOfClassElement(node, parent)) : 1245 undefined; 1246 1247 return factory.updateSetAccessorDeclaration( 1248 node, 1249 concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)), 1250 visitPropertyNameOfClassElement(node), 1251 visitParameterList(node.parameters, visitor, context), 1252 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 1253 ); 1254 } 1255 1256 function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> { 1257 if (!shouldEmitFunctionLikeDeclaration(node)) { 1258 return factory.createNotEmittedStatement(node); 1259 } 1260 const updated = factory.updateFunctionDeclaration( 1261 node, 1262 visitNodes(node.modifiers, modifierVisitor, isModifier), 1263 node.asteriskToken, 1264 node.name, 1265 /*typeParameters*/ undefined, 1266 visitParameterList(node.parameters, visitor, context), 1267 /*type*/ undefined, 1268 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 1269 ); 1270 if (isExportOfNamespace(node)) { 1271 const statements: Statement[] = [updated]; 1272 addExportMemberAssignment(statements, node); 1273 return statements; 1274 } 1275 return updated; 1276 } 1277 1278 function visitFunctionExpression(node: FunctionExpression): Expression { 1279 if (!shouldEmitFunctionLikeDeclaration(node)) { 1280 return factory.createOmittedExpression(); 1281 } 1282 const updated = factory.updateFunctionExpression( 1283 node, 1284 visitNodes(node.modifiers, modifierVisitor, isModifier), 1285 node.asteriskToken, 1286 node.name, 1287 /*typeParameters*/ undefined, 1288 visitParameterList(node.parameters, visitor, context), 1289 /*type*/ undefined, 1290 visitFunctionBody(node.body, visitor, context) || factory.createBlock([]) 1291 ); 1292 return updated; 1293 } 1294 1295 function visitArrowFunction(node: ArrowFunction) { 1296 const updated = factory.updateArrowFunction( 1297 node, 1298 visitNodes(node.modifiers, modifierVisitor, isModifier), 1299 /*typeParameters*/ undefined, 1300 visitParameterList(node.parameters, visitor, context), 1301 /*type*/ undefined, 1302 node.equalsGreaterThanToken, 1303 visitFunctionBody(node.body, visitor, context), 1304 ); 1305 return updated; 1306 } 1307 1308 function visitParameter(node: ParameterDeclaration) { 1309 if (parameterIsThisKeyword(node)) { 1310 return undefined; 1311 } 1312 1313 const updated = factory.updateParameterDeclaration( 1314 node, 1315 elideNodes(factory, node.modifiers), // preserve positions, if available 1316 node.dotDotDotToken, 1317 visitNode(node.name, visitor, isBindingName), 1318 /*questionToken*/ undefined, 1319 /*type*/ undefined, 1320 visitNode(node.initializer, visitor, isExpression) 1321 ); 1322 if (updated !== node) { 1323 // While we emit the source map for the node after skipping decorators and modifiers, 1324 // we need to emit the comments for the original range. 1325 setCommentRange(updated, node); 1326 setTextRange(updated, moveRangePastModifiers(node)); 1327 setSourceMapRange(updated, moveRangePastModifiers(node)); 1328 setEmitFlags(updated.name, EmitFlags.NoTrailingSourceMap); 1329 } 1330 return updated; 1331 } 1332 1333 function visitVariableStatement(node: VariableStatement): Statement | undefined { 1334 if (isExportOfNamespace(node)) { 1335 const variables = getInitializedVariables(node.declarationList); 1336 if (variables.length === 0) { 1337 // elide statement if there are no initialized variables. 1338 return undefined; 1339 } 1340 1341 return setTextRange( 1342 factory.createExpressionStatement( 1343 factory.inlineExpressions( 1344 map(variables, transformInitializedVariable) 1345 ) 1346 ), 1347 node 1348 ); 1349 } 1350 else { 1351 return visitEachChild(node, visitor, context); 1352 } 1353 } 1354 1355 function transformInitializedVariable(node: InitializedVariableDeclaration): Expression { 1356 const name = node.name; 1357 if (isBindingPattern(name)) { 1358 return flattenDestructuringAssignment( 1359 node, 1360 visitor, 1361 context, 1362 FlattenLevel.All, 1363 /*needsValue*/ false, 1364 createNamespaceExportExpression 1365 ); 1366 } 1367 else { 1368 return setTextRange( 1369 factory.createAssignment( 1370 getNamespaceMemberNameWithSourceMapsAndWithoutComments(name), 1371 visitNode(node.initializer, visitor, isExpression) 1372 ), 1373 /*location*/ node 1374 ); 1375 } 1376 } 1377 1378 function visitVariableDeclaration(node: VariableDeclaration) { 1379 const updated = factory.updateVariableDeclaration( 1380 node, 1381 visitNode(node.name, visitor, isBindingName), 1382 /*exclamationToken*/ undefined, 1383 /*type*/ undefined, 1384 visitNode(node.initializer, visitor, isExpression)); 1385 if (node.type) { 1386 setTypeNode(updated.name, node.type); 1387 } 1388 return updated; 1389 } 1390 1391 function visitParenthesizedExpression(node: ParenthesizedExpression): Expression { 1392 const innerExpression = skipOuterExpressions(node.expression, ~OuterExpressionKinds.Assertions); 1393 if (isAssertionExpression(innerExpression)) { 1394 // Make sure we consider all nested cast expressions, e.g.: 1395 // (<any><number><any>-A).x; 1396 const expression = visitNode(node.expression, visitor, isExpression); 1397 1398 // We have an expression of the form: (<Type>SubExpr). Emitting this as (SubExpr) 1399 // is really not desirable. We would like to emit the subexpression as-is. Omitting 1400 // the parentheses, however, could cause change in the semantics of the generated 1401 // code if the casted expression has a lower precedence than the rest of the 1402 // expression. 1403 // 1404 // To preserve comments, we return a "PartiallyEmittedExpression" here which will 1405 // preserve the position information of the original expression. 1406 // 1407 // Due to the auto-parenthesization rules used by the visitor and factory functions 1408 // we can safely elide the parentheses here, as a new synthetic 1409 // ParenthesizedExpression will be inserted if we remove parentheses too 1410 // aggressively. 1411 // 1412 // If there are leading comments on the expression itself, the emitter will handle ASI 1413 // for return, throw, and yield by re-introducing parenthesis during emit on an as-need 1414 // basis. 1415 return factory.createPartiallyEmittedExpression(expression, node); 1416 } 1417 1418 return visitEachChild(node, visitor, context); 1419 } 1420 1421 function visitAssertionExpression(node: AssertionExpression): Expression { 1422 const expression = visitNode(node.expression, visitor, isExpression); 1423 return factory.createPartiallyEmittedExpression(expression, node); 1424 } 1425 1426 function visitNonNullExpression(node: NonNullExpression): Expression { 1427 const expression = visitNode(node.expression, visitor, isLeftHandSideExpression); 1428 return factory.createPartiallyEmittedExpression(expression, node); 1429 } 1430 1431 function visitSatisfiesExpression(node: SatisfiesExpression): Expression { 1432 const expression = visitNode(node.expression, visitor, isExpression); 1433 return factory.createPartiallyEmittedExpression(expression, node); 1434 } 1435 1436 function visitCallExpression(node: CallExpression) { 1437 return factory.updateCallExpression( 1438 node, 1439 visitNode(node.expression, visitor, isExpression), 1440 /*typeArguments*/ undefined, 1441 visitNodes(node.arguments, visitor, isExpression)); 1442 } 1443 1444 function visitNewExpression(node: NewExpression) { 1445 return factory.updateNewExpression( 1446 node, 1447 visitNode(node.expression, visitor, isExpression), 1448 /*typeArguments*/ undefined, 1449 visitNodes(node.arguments, visitor, isExpression)); 1450 } 1451 1452 function visitTaggedTemplateExpression(node: TaggedTemplateExpression) { 1453 return factory.updateTaggedTemplateExpression( 1454 node, 1455 visitNode(node.tag, visitor, isExpression), 1456 /*typeArguments*/ undefined, 1457 visitNode(node.template, visitor, isExpression)); 1458 } 1459 1460 function visitJsxSelfClosingElement(node: JsxSelfClosingElement) { 1461 return factory.updateJsxSelfClosingElement( 1462 node, 1463 visitNode(node.tagName, visitor, isJsxTagNameExpression), 1464 /*typeArguments*/ undefined, 1465 visitNode(node.attributes, visitor, isJsxAttributes)); 1466 } 1467 1468 function visitJsxJsxOpeningElement(node: JsxOpeningElement) { 1469 return factory.updateJsxOpeningElement( 1470 node, 1471 visitNode(node.tagName, visitor, isJsxTagNameExpression), 1472 /*typeArguments*/ undefined, 1473 visitNode(node.attributes, visitor, isJsxAttributes)); 1474 } 1475 1476 /** 1477 * Determines whether to emit an enum declaration. 1478 * 1479 * @param node The enum declaration node. 1480 */ 1481 function shouldEmitEnumDeclaration(node: EnumDeclaration) { 1482 return !isEnumConst(node) 1483 || shouldPreserveConstEnums(compilerOptions); 1484 } 1485 1486 /** 1487 * Visits an enum declaration. 1488 * 1489 * This function will be called any time a TypeScript enum is encountered. 1490 * 1491 * @param node The enum declaration node. 1492 */ 1493 function visitEnumDeclaration(node: EnumDeclaration): VisitResult<Statement> { 1494 if (!shouldEmitEnumDeclaration(node)) { 1495 return factory.createNotEmittedStatement(node); 1496 } 1497 1498 const statements: Statement[] = []; 1499 1500 // We request to be advised when the printer is about to print this node. This allows 1501 // us to set up the correct state for later substitutions. 1502 let emitFlags = EmitFlags.AdviseOnEmitNode; 1503 1504 // If needed, we should emit a variable declaration for the enum. If we emit 1505 // a leading variable declaration, we should not emit leading comments for the 1506 // enum body. 1507 const varAdded = addVarForEnumOrModuleDeclaration(statements, node); 1508 if (varAdded) { 1509 // We should still emit the comments if we are emitting a system module. 1510 if (moduleKind !== ModuleKind.System || currentLexicalScope !== currentSourceFile) { 1511 emitFlags |= EmitFlags.NoLeadingComments; 1512 } 1513 } 1514 1515 // `parameterName` is the declaration name used inside of the enum. 1516 const parameterName = getNamespaceParameterName(node); 1517 1518 // `containerName` is the expression used inside of the enum for assignments. 1519 const containerName = getNamespaceContainerName(node); 1520 1521 // `exportName` is the expression used within this node's container for any exported references. 1522 const exportName = hasSyntacticModifier(node, ModifierFlags.Export) 1523 ? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true) 1524 : factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 1525 1526 // x || (x = {}) 1527 // exports.x || (exports.x = {}) 1528 let moduleArg = 1529 factory.createLogicalOr( 1530 exportName, 1531 factory.createAssignment( 1532 exportName, 1533 factory.createObjectLiteralExpression() 1534 ) 1535 ); 1536 1537 if (hasNamespaceQualifiedExportName(node)) { 1538 // `localName` is the expression used within this node's containing scope for any local references. 1539 const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 1540 1541 // x = (exports.x || (exports.x = {})) 1542 moduleArg = factory.createAssignment(localName, moduleArg); 1543 } 1544 1545 // (function (x) { 1546 // x[x["y"] = 0] = "y"; 1547 // ... 1548 // })(x || (x = {})); 1549 const enumStatement = factory.createExpressionStatement( 1550 factory.createCallExpression( 1551 factory.createFunctionExpression( 1552 /*modifiers*/ undefined, 1553 /*asteriskToken*/ undefined, 1554 /*name*/ undefined, 1555 /*typeParameters*/ undefined, 1556 [factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)], 1557 /*type*/ undefined, 1558 transformEnumBody(node, containerName) 1559 ), 1560 /*typeArguments*/ undefined, 1561 [moduleArg] 1562 ) 1563 ); 1564 1565 setOriginalNode(enumStatement, node); 1566 if (varAdded) { 1567 // If a variable was added, synthetic comments are emitted on it, not on the moduleStatement. 1568 setSyntheticLeadingComments(enumStatement, undefined); 1569 setSyntheticTrailingComments(enumStatement, undefined); 1570 } 1571 setTextRange(enumStatement, node); 1572 addEmitFlags(enumStatement, emitFlags); 1573 statements.push(enumStatement); 1574 1575 // Add a DeclarationMarker for the enum to preserve trailing comments and mark 1576 // the end of the declaration. 1577 statements.push(factory.createEndOfDeclarationMarker(node)); 1578 return statements; 1579 } 1580 1581 /** 1582 * Transforms the body of an enum declaration. 1583 * 1584 * @param node The enum declaration node. 1585 */ 1586 function transformEnumBody(node: EnumDeclaration, localName: Identifier): Block { 1587 const savedCurrentNamespaceLocalName = currentNamespaceContainerName; 1588 currentNamespaceContainerName = localName; 1589 1590 const statements: Statement[] = []; 1591 startLexicalEnvironment(); 1592 const members = map(node.members, transformEnumMember); 1593 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 1594 addRange(statements, members); 1595 1596 currentNamespaceContainerName = savedCurrentNamespaceLocalName; 1597 return factory.createBlock( 1598 setTextRange(factory.createNodeArray(statements), /*location*/ node.members), 1599 /*multiLine*/ true 1600 ); 1601 } 1602 1603 /** 1604 * Transforms an enum member into a statement. 1605 * 1606 * @param member The enum member node. 1607 */ 1608 function transformEnumMember(member: EnumMember): Statement { 1609 // enums don't support computed properties 1610 // we pass false as 'generateNameForComputedPropertyName' for a backward compatibility purposes 1611 // old emitter always generate 'expression' part of the name as-is. 1612 const name = getExpressionForPropertyName(member, /*generateNameForComputedPropertyName*/ false); 1613 const valueExpression = transformEnumMemberDeclarationValue(member); 1614 const innerAssignment = factory.createAssignment( 1615 factory.createElementAccessExpression( 1616 currentNamespaceContainerName, 1617 name 1618 ), 1619 valueExpression 1620 ); 1621 const outerAssignment = valueExpression.kind === SyntaxKind.StringLiteral ? 1622 innerAssignment : 1623 factory.createAssignment( 1624 factory.createElementAccessExpression( 1625 currentNamespaceContainerName, 1626 innerAssignment 1627 ), 1628 name 1629 ); 1630 return setTextRange( 1631 factory.createExpressionStatement( 1632 setTextRange( 1633 outerAssignment, 1634 member 1635 ) 1636 ), 1637 member 1638 ); 1639 } 1640 1641 /** 1642 * Transforms the value of an enum member. 1643 * 1644 * @param member The enum member node. 1645 */ 1646 function transformEnumMemberDeclarationValue(member: EnumMember): Expression { 1647 const value = resolver.getConstantValue(member); 1648 if (value !== undefined) { 1649 return typeof value === "string" ? factory.createStringLiteral(value) : factory.createNumericLiteral(value); 1650 } 1651 else { 1652 enableSubstitutionForNonQualifiedEnumMembers(); 1653 if (member.initializer) { 1654 return visitNode(member.initializer, visitor, isExpression); 1655 } 1656 else { 1657 return factory.createVoidZero(); 1658 } 1659 } 1660 } 1661 1662 /** 1663 * Determines whether to elide a module declaration. 1664 * 1665 * @param node The module declaration node. 1666 */ 1667 function shouldEmitModuleDeclaration(nodeIn: ModuleDeclaration) { 1668 const node = getParseTreeNode(nodeIn, isModuleDeclaration); 1669 if (!node) { 1670 // If we can't find a parse tree node, assume the node is instantiated. 1671 return true; 1672 } 1673 return isInstantiatedModule(node, shouldPreserveConstEnums(compilerOptions)); 1674 } 1675 1676 /** 1677 * Determines whether an exported declaration will have a qualified export name (e.g. `f.x` 1678 * or `exports.x`). 1679 */ 1680 function hasNamespaceQualifiedExportName(node: Node) { 1681 return isExportOfNamespace(node) 1682 || (isExternalModuleExport(node) 1683 && moduleKind !== ModuleKind.ES2015 1684 && moduleKind !== ModuleKind.ES2020 1685 && moduleKind !== ModuleKind.ES2022 1686 && moduleKind !== ModuleKind.ESNext 1687 && moduleKind !== ModuleKind.System); 1688 } 1689 1690 /** 1691 * Records that a declaration was emitted in the current scope, if it was the first 1692 * declaration for the provided symbol. 1693 */ 1694 function recordEmittedDeclarationInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration) { 1695 if (!currentScopeFirstDeclarationsOfName) { 1696 currentScopeFirstDeclarationsOfName = new Map(); 1697 } 1698 1699 const name = declaredNameInScope(node); 1700 if (!currentScopeFirstDeclarationsOfName.has(name)) { 1701 currentScopeFirstDeclarationsOfName.set(name, node); 1702 } 1703 } 1704 1705 /** 1706 * Determines whether a declaration is the first declaration with 1707 * the same name emitted in the current scope. 1708 */ 1709 function isFirstEmittedDeclarationInScope(node: ModuleDeclaration | EnumDeclaration) { 1710 if (currentScopeFirstDeclarationsOfName) { 1711 const name = declaredNameInScope(node); 1712 return currentScopeFirstDeclarationsOfName.get(name) === node; 1713 } 1714 return true; 1715 } 1716 1717 function declaredNameInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration): __String { 1718 Debug.assertNode(node.name, isIdentifier); 1719 return node.name.escapedText; 1720 } 1721 1722 /** 1723 * Adds a leading VariableStatement for a enum or module declaration. 1724 */ 1725 function addVarForEnumOrModuleDeclaration(statements: Statement[], node: ModuleDeclaration | EnumDeclaration) { 1726 // Emit a variable statement for the module. We emit top-level enums as a `var` 1727 // declaration to avoid static errors in global scripts scripts due to redeclaration. 1728 // enums in any other scope are emitted as a `let` declaration. 1729 const statement = factory.createVariableStatement( 1730 visitNodes(node.modifiers, modifierVisitor, isModifier), 1731 factory.createVariableDeclarationList([ 1732 factory.createVariableDeclaration( 1733 factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) 1734 ) 1735 ], currentLexicalScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let) 1736 ); 1737 1738 setOriginalNode(statement, node); 1739 1740 recordEmittedDeclarationInScope(node); 1741 if (isFirstEmittedDeclarationInScope(node)) { 1742 // Adjust the source map emit to match the old emitter. 1743 if (node.kind === SyntaxKind.EnumDeclaration) { 1744 setSourceMapRange(statement.declarationList, node); 1745 } 1746 else { 1747 setSourceMapRange(statement, node); 1748 } 1749 1750 // Trailing comments for module declaration should be emitted after the function closure 1751 // instead of the variable statement: 1752 // 1753 // /** Module comment*/ 1754 // module m1 { 1755 // function foo4Export() { 1756 // } 1757 // } // trailing comment module 1758 // 1759 // Should emit: 1760 // 1761 // /** Module comment*/ 1762 // var m1; 1763 // (function (m1) { 1764 // function foo4Export() { 1765 // } 1766 // })(m1 || (m1 = {})); // trailing comment module 1767 // 1768 setCommentRange(statement, node); 1769 addEmitFlags(statement, EmitFlags.NoTrailingComments | EmitFlags.HasEndOfDeclarationMarker); 1770 statements.push(statement); 1771 return true; 1772 } 1773 else { 1774 // For an EnumDeclaration or ModuleDeclaration that merges with a preceeding 1775 // declaration we do not emit a leading variable declaration. To preserve the 1776 // begin/end semantics of the declararation and to properly handle exports 1777 // we wrap the leading variable declaration in a `MergeDeclarationMarker`. 1778 const mergeMarker = factory.createMergeDeclarationMarker(statement); 1779 setEmitFlags(mergeMarker, EmitFlags.NoComments | EmitFlags.HasEndOfDeclarationMarker); 1780 statements.push(mergeMarker); 1781 return false; 1782 } 1783 } 1784 1785 /** 1786 * Visits a module declaration node. 1787 * 1788 * This function will be called any time a TypeScript namespace (ModuleDeclaration) is encountered. 1789 * 1790 * @param node The module declaration node. 1791 */ 1792 function visitModuleDeclaration(node: ModuleDeclaration): VisitResult<Statement> { 1793 if (!shouldEmitModuleDeclaration(node)) { 1794 return factory.createNotEmittedStatement(node); 1795 } 1796 1797 Debug.assertNode(node.name, isIdentifier, "A TypeScript namespace should have an Identifier name."); 1798 enableSubstitutionForNamespaceExports(); 1799 1800 const statements: Statement[] = []; 1801 1802 // We request to be advised when the printer is about to print this node. This allows 1803 // us to set up the correct state for later substitutions. 1804 let emitFlags = EmitFlags.AdviseOnEmitNode; 1805 1806 // If needed, we should emit a variable declaration for the module. If we emit 1807 // a leading variable declaration, we should not emit leading comments for the 1808 // module body. 1809 const varAdded = addVarForEnumOrModuleDeclaration(statements, node); 1810 if (varAdded) { 1811 // We should still emit the comments if we are emitting a system module. 1812 if (moduleKind !== ModuleKind.System || currentLexicalScope !== currentSourceFile) { 1813 emitFlags |= EmitFlags.NoLeadingComments; 1814 } 1815 } 1816 1817 // `parameterName` is the declaration name used inside of the namespace. 1818 const parameterName = getNamespaceParameterName(node); 1819 1820 // `containerName` is the expression used inside of the namespace for exports. 1821 const containerName = getNamespaceContainerName(node); 1822 1823 // `exportName` is the expression used within this node's container for any exported references. 1824 const exportName = hasSyntacticModifier(node, ModifierFlags.Export) 1825 ? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true) 1826 : factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 1827 1828 // x || (x = {}) 1829 // exports.x || (exports.x = {}) 1830 let moduleArg = 1831 factory.createLogicalOr( 1832 exportName, 1833 factory.createAssignment( 1834 exportName, 1835 factory.createObjectLiteralExpression() 1836 ) 1837 ); 1838 1839 if (hasNamespaceQualifiedExportName(node)) { 1840 // `localName` is the expression used within this node's containing scope for any local references. 1841 const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); 1842 1843 // x = (exports.x || (exports.x = {})) 1844 moduleArg = factory.createAssignment(localName, moduleArg); 1845 } 1846 1847 // (function (x_1) { 1848 // x_1.y = ...; 1849 // })(x || (x = {})); 1850 const moduleStatement = factory.createExpressionStatement( 1851 factory.createCallExpression( 1852 factory.createFunctionExpression( 1853 /*modifiers*/ undefined, 1854 /*asteriskToken*/ undefined, 1855 /*name*/ undefined, 1856 /*typeParameters*/ undefined, 1857 [factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)], 1858 /*type*/ undefined, 1859 transformModuleBody(node, containerName) 1860 ), 1861 /*typeArguments*/ undefined, 1862 [moduleArg] 1863 ) 1864 ); 1865 1866 setOriginalNode(moduleStatement, node); 1867 if (varAdded) { 1868 // If a variable was added, synthetic comments are emitted on it, not on the moduleStatement. 1869 setSyntheticLeadingComments(moduleStatement, undefined); 1870 setSyntheticTrailingComments(moduleStatement, undefined); 1871 } 1872 setTextRange(moduleStatement, node); 1873 addEmitFlags(moduleStatement, emitFlags); 1874 statements.push(moduleStatement); 1875 1876 // Add a DeclarationMarker for the namespace to preserve trailing comments and mark 1877 // the end of the declaration. 1878 statements.push(factory.createEndOfDeclarationMarker(node)); 1879 return statements; 1880 } 1881 1882 /** 1883 * Transforms the body of a module declaration. 1884 * 1885 * @param node The module declaration node. 1886 */ 1887 function transformModuleBody(node: ModuleDeclaration, namespaceLocalName: Identifier): Block { 1888 const savedCurrentNamespaceContainerName = currentNamespaceContainerName; 1889 const savedCurrentNamespace = currentNamespace; 1890 const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; 1891 currentNamespaceContainerName = namespaceLocalName; 1892 currentNamespace = node; 1893 currentScopeFirstDeclarationsOfName = undefined; 1894 1895 const statements: Statement[] = []; 1896 startLexicalEnvironment(); 1897 1898 let statementsLocation: TextRange | undefined; 1899 let blockLocation: TextRange | undefined; 1900 if (node.body) { 1901 if (node.body.kind === SyntaxKind.ModuleBlock) { 1902 saveStateAndInvoke(node.body, body => addRange(statements, visitNodes((body as ModuleBlock).statements, namespaceElementVisitor, isStatement))); 1903 statementsLocation = node.body.statements; 1904 blockLocation = node.body; 1905 } 1906 else { 1907 const result = visitModuleDeclaration(node.body as ModuleDeclaration); 1908 if (result) { 1909 if (isArray(result)) { 1910 addRange(statements, result); 1911 } 1912 else { 1913 statements.push(result); 1914 } 1915 } 1916 1917 const moduleBlock = getInnerMostModuleDeclarationFromDottedModule(node)!.body as ModuleBlock; 1918 statementsLocation = moveRangePos(moduleBlock.statements, -1); 1919 } 1920 } 1921 1922 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 1923 currentNamespaceContainerName = savedCurrentNamespaceContainerName; 1924 currentNamespace = savedCurrentNamespace; 1925 currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName; 1926 1927 const block = factory.createBlock( 1928 setTextRange( 1929 factory.createNodeArray(statements), 1930 /*location*/ statementsLocation 1931 ), 1932 /*multiLine*/ true 1933 ); 1934 setTextRange(block, blockLocation); 1935 1936 // namespace hello.hi.world { 1937 // function foo() {} 1938 // 1939 // // TODO, blah 1940 // } 1941 // 1942 // should be emitted as 1943 // 1944 // var hello; 1945 // (function (hello) { 1946 // var hi; 1947 // (function (hi) { 1948 // var world; 1949 // (function (world) { 1950 // function foo() { } 1951 // // TODO, blah 1952 // })(world = hi.world || (hi.world = {})); 1953 // })(hi = hello.hi || (hello.hi = {})); 1954 // })(hello || (hello = {})); 1955 // We only want to emit comment on the namespace which contains block body itself, not the containing namespaces. 1956 if (!node.body || node.body.kind !== SyntaxKind.ModuleBlock) { 1957 setEmitFlags(block, getEmitFlags(block) | EmitFlags.NoComments); 1958 } 1959 return block; 1960 } 1961 1962 function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration | undefined { 1963 if (moduleDeclaration.body!.kind === SyntaxKind.ModuleDeclaration) { 1964 const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration.body as ModuleDeclaration); 1965 return recursiveInnerModule || moduleDeclaration.body as ModuleDeclaration; 1966 } 1967 } 1968 1969 /** 1970 * Visits an import declaration, eliding it if it is type-only or if it has an import clause that may be elided. 1971 * 1972 * @param node The import declaration node. 1973 */ 1974 function visitImportDeclaration(node: ImportDeclaration): VisitResult<Statement> { 1975 if (!node.importClause) { 1976 // Do not elide a side-effect only import declaration. 1977 // import "foo"; 1978 return node; 1979 } 1980 if (node.importClause.isTypeOnly) { 1981 // Always elide type-only imports 1982 return undefined; 1983 } 1984 1985 // Elide the declaration if the import clause was elided. 1986 const importClause = visitNode(node.importClause, visitImportClause, isImportClause); 1987 return importClause || 1988 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 1989 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error 1990 ? factory.updateImportDeclaration( 1991 node, 1992 /*modifiers*/ undefined, 1993 importClause, 1994 node.moduleSpecifier, 1995 node.assertClause) 1996 : undefined; 1997 } 1998 1999 /** 2000 * Visits an import clause, eliding it if its `name` and `namedBindings` may both be elided. 2001 * 2002 * @param node The import clause node. 2003 */ 2004 function visitImportClause(node: ImportClause): VisitResult<ImportClause> { 2005 Debug.assert(!node.isTypeOnly); 2006 // Elide the import clause if we elide both its name and its named bindings. 2007 const name = shouldEmitAliasDeclaration(node) ? node.name : undefined; 2008 const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings); 2009 return (name || namedBindings) ? factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings) : undefined; 2010 } 2011 2012 /** 2013 * Visits named import bindings, eliding them if their targets, their references, and the compilation settings allow. 2014 * 2015 * @param node The named import bindings node. 2016 */ 2017 function visitNamedImportBindings(node: NamedImportBindings): VisitResult<NamedImportBindings> { 2018 if (node.kind === SyntaxKind.NamespaceImport) { 2019 // Elide a namespace import if it is not referenced. 2020 return shouldEmitAliasDeclaration(node) ? node : undefined; 2021 } 2022 else { 2023 // Elide named imports if all of its import specifiers are elided and settings allow. 2024 const allowEmpty = compilerOptions.preserveValueImports && ( 2025 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 2026 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error); 2027 const elements = visitNodes(node.elements, visitImportSpecifier, isImportSpecifier); 2028 return allowEmpty || some(elements) ? factory.updateNamedImports(node, elements) : undefined; 2029 } 2030 } 2031 2032 /** 2033 * Visits an import specifier, eliding it if its target, its references, and the compilation settings allow. 2034 * 2035 * @param node The import specifier node. 2036 */ 2037 function visitImportSpecifier(node: ImportSpecifier): VisitResult<ImportSpecifier> { 2038 return !node.isTypeOnly && shouldEmitAliasDeclaration(node) ? node : undefined; 2039 } 2040 2041 /** 2042 * Visits an export assignment, eliding it if it does not contain a clause that resolves 2043 * to a value. 2044 * 2045 * @param node The export assignment node. 2046 */ 2047 function visitExportAssignment(node: ExportAssignment): VisitResult<Statement> { 2048 // Elide the export assignment if it does not reference a value. 2049 return resolver.isValueAliasDeclaration(node) 2050 ? visitEachChild(node, visitor, context) 2051 : undefined; 2052 } 2053 2054 /** 2055 * Visits an export declaration, eliding it if it does not contain a clause that resolves to a value. 2056 * 2057 * @param node The export declaration node. 2058 */ 2059 function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> { 2060 if (node.isTypeOnly) { 2061 return undefined; 2062 } 2063 2064 if (!node.exportClause || isNamespaceExport(node.exportClause)) { 2065 // never elide `export <whatever> from <whereever>` declarations - 2066 // they should be kept for sideffects/untyped exports, even when the 2067 // type checker doesn't know about any exports 2068 return node; 2069 } 2070 2071 // Elide the export declaration if all of its named exports are elided. 2072 const allowEmpty = !!node.moduleSpecifier && ( 2073 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 2074 compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error); 2075 const exportClause = visitNode( 2076 node.exportClause, 2077 (bindings: NamedExportBindings) => visitNamedExportBindings(bindings, allowEmpty), 2078 isNamedExportBindings); 2079 2080 return exportClause 2081 ? factory.updateExportDeclaration( 2082 node, 2083 /*modifiers*/ undefined, 2084 node.isTypeOnly, 2085 exportClause, 2086 node.moduleSpecifier, 2087 node.assertClause) 2088 : undefined; 2089 } 2090 2091 /** 2092 * Visits named exports, eliding it if it does not contain an export specifier that 2093 * resolves to a value. 2094 * 2095 * @param node The named exports node. 2096 */ 2097 function visitNamedExports(node: NamedExports, allowEmpty: boolean): VisitResult<NamedExports> { 2098 // Elide the named exports if all of its export specifiers were elided. 2099 const elements = visitNodes(node.elements, visitExportSpecifier, isExportSpecifier); 2100 return allowEmpty || some(elements) ? factory.updateNamedExports(node, elements) : undefined; 2101 } 2102 2103 function visitNamespaceExports(node: NamespaceExport): VisitResult<NamespaceExport> { 2104 return factory.updateNamespaceExport(node, visitNode(node.name, visitor, isIdentifier)); 2105 } 2106 2107 function visitNamedExportBindings(node: NamedExportBindings, allowEmpty: boolean): VisitResult<NamedExportBindings> { 2108 return isNamespaceExport(node) ? visitNamespaceExports(node) : visitNamedExports(node, allowEmpty); 2109 } 2110 2111 /** 2112 * Visits an export specifier, eliding it if it does not resolve to a value. 2113 * 2114 * @param node The export specifier node. 2115 */ 2116 function visitExportSpecifier(node: ExportSpecifier): VisitResult<ExportSpecifier> { 2117 // Elide an export specifier if it does not reference a value. 2118 return !node.isTypeOnly && resolver.isValueAliasDeclaration(node) ? node : undefined; 2119 } 2120 2121 /** 2122 * Determines whether to emit an import equals declaration. 2123 * 2124 * @param node The import equals declaration node. 2125 */ 2126 function shouldEmitImportEqualsDeclaration(node: ImportEqualsDeclaration) { 2127 // preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when 2128 // - current file is not external module 2129 // - import declaration is top level and target is value imported by entity name 2130 return shouldEmitAliasDeclaration(node) 2131 || (!isExternalModule(currentSourceFile) 2132 && resolver.isTopLevelValueImportEqualsWithEntityName(node)); 2133 } 2134 2135 /** 2136 * Visits an import equals declaration. 2137 * 2138 * @param node The import equals declaration node. 2139 */ 2140 function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> { 2141 // Always elide type-only imports 2142 if (node.isTypeOnly) { 2143 return undefined; 2144 } 2145 2146 if (isExternalModuleImportEqualsDeclaration(node)) { 2147 const isReferenced = shouldEmitAliasDeclaration(node); 2148 // If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'. 2149 if (!isReferenced && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) { 2150 return setOriginalNode( 2151 setTextRange( 2152 factory.createImportDeclaration( 2153 /*modifiers*/ undefined, 2154 /*importClause*/ undefined, 2155 node.moduleReference.expression, 2156 /*assertClause*/ undefined 2157 ), 2158 node, 2159 ), 2160 node, 2161 ); 2162 } 2163 2164 return isReferenced ? visitEachChild(node, visitor, context) : undefined; 2165 } 2166 2167 if (!shouldEmitImportEqualsDeclaration(node)) { 2168 return undefined; 2169 } 2170 2171 const moduleReference = createExpressionFromEntityName(factory, node.moduleReference as EntityName); 2172 setEmitFlags(moduleReference, EmitFlags.NoComments | EmitFlags.NoNestedComments); 2173 2174 if (isNamedExternalModuleExport(node) || !isExportOfNamespace(node)) { 2175 // export var ${name} = ${moduleReference}; 2176 // var ${name} = ${moduleReference}; 2177 return setOriginalNode( 2178 setTextRange( 2179 factory.createVariableStatement( 2180 visitNodes(node.modifiers, modifierVisitor, isModifier), 2181 factory.createVariableDeclarationList([ 2182 setOriginalNode( 2183 factory.createVariableDeclaration( 2184 node.name, 2185 /*exclamationToken*/ undefined, 2186 /*type*/ undefined, 2187 moduleReference 2188 ), 2189 node 2190 ) 2191 ]) 2192 ), 2193 node 2194 ), 2195 node 2196 ); 2197 } 2198 else { 2199 // exports.${name} = ${moduleReference}; 2200 return setOriginalNode( 2201 createNamespaceExport( 2202 node.name, 2203 moduleReference, 2204 node 2205 ), 2206 node 2207 ); 2208 } 2209 } 2210 2211 /** 2212 * Gets a value indicating whether the node is exported from a namespace. 2213 * 2214 * @param node The node to test. 2215 */ 2216 function isExportOfNamespace(node: Node) { 2217 return currentNamespace !== undefined && hasSyntacticModifier(node, ModifierFlags.Export); 2218 } 2219 2220 /** 2221 * Gets a value indicating whether the node is exported from an external module. 2222 * 2223 * @param node The node to test. 2224 */ 2225 function isExternalModuleExport(node: Node) { 2226 return currentNamespace === undefined && hasSyntacticModifier(node, ModifierFlags.Export); 2227 } 2228 2229 /** 2230 * Gets a value indicating whether the node is a named export from an external module. 2231 * 2232 * @param node The node to test. 2233 */ 2234 function isNamedExternalModuleExport(node: Node) { 2235 return isExternalModuleExport(node) 2236 && !hasSyntacticModifier(node, ModifierFlags.Default); 2237 } 2238 2239 /** 2240 * Gets a value indicating whether the node is the default export of an external module. 2241 * 2242 * @param node The node to test. 2243 */ 2244 function isDefaultExternalModuleExport(node: Node) { 2245 return isExternalModuleExport(node) 2246 && hasSyntacticModifier(node, ModifierFlags.Default); 2247 } 2248 2249 function addExportMemberAssignment(statements: Statement[], node: ClassDeclaration | FunctionDeclaration) { 2250 const expression = factory.createAssignment( 2251 factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true), 2252 factory.getLocalName(node) 2253 ); 2254 setSourceMapRange(expression, createRange(node.name ? node.name.pos : node.pos, node.end)); 2255 2256 const statement = factory.createExpressionStatement(expression); 2257 setSourceMapRange(statement, createRange(-1, node.end)); 2258 statements.push(statement); 2259 } 2260 2261 function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) { 2262 return setTextRange( 2263 factory.createExpressionStatement( 2264 factory.createAssignment( 2265 factory.getNamespaceMemberName(currentNamespaceContainerName, exportName, /*allowComments*/ false, /*allowSourceMaps*/ true), 2266 exportValue 2267 ) 2268 ), 2269 location 2270 ); 2271 } 2272 2273 function createNamespaceExportExpression(exportName: Identifier, exportValue: Expression, location?: TextRange) { 2274 return setTextRange(factory.createAssignment(getNamespaceMemberNameWithSourceMapsAndWithoutComments(exportName), exportValue), location); 2275 } 2276 2277 function getNamespaceMemberNameWithSourceMapsAndWithoutComments(name: Identifier) { 2278 return factory.getNamespaceMemberName(currentNamespaceContainerName, name, /*allowComments*/ false, /*allowSourceMaps*/ true); 2279 } 2280 2281 /** 2282 * Gets the declaration name used inside of a namespace or enum. 2283 */ 2284 function getNamespaceParameterName(node: ModuleDeclaration | EnumDeclaration) { 2285 const name = factory.getGeneratedNameForNode(node); 2286 setSourceMapRange(name, node.name); 2287 return name; 2288 } 2289 2290 /** 2291 * Gets the expression used to refer to a namespace or enum within the body 2292 * of its declaration. 2293 */ 2294 function getNamespaceContainerName(node: ModuleDeclaration | EnumDeclaration) { 2295 return factory.getGeneratedNameForNode(node); 2296 } 2297 2298 function enableSubstitutionForNonQualifiedEnumMembers() { 2299 if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers) === 0) { 2300 enabledSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; 2301 context.enableSubstitution(SyntaxKind.Identifier); 2302 } 2303 } 2304 2305 function enableSubstitutionForNamespaceExports() { 2306 if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) { 2307 enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; 2308 2309 // We need to enable substitutions for identifiers and shorthand property assignments. This allows us to 2310 // substitute the names of exported members of a namespace. 2311 context.enableSubstitution(SyntaxKind.Identifier); 2312 context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); 2313 2314 // We need to be notified when entering and exiting namespaces. 2315 context.enableEmitNotification(SyntaxKind.ModuleDeclaration); 2316 } 2317 } 2318 2319 function isTransformedModuleDeclaration(node: Node): boolean { 2320 return getOriginalNode(node).kind === SyntaxKind.ModuleDeclaration; 2321 } 2322 2323 function isTransformedEnumDeclaration(node: Node): boolean { 2324 return getOriginalNode(node).kind === SyntaxKind.EnumDeclaration; 2325 } 2326 2327 /** 2328 * Hook for node emit. 2329 * 2330 * @param hint A hint as to the intended usage of the node. 2331 * @param node The node to emit. 2332 * @param emit A callback used to emit the node in the printer. 2333 */ 2334 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { 2335 const savedApplicableSubstitutions = applicableSubstitutions; 2336 const savedCurrentSourceFile = currentSourceFile; 2337 2338 if (isSourceFile(node)) { 2339 currentSourceFile = node; 2340 } 2341 2342 if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) { 2343 applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; 2344 } 2345 2346 if (enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) { 2347 applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; 2348 } 2349 2350 previousOnEmitNode(hint, node, emitCallback); 2351 2352 applicableSubstitutions = savedApplicableSubstitutions; 2353 currentSourceFile = savedCurrentSourceFile; 2354 } 2355 2356 /** 2357 * Hooks node substitutions. 2358 * 2359 * @param hint A hint as to the intended usage of the node. 2360 * @param node The node to substitute. 2361 */ 2362 function onSubstituteNode(hint: EmitHint, node: Node) { 2363 node = previousOnSubstituteNode(hint, node); 2364 if (hint === EmitHint.Expression) { 2365 return substituteExpression(node as Expression); 2366 } 2367 else if (isShorthandPropertyAssignment(node)) { 2368 return substituteShorthandPropertyAssignment(node); 2369 } 2370 2371 return node; 2372 } 2373 2374 function substituteShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElementLike { 2375 if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) { 2376 const name = node.name; 2377 const exportedName = trySubstituteNamespaceExportedName(name); 2378 if (exportedName) { 2379 // A shorthand property with an assignment initializer is probably part of a 2380 // destructuring assignment 2381 if (node.objectAssignmentInitializer) { 2382 const initializer = factory.createAssignment(exportedName, node.objectAssignmentInitializer); 2383 return setTextRange(factory.createPropertyAssignment(name, initializer), node); 2384 } 2385 return setTextRange(factory.createPropertyAssignment(name, exportedName), node); 2386 } 2387 } 2388 return node; 2389 } 2390 2391 function substituteExpression(node: Expression) { 2392 switch (node.kind) { 2393 case SyntaxKind.Identifier: 2394 return substituteExpressionIdentifier(node as Identifier); 2395 case SyntaxKind.PropertyAccessExpression: 2396 return substitutePropertyAccessExpression(node as PropertyAccessExpression); 2397 case SyntaxKind.ElementAccessExpression: 2398 return substituteElementAccessExpression(node as ElementAccessExpression); 2399 } 2400 2401 return node; 2402 } 2403 2404 function substituteExpressionIdentifier(node: Identifier): Expression { 2405 return trySubstituteNamespaceExportedName(node) 2406 || node; 2407 } 2408 2409 function trySubstituteNamespaceExportedName(node: Identifier): Expression | undefined { 2410 // If this is explicitly a local name, do not substitute. 2411 if (enabledSubstitutions & applicableSubstitutions && !isGeneratedIdentifier(node) && !isLocalName(node)) { 2412 // If we are nested within a namespace declaration, we may need to qualifiy 2413 // an identifier that is exported from a merged namespace. 2414 const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false); 2415 if (container && container.kind !== SyntaxKind.SourceFile) { 2416 const substitute = 2417 (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || 2418 (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration); 2419 if (substitute) { 2420 return setTextRange( 2421 factory.createPropertyAccessExpression(factory.getGeneratedNameForNode(container), node), 2422 /*location*/ node 2423 ); 2424 } 2425 } 2426 } 2427 2428 return undefined; 2429 } 2430 2431 function substitutePropertyAccessExpression(node: PropertyAccessExpression) { 2432 return substituteConstantValue(node); 2433 } 2434 2435 function substituteElementAccessExpression(node: ElementAccessExpression) { 2436 return substituteConstantValue(node); 2437 } 2438 2439 function safeMultiLineComment(value: string): string { 2440 return value.replace(/\*\//g, "*_/"); 2441 } 2442 2443 function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression { 2444 const constantValue = tryGetConstEnumValue(node); 2445 if (constantValue !== undefined) { 2446 // track the constant value on the node for the printer in needsDotDotForPropertyAccess 2447 setConstantValue(node, constantValue); 2448 2449 const substitute = typeof constantValue === "string" ? factory.createStringLiteral(constantValue) : factory.createNumericLiteral(constantValue); 2450 if (!compilerOptions.removeComments) { 2451 const originalNode = getOriginalNode(node, isAccessExpression); 2452 2453 addSyntheticTrailingComment(substitute, SyntaxKind.MultiLineCommentTrivia, ` ${safeMultiLineComment(getTextOfNode(originalNode))} `); 2454 } 2455 return substitute; 2456 } 2457 2458 return node; 2459 } 2460 2461 function tryGetConstEnumValue(node: Node): string | number | undefined { 2462 if (compilerOptions.isolatedModules) { 2463 return undefined; 2464 } 2465 2466 return isPropertyAccessExpression(node) || isElementAccessExpression(node) ? resolver.getConstantValue(node) : undefined; 2467 } 2468 2469 function shouldEmitAliasDeclaration(node: Node): boolean { 2470 return isInJSFile(node) || 2471 (compilerOptions.preserveValueImports 2472 ? resolver.isValueAliasDeclaration(node) 2473 : resolver.isReferencedAliasDeclaration(node)); 2474 } 2475 } 2476} 2477