1/* @internal */ 2namespace ts { 3 4 // Compound nodes 5 6 export function createEmptyExports(factory: NodeFactory) { 7 return factory.createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([]), /*moduleSpecifier*/ undefined); 8 } 9 10 export function createMemberAccessForPropertyName(factory: NodeFactory, target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression { 11 if (isComputedPropertyName(memberName)) { 12 return setTextRange(factory.createElementAccessExpression(target, memberName.expression), location); 13 } 14 else { 15 const expression = setTextRange( 16 isIdentifierOrPrivateIdentifier(memberName) 17 ? factory.createPropertyAccessExpression(target, memberName) 18 : factory.createElementAccessExpression(target, memberName), 19 memberName 20 ); 21 getOrCreateEmitNode(expression).flags |= EmitFlags.NoNestedSourceMaps; 22 return expression; 23 } 24 } 25 26 function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment) { 27 // To ensure the emit resolver can properly resolve the namespace, we need to 28 // treat this identifier as if it were a source tree node by clearing the `Synthesized` 29 // flag and setting a parent node. 30 const react = parseNodeFactory.createIdentifier(reactNamespace || "React"); 31 // Set the parent that is in parse tree 32 // this makes sure that parent chain is intact for checker to traverse complete scope tree 33 setParent(react, getParseTreeNode(parent)); 34 return react; 35 } 36 37 function createJsxFactoryExpressionFromEntityName(factory: NodeFactory, jsxFactory: EntityName, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { 38 if (isQualifiedName(jsxFactory)) { 39 const left = createJsxFactoryExpressionFromEntityName(factory, jsxFactory.left, parent); 40 const right = factory.createIdentifier(idText(jsxFactory.right)) as Mutable<Identifier>; 41 right.escapedText = jsxFactory.right.escapedText; 42 return factory.createPropertyAccessExpression(left, right); 43 } 44 else { 45 return createReactNamespace(idText(jsxFactory), parent); 46 } 47 } 48 49 export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { 50 return jsxFactoryEntity ? 51 createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) : 52 factory.createPropertyAccessExpression( 53 createReactNamespace(reactNamespace, parent), 54 "createElement" 55 ); 56 } 57 58 function createJsxFragmentFactoryExpression(factory: NodeFactory, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { 59 return jsxFragmentFactoryEntity ? 60 createJsxFactoryExpressionFromEntityName(factory, jsxFragmentFactoryEntity, parent) : 61 factory.createPropertyAccessExpression( 62 createReactNamespace(reactNamespace, parent), 63 "Fragment" 64 ); 65 } 66 67 export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression { 68 const argumentsList = [tagName]; 69 if (props) { 70 argumentsList.push(props); 71 } 72 73 if (children && children.length > 0) { 74 if (!props) { 75 argumentsList.push(factory.createNull()); 76 } 77 78 if (children.length > 1) { 79 for (const child of children) { 80 startOnNewLine(child); 81 argumentsList.push(child); 82 } 83 } 84 else { 85 argumentsList.push(children[0]); 86 } 87 } 88 89 return setTextRange( 90 factory.createCallExpression( 91 callee, 92 /*typeArguments*/ undefined, 93 argumentsList 94 ), 95 location 96 ); 97 } 98 99 export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression { 100 const tagName = createJsxFragmentFactoryExpression(factory, jsxFragmentFactoryEntity, reactNamespace, parentElement); 101 const argumentsList = [tagName, factory.createNull()]; 102 103 if (children && children.length > 0) { 104 if (children.length > 1) { 105 for (const child of children) { 106 startOnNewLine(child); 107 argumentsList.push(child); 108 } 109 } 110 else { 111 argumentsList.push(children[0]); 112 } 113 } 114 115 return setTextRange( 116 factory.createCallExpression( 117 createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement), 118 /*typeArguments*/ undefined, 119 argumentsList 120 ), 121 location 122 ); 123 } 124 125 // Utilities 126 127 export function createForOfBindingStatement(factory: NodeFactory, node: ForInitializer, boundValue: Expression): Statement { 128 if (isVariableDeclarationList(node)) { 129 const firstDeclaration = first(node.declarations); 130 const updatedDeclaration = factory.updateVariableDeclaration( 131 firstDeclaration, 132 firstDeclaration.name, 133 /*exclamationToken*/ undefined, 134 /*type*/ undefined, 135 boundValue 136 ); 137 return setTextRange( 138 factory.createVariableStatement( 139 /*modifiers*/ undefined, 140 factory.updateVariableDeclarationList(node, [updatedDeclaration]) 141 ), 142 /*location*/ node 143 ); 144 } 145 else { 146 const updatedExpression = setTextRange(factory.createAssignment(node, boundValue), /*location*/ node); 147 return setTextRange(factory.createExpressionStatement(updatedExpression), /*location*/ node); 148 } 149 } 150 151 export function insertLeadingStatement(factory: NodeFactory, dest: Statement, source: Statement) { 152 if (isBlock(dest)) { 153 return factory.updateBlock(dest, setTextRange(factory.createNodeArray([source, ...dest.statements]), dest.statements)); 154 } 155 else { 156 return factory.createBlock(factory.createNodeArray([dest, source]), /*multiLine*/ true); 157 } 158 } 159 160 export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName | Expression): Expression { 161 if (isQualifiedName(node)) { 162 const left = createExpressionFromEntityName(factory, node.left); 163 // TODO(rbuckton): Does this need to be parented? 164 const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent); 165 return setTextRange(factory.createPropertyAccessExpression(left, right), node); 166 } 167 else { 168 // TODO(rbuckton): Does this need to be parented? 169 return setParent(setTextRange(factory.cloneNode(node), node), node.parent); 170 } 171 } 172 173 export function createExpressionForPropertyName(factory: NodeFactory, memberName: Exclude<PropertyName, PrivateIdentifier>): Expression { 174 if (isIdentifier(memberName)) { 175 return factory.createStringLiteralFromNode(memberName); 176 } 177 else if (isComputedPropertyName(memberName)) { 178 // TODO(rbuckton): Does this need to be parented? 179 return setParent(setTextRange(factory.cloneNode(memberName.expression), memberName.expression), memberName.expression.parent); 180 } 181 else { 182 // TODO(rbuckton): Does this need to be parented? 183 return setParent(setTextRange(factory.cloneNode(memberName), memberName), memberName.parent); 184 } 185 } 186 187 function createExpressionForAccessorDeclaration(factory: NodeFactory, properties: NodeArray<Declaration>, property: AccessorDeclaration & { readonly name: Exclude<PropertyName, PrivateIdentifier>; }, receiver: Expression, multiLine: boolean) { 188 const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property); 189 if (property === firstAccessor) { 190 return setTextRange( 191 factory.createObjectDefinePropertyCall( 192 receiver, 193 createExpressionForPropertyName(factory, property.name), 194 factory.createPropertyDescriptor({ 195 enumerable: factory.createFalse(), 196 configurable: true, 197 get: getAccessor && setTextRange( 198 setOriginalNode( 199 factory.createFunctionExpression( 200 getAccessor.modifiers, 201 /*asteriskToken*/ undefined, 202 /*name*/ undefined, 203 /*typeParameters*/ undefined, 204 getAccessor.parameters, 205 /*type*/ undefined, 206 getAccessor.body! // TODO: GH#18217 207 ), 208 getAccessor 209 ), 210 getAccessor 211 ), 212 set: setAccessor && setTextRange( 213 setOriginalNode( 214 factory.createFunctionExpression( 215 setAccessor.modifiers, 216 /*asteriskToken*/ undefined, 217 /*name*/ undefined, 218 /*typeParameters*/ undefined, 219 setAccessor.parameters, 220 /*type*/ undefined, 221 setAccessor.body! // TODO: GH#18217 222 ), 223 setAccessor 224 ), 225 setAccessor 226 ) 227 }, !multiLine) 228 ), 229 firstAccessor 230 ); 231 } 232 233 return undefined; 234 } 235 236 function createExpressionForPropertyAssignment(factory: NodeFactory, property: PropertyAssignment, receiver: Expression) { 237 return setOriginalNode( 238 setTextRange( 239 factory.createAssignment( 240 createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), 241 property.initializer 242 ), 243 property 244 ), 245 property 246 ); 247 } 248 249 function createExpressionForShorthandPropertyAssignment(factory: NodeFactory, property: ShorthandPropertyAssignment, receiver: Expression) { 250 return setOriginalNode( 251 setTextRange( 252 factory.createAssignment( 253 createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), 254 factory.cloneNode(property.name) 255 ), 256 /*location*/ property 257 ), 258 /*original*/ property 259 ); 260 } 261 262 function createExpressionForMethodDeclaration(factory: NodeFactory, method: MethodDeclaration, receiver: Expression) { 263 return setOriginalNode( 264 setTextRange( 265 factory.createAssignment( 266 createMemberAccessForPropertyName(factory, receiver, method.name, /*location*/ method.name), 267 setOriginalNode( 268 setTextRange( 269 factory.createFunctionExpression( 270 method.modifiers, 271 method.asteriskToken, 272 /*name*/ undefined, 273 /*typeParameters*/ undefined, 274 method.parameters, 275 /*type*/ undefined, 276 method.body! // TODO: GH#18217 277 ), 278 /*location*/ method 279 ), 280 /*original*/ method 281 ) 282 ), 283 /*location*/ method 284 ), 285 /*original*/ method 286 ); 287 } 288 289 export function createExpressionForObjectLiteralElementLike(factory: NodeFactory, node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined { 290 if (property.name && isPrivateIdentifier(property.name)) { 291 Debug.failBadSyntaxKind(property.name, "Private identifiers are not allowed in object literals."); 292 } 293 switch (property.kind) { 294 case SyntaxKind.GetAccessor: 295 case SyntaxKind.SetAccessor: 296 return createExpressionForAccessorDeclaration(factory, node.properties, property as typeof property & { readonly name: Exclude<PropertyName, PrivateIdentifier> }, receiver, !!node.multiLine); 297 case SyntaxKind.PropertyAssignment: 298 return createExpressionForPropertyAssignment(factory, property, receiver); 299 case SyntaxKind.ShorthandPropertyAssignment: 300 return createExpressionForShorthandPropertyAssignment(factory, property, receiver); 301 case SyntaxKind.MethodDeclaration: 302 return createExpressionForMethodDeclaration(factory, property, receiver); 303 } 304 } 305 306 /** 307 * Gets whether an identifier should only be referred to by its internal name. 308 */ 309 export function isInternalName(node: Identifier) { 310 return (getEmitFlags(node) & EmitFlags.InternalName) !== 0; 311 } 312 313 /** 314 * Gets whether an identifier should only be referred to by its local name. 315 */ 316 export function isLocalName(node: Identifier) { 317 return (getEmitFlags(node) & EmitFlags.LocalName) !== 0; 318 } 319 320 /** 321 * Gets whether an identifier should only be referred to by its export representation if the 322 * name points to an exported symbol. 323 */ 324 export function isExportName(node: Identifier) { 325 return (getEmitFlags(node) & EmitFlags.ExportName) !== 0; 326 } 327 328 function isUseStrictPrologue(node: ExpressionStatement): boolean { 329 return isStringLiteral(node.expression) && node.expression.text === "use strict"; 330 } 331 332 export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined { 333 for (const statement of statements) { 334 if (isPrologueDirective(statement)) { 335 if (isUseStrictPrologue(statement)) { 336 return statement; 337 } 338 } 339 else { 340 break; 341 } 342 } 343 return undefined; 344 } 345 346 export function startsWithUseStrict(statements: readonly Statement[]) { 347 const firstStatement = firstOrUndefined(statements); 348 return firstStatement !== undefined 349 && isPrologueDirective(firstStatement) 350 && isUseStrictPrologue(firstStatement); 351 } 352 353 export function isCommaSequence(node: Expression): node is BinaryExpression & {operatorToken: Token<SyntaxKind.CommaToken>} | CommaListExpression { 354 return node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.CommaToken || 355 node.kind === SyntaxKind.CommaListExpression; 356 } 357 358 export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression { 359 switch (node.kind) { 360 case SyntaxKind.ParenthesizedExpression: 361 return (kinds & OuterExpressionKinds.Parentheses) !== 0; 362 case SyntaxKind.TypeAssertionExpression: 363 case SyntaxKind.AsExpression: 364 return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; 365 case SyntaxKind.NonNullExpression: 366 return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; 367 case SyntaxKind.PartiallyEmittedExpression: 368 return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; 369 } 370 return false; 371 } 372 373 export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; 374 export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; 375 export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { 376 while (isOuterExpression(node, kinds)) { 377 node = node.expression; 378 } 379 return node; 380 } 381 382 export function skipAssertions(node: Expression): Expression; 383 export function skipAssertions(node: Node): Node; 384 export function skipAssertions(node: Node): Node { 385 return skipOuterExpressions(node, OuterExpressionKinds.Assertions); 386 } 387 388 export function startOnNewLine<T extends Node>(node: T): T { 389 return setStartsOnNewLine(node, /*newLine*/ true); 390 } 391 392 export function getExternalHelpersModuleName(node: SourceFile) { 393 const parseNode = getOriginalNode(node, isSourceFile); 394 const emitNode = parseNode && parseNode.emitNode; 395 return emitNode && emitNode.externalHelpersModuleName; 396 } 397 398 export function hasRecordedExternalHelpers(sourceFile: SourceFile) { 399 const parseNode = getOriginalNode(sourceFile, isSourceFile); 400 const emitNode = parseNode && parseNode.emitNode; 401 return !!emitNode && (!!emitNode.externalHelpersModuleName || !!emitNode.externalHelpers); 402 } 403 404 export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: NodeFactory, helperFactory: EmitHelperFactory, sourceFile: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStar?: boolean, hasImportDefault?: boolean) { 405 if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) { 406 let namedBindings: NamedImportBindings | undefined; 407 const moduleKind = getEmitModuleKind(compilerOptions); 408 if (moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) { 409 // use named imports 410 const helpers = getEmitHelpers(sourceFile); 411 if (helpers) { 412 const helperNames: string[] = []; 413 for (const helper of helpers) { 414 if (!helper.scoped) { 415 const importName = (helper as UnscopedEmitHelper).importName; 416 if (importName) { 417 pushIfUnique(helperNames, importName); 418 } 419 } 420 } 421 if (some(helperNames)) { 422 helperNames.sort(compareStringsCaseSensitive); 423 // Alias the imports if the names are used somewhere in the file. 424 // NOTE: We don't need to care about global import collisions as this is a module. 425 namedBindings = nodeFactory.createNamedImports( 426 map(helperNames, name => isFileLevelUniqueName(sourceFile, name) 427 ? nodeFactory.createImportSpecifier(/*propertyName*/ undefined, nodeFactory.createIdentifier(name)) 428 : nodeFactory.createImportSpecifier(nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name)) 429 ) 430 ); 431 const parseNode = getOriginalNode(sourceFile, isSourceFile); 432 const emitNode = getOrCreateEmitNode(parseNode); 433 emitNode.externalHelpers = true; 434 } 435 } 436 } 437 else { 438 // use a namespace import 439 const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(nodeFactory, sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStar || hasImportDefault); 440 if (externalHelpersModuleName) { 441 namedBindings = nodeFactory.createNamespaceImport(externalHelpersModuleName); 442 } 443 } 444 if (namedBindings) { 445 const externalHelpersImportDeclaration = nodeFactory.createImportDeclaration( 446 /*decorators*/ undefined, 447 /*modifiers*/ undefined, 448 nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings), 449 nodeFactory.createStringLiteral(externalHelpersModuleNameText) 450 ); 451 addEmitFlags(externalHelpersImportDeclaration, EmitFlags.NeverApplyImportHelper); 452 return externalHelpersImportDeclaration; 453 } 454 } 455 } 456 457 export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactory, node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) { 458 if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) { 459 const externalHelpersModuleName = getExternalHelpersModuleName(node); 460 if (externalHelpersModuleName) { 461 return externalHelpersModuleName; 462 } 463 464 const moduleKind = getEmitModuleKind(compilerOptions); 465 let create = (hasExportStarsToExportValues || (compilerOptions.esModuleInterop && hasImportStarOrImportDefault)) 466 && moduleKind !== ModuleKind.System 467 && moduleKind < ModuleKind.ES2015; 468 if (!create) { 469 const helpers = getEmitHelpers(node); 470 if (helpers) { 471 for (const helper of helpers) { 472 if (!helper.scoped) { 473 create = true; 474 break; 475 } 476 } 477 } 478 } 479 480 if (create) { 481 const parseNode = getOriginalNode(node, isSourceFile); 482 const emitNode = getOrCreateEmitNode(parseNode); 483 return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = factory.createUniqueName(externalHelpersModuleNameText)); 484 } 485 } 486 } 487 488 /** 489 * Get the name of that target module from an import or export declaration 490 */ 491 export function getLocalNameForExternalImport(factory: NodeFactory, node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile): Identifier | undefined { 492 const namespaceDeclaration = getNamespaceDeclarationNode(node); 493 if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) { 494 const name = namespaceDeclaration.name; 495 return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name)); 496 } 497 if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) { 498 return factory.getGeneratedNameForNode(node); 499 } 500 if (node.kind === SyntaxKind.ExportDeclaration && node.moduleSpecifier) { 501 return factory.getGeneratedNameForNode(node); 502 } 503 return undefined; 504 } 505 506 /** 507 * Get the name of a target module from an import/export declaration as should be written in the emitted output. 508 * The emitted output name can be different from the input if: 509 * 1. The module has a /// <amd-module name="<new name>" /> 510 * 2. --out or --outFile is used, making the name relative to the rootDir 511 * 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System). 512 * Otherwise, a new StringLiteral node representing the module name will be returned. 513 */ 514 export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { 515 const moduleName = getExternalModuleName(importNode); 516 if (moduleName && isStringLiteral(moduleName)) { 517 return tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions) 518 || tryRenameExternalModule(factory, moduleName, sourceFile) 519 || factory.cloneNode(moduleName); 520 } 521 522 return undefined; 523 } 524 525 /** 526 * Some bundlers (SystemJS builder) sometimes want to rename dependencies. 527 * Here we check if alternative name was provided for a given moduleName and return it if possible. 528 */ 529 function tryRenameExternalModule(factory: NodeFactory, moduleName: LiteralExpression, sourceFile: SourceFile) { 530 const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); 531 return rename ? factory.createStringLiteral(rename) : undefined; 532 } 533 534 /** 535 * Get the name of a module as should be written in the emitted output. 536 * The emitted output name can be different from the input if: 537 * 1. The module has a /// <amd-module name="<new name>" /> 538 * 2. --out or --outFile is used, making the name relative to the rootDir 539 * Otherwise, a new StringLiteral node representing the module name will be returned. 540 */ 541 export function tryGetModuleNameFromFile(factory: NodeFactory, file: SourceFile | undefined, host: EmitHost, options: CompilerOptions): StringLiteral | undefined { 542 if (!file) { 543 return undefined; 544 } 545 if (file.moduleName) { 546 return factory.createStringLiteral(file.moduleName); 547 } 548 if (!file.isDeclarationFile && outFile(options)) { 549 return factory.createStringLiteral(getExternalModuleNameFromPath(host, file.fileName)); 550 } 551 return undefined; 552 } 553 554 function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) { 555 return tryGetModuleNameFromFile(factory, resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); 556 } 557 558 /** 559 * Gets the initializer of an BindingOrAssignmentElement. 560 */ 561 export function getInitializerOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Expression | undefined { 562 if (isDeclarationBindingElement(bindingElement)) { 563 // `1` in `let { a = 1 } = ...` 564 // `1` in `let { a: b = 1 } = ...` 565 // `1` in `let { a: {b} = 1 } = ...` 566 // `1` in `let { a: [b] = 1 } = ...` 567 // `1` in `let [a = 1] = ...` 568 // `1` in `let [{a} = 1] = ...` 569 // `1` in `let [[a] = 1] = ...` 570 return bindingElement.initializer; 571 } 572 573 if (isPropertyAssignment(bindingElement)) { 574 // `1` in `({ a: b = 1 } = ...)` 575 // `1` in `({ a: {b} = 1 } = ...)` 576 // `1` in `({ a: [b] = 1 } = ...)` 577 const initializer = bindingElement.initializer; 578 return isAssignmentExpression(initializer, /*excludeCompoundAssignment*/ true) 579 ? initializer.right 580 : undefined; 581 } 582 583 if (isShorthandPropertyAssignment(bindingElement)) { 584 // `1` in `({ a = 1 } = ...)` 585 return bindingElement.objectAssignmentInitializer; 586 } 587 588 if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { 589 // `1` in `[a = 1] = ...` 590 // `1` in `[{a} = 1] = ...` 591 // `1` in `[[a] = 1] = ...` 592 return bindingElement.right; 593 } 594 595 if (isSpreadElement(bindingElement)) { 596 // Recovery consistent with existing emit. 597 return getInitializerOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.expression); 598 } 599 } 600 601 /** 602 * Gets the name of an BindingOrAssignmentElement. 603 */ 604 export function getTargetOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementTarget | undefined { 605 if (isDeclarationBindingElement(bindingElement)) { 606 // `a` in `let { a } = ...` 607 // `a` in `let { a = 1 } = ...` 608 // `b` in `let { a: b } = ...` 609 // `b` in `let { a: b = 1 } = ...` 610 // `a` in `let { ...a } = ...` 611 // `{b}` in `let { a: {b} } = ...` 612 // `{b}` in `let { a: {b} = 1 } = ...` 613 // `[b]` in `let { a: [b] } = ...` 614 // `[b]` in `let { a: [b] = 1 } = ...` 615 // `a` in `let [a] = ...` 616 // `a` in `let [a = 1] = ...` 617 // `a` in `let [...a] = ...` 618 // `{a}` in `let [{a}] = ...` 619 // `{a}` in `let [{a} = 1] = ...` 620 // `[a]` in `let [[a]] = ...` 621 // `[a]` in `let [[a] = 1] = ...` 622 return bindingElement.name; 623 } 624 625 if (isObjectLiteralElementLike(bindingElement)) { 626 switch (bindingElement.kind) { 627 case SyntaxKind.PropertyAssignment: 628 // `b` in `({ a: b } = ...)` 629 // `b` in `({ a: b = 1 } = ...)` 630 // `{b}` in `({ a: {b} } = ...)` 631 // `{b}` in `({ a: {b} = 1 } = ...)` 632 // `[b]` in `({ a: [b] } = ...)` 633 // `[b]` in `({ a: [b] = 1 } = ...)` 634 // `b.c` in `({ a: b.c } = ...)` 635 // `b.c` in `({ a: b.c = 1 } = ...)` 636 // `b[0]` in `({ a: b[0] } = ...)` 637 // `b[0]` in `({ a: b[0] = 1 } = ...)` 638 return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.initializer); 639 640 case SyntaxKind.ShorthandPropertyAssignment: 641 // `a` in `({ a } = ...)` 642 // `a` in `({ a = 1 } = ...)` 643 return bindingElement.name; 644 645 case SyntaxKind.SpreadAssignment: 646 // `a` in `({ ...a } = ...)` 647 return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.expression); 648 } 649 650 // no target 651 return undefined; 652 } 653 654 if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { 655 // `a` in `[a = 1] = ...` 656 // `{a}` in `[{a} = 1] = ...` 657 // `[a]` in `[[a] = 1] = ...` 658 // `a.b` in `[a.b = 1] = ...` 659 // `a[0]` in `[a[0] = 1] = ...` 660 return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.left); 661 } 662 663 if (isSpreadElement(bindingElement)) { 664 // `a` in `[...a] = ...` 665 return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.expression); 666 } 667 668 // `a` in `[a] = ...` 669 // `{a}` in `[{a}] = ...` 670 // `[a]` in `[[a]] = ...` 671 // `a.b` in `[a.b] = ...` 672 // `a[0]` in `[a[0]] = ...` 673 return bindingElement; 674 } 675 676 /** 677 * Determines whether an BindingOrAssignmentElement is a rest element. 678 */ 679 export function getRestIndicatorOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementRestIndicator | undefined { 680 switch (bindingElement.kind) { 681 case SyntaxKind.Parameter: 682 case SyntaxKind.BindingElement: 683 // `...` in `let [...a] = ...` 684 return bindingElement.dotDotDotToken; 685 686 case SyntaxKind.SpreadElement: 687 case SyntaxKind.SpreadAssignment: 688 // `...` in `[...a] = ...` 689 return bindingElement; 690 } 691 692 return undefined; 693 } 694 695 /** 696 * Gets the property name of a BindingOrAssignmentElement 697 */ 698 export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined { 699 const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement); 700 Debug.assert(!!propertyName || isSpreadAssignment(bindingElement), "Invalid property name for binding element."); 701 return propertyName; 702 } 703 704 export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined { 705 switch (bindingElement.kind) { 706 case SyntaxKind.BindingElement: 707 // `a` in `let { a: b } = ...` 708 // `[a]` in `let { [a]: b } = ...` 709 // `"a"` in `let { "a": b } = ...` 710 // `1` in `let { 1: b } = ...` 711 if (bindingElement.propertyName) { 712 const propertyName = bindingElement.propertyName; 713 if (isPrivateIdentifier(propertyName)) { 714 return Debug.failBadSyntaxKind(propertyName); 715 } 716 return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression) 717 ? propertyName.expression 718 : propertyName; 719 } 720 721 break; 722 723 case SyntaxKind.PropertyAssignment: 724 // `a` in `({ a: b } = ...)` 725 // `[a]` in `({ [a]: b } = ...)` 726 // `"a"` in `({ "a": b } = ...)` 727 // `1` in `({ 1: b } = ...)` 728 if (bindingElement.name) { 729 const propertyName = bindingElement.name; 730 if (isPrivateIdentifier(propertyName)) { 731 return Debug.failBadSyntaxKind(propertyName); 732 } 733 return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression) 734 ? propertyName.expression 735 : propertyName; 736 } 737 738 break; 739 740 case SyntaxKind.SpreadAssignment: 741 // `a` in `({ ...a } = ...)` 742 if (bindingElement.name && isPrivateIdentifier(bindingElement.name)) { 743 return Debug.failBadSyntaxKind(bindingElement.name); 744 } 745 return bindingElement.name; 746 } 747 748 const target = getTargetOfBindingOrAssignmentElement(bindingElement); 749 if (target && isPropertyName(target)) { 750 return target; 751 } 752 } 753 754 function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral { 755 const kind = node.kind; 756 return kind === SyntaxKind.StringLiteral 757 || kind === SyntaxKind.NumericLiteral; 758 } 759 760 /** 761 * Gets the elements of a BindingOrAssignmentPattern 762 */ 763 export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): readonly BindingOrAssignmentElement[] { 764 switch (name.kind) { 765 case SyntaxKind.ObjectBindingPattern: 766 case SyntaxKind.ArrayBindingPattern: 767 case SyntaxKind.ArrayLiteralExpression: 768 // `a` in `{a}` 769 // `a` in `[a]` 770 return <readonly BindingOrAssignmentElement[]>name.elements; 771 772 case SyntaxKind.ObjectLiteralExpression: 773 // `a` in `{a}` 774 return <readonly BindingOrAssignmentElement[]>name.properties; 775 } 776 } 777 778 /* @internal */ 779 export function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) { 780 if (fullName) { 781 let rightNode = fullName; 782 while (true) { 783 if (isIdentifier(rightNode) || !rightNode.body) { 784 return isIdentifier(rightNode) ? rightNode : rightNode.name; 785 } 786 rightNode = rightNode.body; 787 } 788 } 789 } 790 791 export function canHaveModifiers(node: Node): node is HasModifiers { 792 const kind = node.kind; 793 return kind === SyntaxKind.Parameter 794 || kind === SyntaxKind.PropertySignature 795 || kind === SyntaxKind.PropertyDeclaration 796 || kind === SyntaxKind.MethodSignature 797 || kind === SyntaxKind.MethodDeclaration 798 || kind === SyntaxKind.Constructor 799 || kind === SyntaxKind.GetAccessor 800 || kind === SyntaxKind.SetAccessor 801 || kind === SyntaxKind.IndexSignature 802 || kind === SyntaxKind.FunctionExpression 803 || kind === SyntaxKind.ArrowFunction 804 || kind === SyntaxKind.ClassExpression 805 || kind === SyntaxKind.VariableStatement 806 || kind === SyntaxKind.FunctionDeclaration 807 || kind === SyntaxKind.ClassDeclaration 808 || kind === SyntaxKind.StructDeclaration 809 || kind === SyntaxKind.InterfaceDeclaration 810 || kind === SyntaxKind.TypeAliasDeclaration 811 || kind === SyntaxKind.EnumDeclaration 812 || kind === SyntaxKind.ModuleDeclaration 813 || kind === SyntaxKind.ImportEqualsDeclaration 814 || kind === SyntaxKind.ImportDeclaration 815 || kind === SyntaxKind.ExportAssignment 816 || kind === SyntaxKind.ExportDeclaration; 817 } 818 819 /* @internal */ 820 export function isExportModifier(node: Modifier): node is ExportKeyword { 821 return node.kind === SyntaxKind.ExportKeyword; 822 } 823 824 /* @internal */ 825 export function isAsyncModifier(node: Modifier): node is AsyncKeyword { 826 return node.kind === SyntaxKind.AsyncKeyword; 827 } 828 829 /* @internal */ 830 export function isStaticModifier(node: Modifier): node is StaticKeyword { 831 return node.kind === SyntaxKind.StaticKeyword; 832 } 833} 834