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