1/* @internal */ 2namespace ts { 3 4 // Compound nodes 5 6 export function createEmptyExports(factory: NodeFactory) { 7 return factory.createExportDeclaration(/*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 isMemberName(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 getModifiers(getAccessor), 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 getModifiers(setAccessor), 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 getModifiers(method), 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 * Expand the read and increment/decrement operations a pre- or post-increment or pre- or post-decrement expression. 308 * 309 * ```ts 310 * // input 311 * <expression>++ 312 * // output (if result is not discarded) 313 * var <temp>; 314 * (<temp> = <expression>, <resultVariable> = <temp>++, <temp>) 315 * // output (if result is discarded) 316 * var <temp>; 317 * (<temp> = <expression>, <temp>++, <temp>) 318 * 319 * // input 320 * ++<expression> 321 * // output (if result is not discarded) 322 * var <temp>; 323 * (<temp> = <expression>, <resultVariable> = ++<temp>) 324 * // output (if result is discarded) 325 * var <temp>; 326 * (<temp> = <expression>, ++<temp>) 327 * ``` 328 * 329 * It is up to the caller to supply a temporary variable for `<resultVariable>` if one is needed. 330 * The temporary variable `<temp>` is injected so that `++` and `--` work uniformly with `number` and `bigint`. 331 * The result of the expression is always the final result of incrementing or decrementing the expression, so that it can be used for storage. 332 * 333 * @param factory {@link NodeFactory} used to create the expanded representation. 334 * @param node The original prefix or postfix unary node. 335 * @param expression The expression to use as the value to increment or decrement 336 * @param resultVariable A temporary variable in which to store the result. Pass `undefined` if the result is discarded, or if the value of `<temp>` is the expected result. 337 */ 338 export function expandPreOrPostfixIncrementOrDecrementExpression(factory: NodeFactory, node: PrefixUnaryExpression | PostfixUnaryExpression, expression: Expression, recordTempVariable: (node: Identifier) => void, resultVariable: Identifier | undefined) { 339 const operator = node.operator; 340 Debug.assert(operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken, "Expected 'node' to be a pre- or post-increment or pre- or post-decrement expression"); 341 342 const temp = factory.createTempVariable(recordTempVariable); 343 expression = factory.createAssignment(temp, expression); 344 setTextRange(expression, node.operand); 345 346 let operation: Expression = isPrefixUnaryExpression(node) ? 347 factory.createPrefixUnaryExpression(operator, temp) : 348 factory.createPostfixUnaryExpression(temp, operator); 349 setTextRange(operation, node); 350 351 if (resultVariable) { 352 operation = factory.createAssignment(resultVariable, operation); 353 setTextRange(operation, node); 354 } 355 356 expression = factory.createComma(expression, operation); 357 setTextRange(expression, node); 358 359 if (isPostfixUnaryExpression(node)) { 360 expression = factory.createComma(expression, temp); 361 setTextRange(expression, node); 362 } 363 364 return expression; 365 } 366 367 /** 368 * Gets whether an identifier should only be referred to by its internal name. 369 */ 370 export function isInternalName(node: Identifier) { 371 return (getEmitFlags(node) & EmitFlags.InternalName) !== 0; 372 } 373 374 /** 375 * Gets whether an identifier should only be referred to by its local name. 376 */ 377 export function isLocalName(node: Identifier) { 378 return (getEmitFlags(node) & EmitFlags.LocalName) !== 0; 379 } 380 381 /** 382 * Gets whether an identifier should only be referred to by its export representation if the 383 * name points to an exported symbol. 384 */ 385 export function isExportName(node: Identifier) { 386 return (getEmitFlags(node) & EmitFlags.ExportName) !== 0; 387 } 388 389 function isUseStrictPrologue(node: ExpressionStatement): boolean { 390 return isStringLiteral(node.expression) && node.expression.text === "use strict"; 391 } 392 393 export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined { 394 for (const statement of statements) { 395 if (isPrologueDirective(statement)) { 396 if (isUseStrictPrologue(statement)) { 397 return statement; 398 } 399 } 400 else { 401 break; 402 } 403 } 404 return undefined; 405 } 406 407 export function startsWithUseStrict(statements: readonly Statement[]) { 408 const firstStatement = firstOrUndefined(statements); 409 return firstStatement !== undefined 410 && isPrologueDirective(firstStatement) 411 && isUseStrictPrologue(firstStatement); 412 } 413 414 export function isCommaSequence(node: Expression): node is BinaryExpression & {operatorToken: Token<SyntaxKind.CommaToken>} | CommaListExpression { 415 return node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.CommaToken || 416 node.kind === SyntaxKind.CommaListExpression; 417 } 418 419 export function isJSDocTypeAssertion(node: Node): node is JSDocTypeAssertion { 420 return isParenthesizedExpression(node) 421 && isInJSFile(node) 422 && !!getJSDocTypeTag(node); 423 } 424 425 export function getJSDocTypeAssertionType(node: JSDocTypeAssertion) { 426 const type = getJSDocType(node); 427 Debug.assertIsDefined(type); 428 return type; 429 } 430 431 export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression { 432 switch (node.kind) { 433 case SyntaxKind.ParenthesizedExpression: 434 if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node)) { 435 return false; 436 } 437 return (kinds & OuterExpressionKinds.Parentheses) !== 0; 438 case SyntaxKind.TypeAssertionExpression: 439 case SyntaxKind.AsExpression: 440 case SyntaxKind.SatisfiesExpression: 441 return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; 442 case SyntaxKind.NonNullExpression: 443 return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; 444 case SyntaxKind.PartiallyEmittedExpression: 445 return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; 446 } 447 return false; 448 } 449 450 export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; 451 export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; 452 export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { 453 while (isOuterExpression(node, kinds)) { 454 node = node.expression; 455 } 456 return node; 457 } 458 459 export function skipAssertions(node: Expression): Expression; 460 export function skipAssertions(node: Node): Node; 461 export function skipAssertions(node: Node): Node { 462 return skipOuterExpressions(node, OuterExpressionKinds.Assertions); 463 } 464 465 export function startOnNewLine<T extends Node>(node: T): T { 466 return setStartsOnNewLine(node, /*newLine*/ true); 467 } 468 469 export function getExternalHelpersModuleName(node: SourceFile) { 470 const parseNode = getOriginalNode(node, isSourceFile); 471 const emitNode = parseNode && parseNode.emitNode; 472 return emitNode && emitNode.externalHelpersModuleName; 473 } 474 475 export function hasRecordedExternalHelpers(sourceFile: SourceFile) { 476 const parseNode = getOriginalNode(sourceFile, isSourceFile); 477 const emitNode = parseNode && parseNode.emitNode; 478 return !!emitNode && (!!emitNode.externalHelpersModuleName || !!emitNode.externalHelpers); 479 } 480 481 export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: NodeFactory, helperFactory: EmitHelperFactory, sourceFile: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStar?: boolean, hasImportDefault?: boolean) { 482 if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) { 483 let namedBindings: NamedImportBindings | undefined; 484 const moduleKind = getEmitModuleKind(compilerOptions); 485 if ((moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) || sourceFile.impliedNodeFormat === ModuleKind.ESNext) { 486 // use named imports 487 const helpers = getEmitHelpers(sourceFile); 488 if (helpers) { 489 const helperNames: string[] = []; 490 for (const helper of helpers) { 491 if (!helper.scoped) { 492 const importName = helper.importName; 493 if (importName) { 494 pushIfUnique(helperNames, importName); 495 } 496 } 497 } 498 if (some(helperNames)) { 499 helperNames.sort(compareStringsCaseSensitive); 500 // Alias the imports if the names are used somewhere in the file. 501 // NOTE: We don't need to care about global import collisions as this is a module. 502 namedBindings = nodeFactory.createNamedImports( 503 map(helperNames, name => isFileLevelUniqueName(sourceFile, name) 504 ? nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, nodeFactory.createIdentifier(name)) 505 : nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name)) 506 ) 507 ); 508 const parseNode = getOriginalNode(sourceFile, isSourceFile); 509 const emitNode = getOrCreateEmitNode(parseNode); 510 emitNode.externalHelpers = true; 511 } 512 } 513 } 514 else { 515 // use a namespace import 516 const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(nodeFactory, sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStar || hasImportDefault); 517 if (externalHelpersModuleName) { 518 namedBindings = nodeFactory.createNamespaceImport(externalHelpersModuleName); 519 } 520 } 521 if (namedBindings) { 522 const externalHelpersImportDeclaration = nodeFactory.createImportDeclaration( 523 /*modifiers*/ undefined, 524 nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings), 525 nodeFactory.createStringLiteral(externalHelpersModuleNameText), 526 /*assertClause*/ undefined 527 ); 528 addEmitFlags(externalHelpersImportDeclaration, EmitFlags.NeverApplyImportHelper); 529 return externalHelpersImportDeclaration; 530 } 531 } 532 } 533 534 export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactory, node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) { 535 if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) { 536 const externalHelpersModuleName = getExternalHelpersModuleName(node); 537 if (externalHelpersModuleName) { 538 return externalHelpersModuleName; 539 } 540 541 const moduleKind = getEmitModuleKind(compilerOptions); 542 let create = (hasExportStarsToExportValues || (getESModuleInterop(compilerOptions) && hasImportStarOrImportDefault)) 543 && moduleKind !== ModuleKind.System 544 && (moduleKind < ModuleKind.ES2015 || node.impliedNodeFormat === ModuleKind.CommonJS); 545 if (!create) { 546 const helpers = getEmitHelpers(node); 547 if (helpers) { 548 for (const helper of helpers) { 549 if (!helper.scoped) { 550 create = true; 551 break; 552 } 553 } 554 } 555 } 556 557 if (create) { 558 const parseNode = getOriginalNode(node, isSourceFile); 559 const emitNode = getOrCreateEmitNode(parseNode); 560 return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = factory.createUniqueName(externalHelpersModuleNameText)); 561 } 562 } 563 } 564 565 /** 566 * Get the name of that target module from an import or export declaration 567 */ 568 export function getLocalNameForExternalImport(factory: NodeFactory, node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile): Identifier | undefined { 569 const namespaceDeclaration = getNamespaceDeclarationNode(node); 570 if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) { 571 const name = namespaceDeclaration.name; 572 return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name)); 573 } 574 if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) { 575 return factory.getGeneratedNameForNode(node); 576 } 577 if (node.kind === SyntaxKind.ExportDeclaration && node.moduleSpecifier) { 578 return factory.getGeneratedNameForNode(node); 579 } 580 return undefined; 581 } 582 583 /** 584 * Get the name of a target module from an import/export declaration as should be written in the emitted output. 585 * The emitted output name can be different from the input if: 586 * 1. The module has a /// <amd-module name="<new name>" /> 587 * 2. --out or --outFile is used, making the name relative to the rootDir 588 * 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System). 589 * Otherwise, a new StringLiteral node representing the module name will be returned. 590 */ 591 export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { 592 const moduleName = getExternalModuleName(importNode); 593 if (moduleName && isStringLiteral(moduleName)) { 594 return tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions) 595 || tryRenameExternalModule(factory, moduleName, sourceFile) 596 || factory.cloneNode(moduleName); 597 } 598 599 return undefined; 600 } 601 602 /** 603 * Some bundlers (SystemJS builder) sometimes want to rename dependencies. 604 * Here we check if alternative name was provided for a given moduleName and return it if possible. 605 */ 606 function tryRenameExternalModule(factory: NodeFactory, moduleName: LiteralExpression, sourceFile: SourceFile) { 607 const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); 608 return rename ? factory.createStringLiteral(rename) : undefined; 609 } 610 611 /** 612 * Get the name of a module as should be written in the emitted output. 613 * The emitted output name can be different from the input if: 614 * 1. The module has a /// <amd-module name="<new name>" /> 615 * 2. --out or --outFile is used, making the name relative to the rootDir 616 * Otherwise, a new StringLiteral node representing the module name will be returned. 617 */ 618 export function tryGetModuleNameFromFile(factory: NodeFactory, file: SourceFile | undefined, host: EmitHost, options: CompilerOptions): StringLiteral | undefined { 619 if (!file) { 620 return undefined; 621 } 622 if (file.moduleName) { 623 return factory.createStringLiteral(file.moduleName); 624 } 625 if (!file.isDeclarationFile && outFile(options)) { 626 return factory.createStringLiteral(getExternalModuleNameFromPath(host, file.fileName)); 627 } 628 return undefined; 629 } 630 631 function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) { 632 return tryGetModuleNameFromFile(factory, resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); 633 } 634 635 /** 636 * Gets the initializer of an BindingOrAssignmentElement. 637 */ 638 export function getInitializerOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Expression | undefined { 639 if (isDeclarationBindingElement(bindingElement)) { 640 // `1` in `let { a = 1 } = ...` 641 // `1` in `let { a: b = 1 } = ...` 642 // `1` in `let { a: {b} = 1 } = ...` 643 // `1` in `let { a: [b] = 1 } = ...` 644 // `1` in `let [a = 1] = ...` 645 // `1` in `let [{a} = 1] = ...` 646 // `1` in `let [[a] = 1] = ...` 647 return bindingElement.initializer; 648 } 649 650 if (isPropertyAssignment(bindingElement)) { 651 // `1` in `({ a: b = 1 } = ...)` 652 // `1` in `({ a: {b} = 1 } = ...)` 653 // `1` in `({ a: [b] = 1 } = ...)` 654 const initializer = bindingElement.initializer; 655 return isAssignmentExpression(initializer, /*excludeCompoundAssignment*/ true) 656 ? initializer.right 657 : undefined; 658 } 659 660 if (isShorthandPropertyAssignment(bindingElement)) { 661 // `1` in `({ a = 1 } = ...)` 662 return bindingElement.objectAssignmentInitializer; 663 } 664 665 if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { 666 // `1` in `[a = 1] = ...` 667 // `1` in `[{a} = 1] = ...` 668 // `1` in `[[a] = 1] = ...` 669 return bindingElement.right; 670 } 671 672 if (isSpreadElement(bindingElement)) { 673 // Recovery consistent with existing emit. 674 return getInitializerOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement); 675 } 676 } 677 678 /** 679 * Gets the name of an BindingOrAssignmentElement. 680 */ 681 export function getTargetOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementTarget | undefined { 682 if (isDeclarationBindingElement(bindingElement)) { 683 // `a` in `let { a } = ...` 684 // `a` in `let { a = 1 } = ...` 685 // `b` in `let { a: b } = ...` 686 // `b` in `let { a: b = 1 } = ...` 687 // `a` in `let { ...a } = ...` 688 // `{b}` in `let { a: {b} } = ...` 689 // `{b}` in `let { a: {b} = 1 } = ...` 690 // `[b]` in `let { a: [b] } = ...` 691 // `[b]` in `let { a: [b] = 1 } = ...` 692 // `a` in `let [a] = ...` 693 // `a` in `let [a = 1] = ...` 694 // `a` in `let [...a] = ...` 695 // `{a}` in `let [{a}] = ...` 696 // `{a}` in `let [{a} = 1] = ...` 697 // `[a]` in `let [[a]] = ...` 698 // `[a]` in `let [[a] = 1] = ...` 699 return bindingElement.name; 700 } 701 702 if (isObjectLiteralElementLike(bindingElement)) { 703 switch (bindingElement.kind) { 704 case SyntaxKind.PropertyAssignment: 705 // `b` in `({ a: b } = ...)` 706 // `b` in `({ a: b = 1 } = ...)` 707 // `{b}` in `({ a: {b} } = ...)` 708 // `{b}` in `({ a: {b} = 1 } = ...)` 709 // `[b]` in `({ a: [b] } = ...)` 710 // `[b]` in `({ a: [b] = 1 } = ...)` 711 // `b.c` in `({ a: b.c } = ...)` 712 // `b.c` in `({ a: b.c = 1 } = ...)` 713 // `b[0]` in `({ a: b[0] } = ...)` 714 // `b[0]` in `({ a: b[0] = 1 } = ...)` 715 return getTargetOfBindingOrAssignmentElement(bindingElement.initializer as BindingOrAssignmentElement); 716 717 case SyntaxKind.ShorthandPropertyAssignment: 718 // `a` in `({ a } = ...)` 719 // `a` in `({ a = 1 } = ...)` 720 return bindingElement.name; 721 722 case SyntaxKind.SpreadAssignment: 723 // `a` in `({ ...a } = ...)` 724 return getTargetOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement); 725 } 726 727 } 728 729 if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { 730 // `a` in `[a = 1] = ...` 731 // `{a}` in `[{a} = 1] = ...` 732 // `[a]` in `[[a] = 1] = ...` 733 // `a.b` in `[a.b = 1] = ...` 734 // `a[0]` in `[a[0] = 1] = ...` 735 return getTargetOfBindingOrAssignmentElement(bindingElement.left as BindingOrAssignmentElement); 736 } 737 738 if (isSpreadElement(bindingElement)) { 739 // `a` in `[...a] = ...` 740 return getTargetOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement); 741 } 742 743 // `a` in `[a] = ...` 744 // `{a}` in `[{a}] = ...` 745 // `[a]` in `[[a]] = ...` 746 // `a.b` in `[a.b] = ...` 747 // `a[0]` in `[a[0]] = ...` 748 return bindingElement; 749 } 750 751 /** 752 * Determines whether an BindingOrAssignmentElement is a rest element. 753 */ 754 export function getRestIndicatorOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementRestIndicator | undefined { 755 switch (bindingElement.kind) { 756 case SyntaxKind.Parameter: 757 case SyntaxKind.BindingElement: 758 // `...` in `let [...a] = ...` 759 return bindingElement.dotDotDotToken; 760 761 case SyntaxKind.SpreadElement: 762 case SyntaxKind.SpreadAssignment: 763 // `...` in `[...a] = ...` 764 return bindingElement; 765 } 766 767 return undefined; 768 } 769 770 /** 771 * Gets the property name of a BindingOrAssignmentElement 772 */ 773 export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined { 774 const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement); 775 Debug.assert(!!propertyName || isSpreadAssignment(bindingElement), "Invalid property name for binding element."); 776 return propertyName; 777 } 778 779 export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined { 780 switch (bindingElement.kind) { 781 case SyntaxKind.BindingElement: 782 // `a` in `let { a: b } = ...` 783 // `[a]` in `let { [a]: b } = ...` 784 // `"a"` in `let { "a": b } = ...` 785 // `1` in `let { 1: b } = ...` 786 if (bindingElement.propertyName) { 787 const propertyName = bindingElement.propertyName; 788 if (isPrivateIdentifier(propertyName)) { 789 return Debug.failBadSyntaxKind(propertyName); 790 } 791 return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression) 792 ? propertyName.expression 793 : propertyName; 794 } 795 796 break; 797 798 case SyntaxKind.PropertyAssignment: 799 // `a` in `({ a: b } = ...)` 800 // `[a]` in `({ [a]: b } = ...)` 801 // `"a"` in `({ "a": b } = ...)` 802 // `1` in `({ 1: b } = ...)` 803 if (bindingElement.name) { 804 const propertyName = bindingElement.name; 805 if (isPrivateIdentifier(propertyName)) { 806 return Debug.failBadSyntaxKind(propertyName); 807 } 808 return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression) 809 ? propertyName.expression 810 : propertyName; 811 } 812 813 break; 814 815 case SyntaxKind.SpreadAssignment: 816 // `a` in `({ ...a } = ...)` 817 if (bindingElement.name && isPrivateIdentifier(bindingElement.name)) { 818 return Debug.failBadSyntaxKind(bindingElement.name); 819 } 820 return bindingElement.name; 821 } 822 823 const target = getTargetOfBindingOrAssignmentElement(bindingElement); 824 if (target && isPropertyName(target)) { 825 return target; 826 } 827 } 828 829 function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral { 830 const kind = node.kind; 831 return kind === SyntaxKind.StringLiteral 832 || kind === SyntaxKind.NumericLiteral; 833 } 834 835 /** 836 * Gets the elements of a BindingOrAssignmentPattern 837 */ 838 export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): readonly BindingOrAssignmentElement[] { 839 switch (name.kind) { 840 case SyntaxKind.ObjectBindingPattern: 841 case SyntaxKind.ArrayBindingPattern: 842 case SyntaxKind.ArrayLiteralExpression: 843 // `a` in `{a}` 844 // `a` in `[a]` 845 return name.elements as readonly BindingOrAssignmentElement[]; 846 847 case SyntaxKind.ObjectLiteralExpression: 848 // `a` in `{a}` 849 return name.properties as readonly BindingOrAssignmentElement[]; 850 } 851 } 852 853 /* @internal */ 854 export function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) { 855 if (fullName) { 856 let rightNode = fullName; 857 while (true) { 858 if (isIdentifier(rightNode) || !rightNode.body) { 859 return isIdentifier(rightNode) ? rightNode : rightNode.name; 860 } 861 rightNode = rightNode.body; 862 } 863 } 864 } 865 866 export function canHaveIllegalType(node: Node): node is HasIllegalType { 867 const kind = node.kind; 868 return kind === SyntaxKind.Constructor 869 || kind === SyntaxKind.SetAccessor; 870 } 871 872 export function canHaveIllegalTypeParameters(node: Node): node is HasIllegalTypeParameters { 873 const kind = node.kind; 874 return kind === SyntaxKind.Constructor 875 || kind === SyntaxKind.GetAccessor 876 || kind === SyntaxKind.SetAccessor; 877 } 878 879 export function canHaveIllegalDecorators(node: Node): node is HasIllegalDecorators { 880 const kind = node.kind; 881 return kind === SyntaxKind.PropertyAssignment 882 || kind === SyntaxKind.ShorthandPropertyAssignment 883 || kind === SyntaxKind.FunctionDeclaration 884 || kind === SyntaxKind.Constructor 885 || kind === SyntaxKind.IndexSignature 886 || kind === SyntaxKind.ClassStaticBlockDeclaration 887 || kind === SyntaxKind.MissingDeclaration 888 || kind === SyntaxKind.VariableStatement 889 || kind === SyntaxKind.InterfaceDeclaration 890 || kind === SyntaxKind.TypeAliasDeclaration 891 || kind === SyntaxKind.EnumDeclaration 892 || kind === SyntaxKind.ModuleDeclaration 893 || kind === SyntaxKind.ImportEqualsDeclaration 894 || kind === SyntaxKind.ImportDeclaration 895 || kind === SyntaxKind.NamespaceExportDeclaration 896 || kind === SyntaxKind.ExportDeclaration 897 || kind === SyntaxKind.ExportAssignment; 898 } 899 900 export function canHaveIllegalModifiers(node: Node): node is HasIllegalModifiers { 901 const kind = node.kind; 902 return kind === SyntaxKind.ClassStaticBlockDeclaration 903 || kind === SyntaxKind.PropertyAssignment 904 || kind === SyntaxKind.ShorthandPropertyAssignment 905 || kind === SyntaxKind.FunctionType 906 || kind === SyntaxKind.MissingDeclaration 907 || kind === SyntaxKind.NamespaceExportDeclaration; 908 } 909 910 export const isTypeNodeOrTypeParameterDeclaration = or(isTypeNode, isTypeParameterDeclaration) as (node: Node) => node is TypeNode | TypeParameterDeclaration; 911 export const isQuestionOrExclamationToken = or(isQuestionToken, isExclamationToken) as (node: Node) => node is QuestionToken | ExclamationToken; 912 export const isIdentifierOrThisTypeNode = or(isIdentifier, isThisTypeNode) as (node: Node) => node is Identifier | ThisTypeNode; 913 export const isReadonlyKeywordOrPlusOrMinusToken = or(isReadonlyKeyword, isPlusToken, isMinusToken) as (node: Node) => node is ReadonlyKeyword | PlusToken | MinusToken; 914 export const isQuestionOrPlusOrMinusToken = or(isQuestionToken, isPlusToken, isMinusToken) as (node: Node) => node is QuestionToken | PlusToken | MinusToken; 915 export const isModuleName = or(isIdentifier, isStringLiteral) as (node: Node) => node is ModuleName; 916 917 export function isLiteralTypeLikeExpression(node: Node): node is NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression { 918 const kind = node.kind; 919 return kind === SyntaxKind.NullKeyword 920 || kind === SyntaxKind.TrueKeyword 921 || kind === SyntaxKind.FalseKeyword 922 || isLiteralExpression(node) 923 || isPrefixUnaryExpression(node); 924 } 925 926 function isExponentiationOperator(kind: SyntaxKind): kind is ExponentiationOperator { 927 return kind === SyntaxKind.AsteriskAsteriskToken; 928 } 929 930 function isMultiplicativeOperator(kind: SyntaxKind): kind is MultiplicativeOperator { 931 return kind === SyntaxKind.AsteriskToken 932 || kind === SyntaxKind.SlashToken 933 || kind === SyntaxKind.PercentToken; 934 } 935 936 function isMultiplicativeOperatorOrHigher(kind: SyntaxKind): kind is MultiplicativeOperatorOrHigher { 937 return isExponentiationOperator(kind) 938 || isMultiplicativeOperator(kind); 939 } 940 941 function isAdditiveOperator(kind: SyntaxKind): kind is AdditiveOperator { 942 return kind === SyntaxKind.PlusToken 943 || kind === SyntaxKind.MinusToken; 944 } 945 946 function isAdditiveOperatorOrHigher(kind: SyntaxKind): kind is AdditiveOperatorOrHigher { 947 return isAdditiveOperator(kind) 948 || isMultiplicativeOperatorOrHigher(kind); 949 } 950 951 function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator { 952 return kind === SyntaxKind.LessThanLessThanToken 953 || kind === SyntaxKind.GreaterThanGreaterThanToken 954 || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken; 955 } 956 957 function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher { 958 return isShiftOperator(kind) 959 || isAdditiveOperatorOrHigher(kind); 960 } 961 962 function isRelationalOperator(kind: SyntaxKind): kind is RelationalOperator { 963 return kind === SyntaxKind.LessThanToken 964 || kind === SyntaxKind.LessThanEqualsToken 965 || kind === SyntaxKind.GreaterThanToken 966 || kind === SyntaxKind.GreaterThanEqualsToken 967 || kind === SyntaxKind.InstanceOfKeyword 968 || kind === SyntaxKind.InKeyword; 969 } 970 971 function isRelationalOperatorOrHigher(kind: SyntaxKind): kind is RelationalOperatorOrHigher { 972 return isRelationalOperator(kind) 973 || isShiftOperatorOrHigher(kind); 974 } 975 976 function isEqualityOperator(kind: SyntaxKind): kind is EqualityOperator { 977 return kind === SyntaxKind.EqualsEqualsToken 978 || kind === SyntaxKind.EqualsEqualsEqualsToken 979 || kind === SyntaxKind.ExclamationEqualsToken 980 || kind === SyntaxKind.ExclamationEqualsEqualsToken; 981 } 982 983 function isEqualityOperatorOrHigher(kind: SyntaxKind): kind is EqualityOperatorOrHigher { 984 return isEqualityOperator(kind) 985 || isRelationalOperatorOrHigher(kind); 986 } 987 988 function isBitwiseOperator(kind: SyntaxKind): kind is BitwiseOperator { 989 return kind === SyntaxKind.AmpersandToken 990 || kind === SyntaxKind.BarToken 991 || kind === SyntaxKind.CaretToken; 992 } 993 994 function isBitwiseOperatorOrHigher(kind: SyntaxKind): kind is BitwiseOperatorOrHigher { 995 return isBitwiseOperator(kind) 996 || isEqualityOperatorOrHigher(kind); 997 } 998 999 // NOTE: The version in utilities includes ExclamationToken, which is not a binary operator. 1000 function isLogicalOperator(kind: SyntaxKind): kind is LogicalOperator { 1001 return kind === SyntaxKind.AmpersandAmpersandToken 1002 || kind === SyntaxKind.BarBarToken; 1003 } 1004 1005 function isLogicalOperatorOrHigher(kind: SyntaxKind): kind is LogicalOperatorOrHigher { 1006 return isLogicalOperator(kind) 1007 || isBitwiseOperatorOrHigher(kind); 1008 } 1009 1010 function isAssignmentOperatorOrHigher(kind: SyntaxKind): kind is AssignmentOperatorOrHigher { 1011 return kind === SyntaxKind.QuestionQuestionToken 1012 || isLogicalOperatorOrHigher(kind) 1013 || isAssignmentOperator(kind); 1014 } 1015 1016 function isBinaryOperator(kind: SyntaxKind): kind is BinaryOperator { 1017 return isAssignmentOperatorOrHigher(kind) 1018 || kind === SyntaxKind.CommaToken; 1019 } 1020 1021 export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken { 1022 return isBinaryOperator(node.kind); 1023 } 1024 1025 type BinaryExpressionState = <TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, outerState: TOuterState) => number; 1026 1027 namespace BinaryExpressionState { 1028 /** 1029 * Handles walking into a `BinaryExpression`. 1030 * @param machine State machine handler functions 1031 * @param frame The current frame 1032 * @returns The new frame 1033 */ 1034 export function enter<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, outerState: TOuterState): number { 1035 const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined; 1036 Debug.assertEqual(stateStack[stackIndex], enter); 1037 userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState); 1038 stateStack[stackIndex] = nextState(machine, enter); 1039 return stackIndex; 1040 } 1041 1042 /** 1043 * Handles walking the `left` side of a `BinaryExpression`. 1044 * @param machine State machine handler functions 1045 * @param frame The current frame 1046 * @returns The new frame 1047 */ 1048 export function left<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { 1049 Debug.assertEqual(stateStack[stackIndex], left); 1050 Debug.assertIsDefined(machine.onLeft); 1051 stateStack[stackIndex] = nextState(machine, left); 1052 const nextNode = machine.onLeft(nodeStack[stackIndex].left, userStateStack[stackIndex], nodeStack[stackIndex]); 1053 if (nextNode) { 1054 checkCircularity(stackIndex, nodeStack, nextNode); 1055 return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); 1056 } 1057 return stackIndex; 1058 } 1059 1060 /** 1061 * Handles walking the `operatorToken` of a `BinaryExpression`. 1062 * @param machine State machine handler functions 1063 * @param frame The current frame 1064 * @returns The new frame 1065 */ 1066 export function operator<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { 1067 Debug.assertEqual(stateStack[stackIndex], operator); 1068 Debug.assertIsDefined(machine.onOperator); 1069 stateStack[stackIndex] = nextState(machine, operator); 1070 machine.onOperator(nodeStack[stackIndex].operatorToken, userStateStack[stackIndex], nodeStack[stackIndex]); 1071 return stackIndex; 1072 } 1073 1074 /** 1075 * Handles walking the `right` side of a `BinaryExpression`. 1076 * @param machine State machine handler functions 1077 * @param frame The current frame 1078 * @returns The new frame 1079 */ 1080 export function right<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { 1081 Debug.assertEqual(stateStack[stackIndex], right); 1082 Debug.assertIsDefined(machine.onRight); 1083 stateStack[stackIndex] = nextState(machine, right); 1084 const nextNode = machine.onRight(nodeStack[stackIndex].right, userStateStack[stackIndex], nodeStack[stackIndex]); 1085 if (nextNode) { 1086 checkCircularity(stackIndex, nodeStack, nextNode); 1087 return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); 1088 } 1089 return stackIndex; 1090 } 1091 1092 /** 1093 * Handles walking out of a `BinaryExpression`. 1094 * @param machine State machine handler functions 1095 * @param frame The current frame 1096 * @returns The new frame 1097 */ 1098 export function exit<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, _outerState: TOuterState): number { 1099 Debug.assertEqual(stateStack[stackIndex], exit); 1100 stateStack[stackIndex] = nextState(machine, exit); 1101 const result = machine.onExit(nodeStack[stackIndex], userStateStack[stackIndex]); 1102 if (stackIndex > 0) { 1103 stackIndex--; 1104 if (machine.foldState) { 1105 const side = stateStack[stackIndex] === exit ? "right" : "left"; 1106 userStateStack[stackIndex] = machine.foldState(userStateStack[stackIndex], result, side); 1107 } 1108 } 1109 else { 1110 resultHolder.value = result; 1111 } 1112 return stackIndex; 1113 } 1114 1115 /** 1116 * Handles a frame that is already done. 1117 * @returns The `done` state. 1118 */ 1119 export function done<TOuterState, TState, TResult>(_machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], _nodeStack: BinaryExpression[], _userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { 1120 Debug.assertEqual(stateStack[stackIndex], done); 1121 return stackIndex; 1122 } 1123 1124 export function nextState<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, currentState: BinaryExpressionState) { 1125 switch (currentState) { 1126 case enter: 1127 if (machine.onLeft) return left; 1128 // falls through 1129 case left: 1130 if (machine.onOperator) return operator; 1131 // falls through 1132 case operator: 1133 if (machine.onRight) return right; 1134 // falls through 1135 case right: return exit; 1136 case exit: return done; 1137 case done: return done; 1138 default: Debug.fail("Invalid state"); 1139 } 1140 } 1141 1142 function pushStack<TState>(stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], node: BinaryExpression) { 1143 stackIndex++; 1144 stateStack[stackIndex] = enter; 1145 nodeStack[stackIndex] = node; 1146 userStateStack[stackIndex] = undefined!; 1147 return stackIndex; 1148 } 1149 1150 function checkCircularity(stackIndex: number, nodeStack: BinaryExpression[], node: BinaryExpression) { 1151 if (Debug.shouldAssert(AssertionLevel.Aggressive)) { 1152 while (stackIndex >= 0) { 1153 Debug.assert(nodeStack[stackIndex] !== node, "Circular traversal detected."); 1154 stackIndex--; 1155 } 1156 } 1157 } 1158 } 1159 1160 /** 1161 * Holds state machine handler functions 1162 */ 1163 class BinaryExpressionStateMachine<TOuterState, TState, TResult> { 1164 constructor( 1165 readonly onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, 1166 readonly onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1167 readonly onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1168 readonly onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1169 readonly onExit: (node: BinaryExpression, userState: TState) => TResult, 1170 readonly foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1171 ) { 1172 } 1173 } 1174 1175 /** 1176 * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. 1177 * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. 1178 * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. 1179 * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. 1180 * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. 1181 * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. 1182 * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. 1183 */ 1184 export function createBinaryExpressionTrampoline<TState, TResult>( 1185 onEnter: (node: BinaryExpression, prev: TState | undefined) => TState, 1186 onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1187 onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1188 onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1189 onExit: (node: BinaryExpression, userState: TState) => TResult, 1190 foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1191 ): (node: BinaryExpression) => TResult; 1192 /** 1193 * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. 1194 * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. 1195 * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. 1196 * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. 1197 * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. 1198 * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. 1199 * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. 1200 */ 1201 export function createBinaryExpressionTrampoline<TOuterState, TState, TResult>( 1202 onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, 1203 onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1204 onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1205 onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1206 onExit: (node: BinaryExpression, userState: TState) => TResult, 1207 foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1208 ): (node: BinaryExpression, outerState: TOuterState) => TResult; 1209 export function createBinaryExpressionTrampoline<TOuterState, TState, TResult>( 1210 onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, 1211 onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1212 onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1213 onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1214 onExit: (node: BinaryExpression, userState: TState) => TResult, 1215 foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1216 ) { 1217 const machine = new BinaryExpressionStateMachine(onEnter, onLeft, onOperator, onRight, onExit, foldState); 1218 return trampoline; 1219 1220 function trampoline(node: BinaryExpression, outerState?: TOuterState) { 1221 const resultHolder: { value: TResult } = { value: undefined! }; 1222 const stateStack: BinaryExpressionState[] = [BinaryExpressionState.enter]; 1223 const nodeStack: BinaryExpression[] = [node]; 1224 const userStateStack: TState[] = [undefined!]; 1225 let stackIndex = 0; 1226 while (stateStack[stackIndex] !== BinaryExpressionState.done) { 1227 stackIndex = stateStack[stackIndex](machine, stackIndex, stateStack, nodeStack, userStateStack, resultHolder, outerState); 1228 } 1229 Debug.assertEqual(stackIndex, 0); 1230 return resultHolder.value; 1231 } 1232 } 1233 1234 /** 1235 * If `nodes` is not undefined, creates an empty `NodeArray` that preserves the `pos` and `end` of `nodes`. 1236 * @internal 1237 */ 1238 export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T>): NodeArray<T>; 1239 export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined; 1240 export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined { 1241 if (nodes === undefined) return undefined; 1242 if (nodes.length === 0) return nodes; 1243 return setTextRange(factory.createNodeArray([], nodes.hasTrailingComma), nodes); 1244 } 1245 1246 /** 1247 * Gets the node from which a name should be generated. 1248 */ 1249 export function getNodeForGeneratedName(name: GeneratedIdentifier | GeneratedPrivateIdentifier) { 1250 if (name.autoGenerateFlags & GeneratedIdentifierFlags.Node) { 1251 const autoGenerateId = name.autoGenerateId; 1252 let node = name as Node; 1253 let original = node.original; 1254 while (original) { 1255 node = original; 1256 1257 // if "node" is a different generated name (having a different "autoGenerateId"), use it and stop traversing. 1258 if (isMemberName(node) 1259 && !!(node.autoGenerateFlags! & GeneratedIdentifierFlags.Node) 1260 && node.autoGenerateId !== autoGenerateId) { 1261 break; 1262 } 1263 1264 original = node.original; 1265 } 1266 // otherwise, return the original node for the source 1267 return node; 1268 } 1269 return name; 1270 } 1271 1272 /** 1273 * Formats a prefix or suffix of a generated name. 1274 */ 1275 export function formatGeneratedNamePart(part: string | undefined): string; 1276 /** 1277 * Formats a prefix or suffix of a generated name. If the part is a {@link GeneratedNamePart}, calls {@link generateName} to format the source node. 1278 */ 1279 export function formatGeneratedNamePart(part: string | GeneratedNamePart | undefined, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string; 1280 export function formatGeneratedNamePart(part: string | GeneratedNamePart | undefined, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string { 1281 return typeof part === "object" ? formatGeneratedName(/*privateName*/ false, part.prefix, part.node, part.suffix, generateName!) : 1282 typeof part === "string" ? part.length > 0 && part.charCodeAt(0) === CharacterCodes.hash ? part.slice(1) : part : 1283 ""; 1284 } 1285 1286 function formatIdentifier(name: string | Identifier | PrivateIdentifier, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) { 1287 return typeof name === "string" ? name : 1288 formatIdentifierWorker(name, Debug.checkDefined(generateName)); 1289 } 1290 1291 function formatIdentifierWorker(node: Identifier | PrivateIdentifier, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) { 1292 return isGeneratedPrivateIdentifier(node) ? generateName(node).slice(1) : 1293 isGeneratedIdentifier(node) ? generateName(node) : 1294 isPrivateIdentifier(node) ? (node.escapedText as string).slice(1) : 1295 idText(node); 1296 } 1297 1298 /** 1299 * Formats a generated name. 1300 * @param privateName When `true`, inserts a `#` character at the start of the result. 1301 * @param prefix The prefix (if any) to include before the base name. 1302 * @param baseName The base name for the generated name. 1303 * @param suffix The suffix (if any) to include after the base name. 1304 */ 1305 export function formatGeneratedName(privateName: boolean, prefix: string | undefined, baseName: string, suffix: string | undefined): string; 1306 /** 1307 * Formats a generated name. 1308 * @param privateName When `true`, inserts a `#` character at the start of the result. 1309 * @param prefix The prefix (if any) to include before the base name. 1310 * @param baseName The base name for the generated name. 1311 * @param suffix The suffix (if any) to include after the base name. 1312 * @param generateName Called to format the source node of {@link prefix} when it is a {@link GeneratedNamePart}. 1313 */ 1314 export function formatGeneratedName(privateName: boolean, prefix: string | GeneratedNamePart | undefined, baseName: string | Identifier | PrivateIdentifier, suffix: string | GeneratedNamePart | undefined, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string; 1315 export function formatGeneratedName(privateName: boolean, prefix: string | GeneratedNamePart | undefined, baseName: string | Identifier | PrivateIdentifier, suffix: string | GeneratedNamePart | undefined, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) { 1316 prefix = formatGeneratedNamePart(prefix, generateName!); 1317 suffix = formatGeneratedNamePart(suffix, generateName!); 1318 baseName = formatIdentifier(baseName, generateName); 1319 return `${privateName ? "#" : ""}${prefix}${baseName}${suffix}`; 1320 } 1321 1322 1323 /** 1324 * Creates a private backing field for an `accessor` {@link PropertyDeclaration}. 1325 */ 1326 export function createAccessorPropertyBackingField(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, initializer: Expression | undefined) { 1327 return factory.updatePropertyDeclaration( 1328 node, 1329 modifiers, 1330 factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage"), 1331 /*questionOrExclamationToken*/ undefined, 1332 /*type*/ undefined, 1333 initializer 1334 ); 1335 } 1336 1337 /** 1338 * Creates a {@link GetAccessorDeclaration} that reads from a private backing field. 1339 */ 1340 export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName) { 1341 return factory.createGetAccessorDeclaration( 1342 modifiers, 1343 name, 1344 [], 1345 /*type*/ undefined, 1346 factory.createBlock([ 1347 factory.createReturnStatement( 1348 factory.createPropertyAccessExpression( 1349 factory.createThis(), 1350 factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage") 1351 ) 1352 ) 1353 ]) 1354 ); 1355 } 1356 1357 /** 1358 * Creates a {@link SetAccessorDeclaration} that writes to a private backing field. 1359 */ 1360 export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName) { 1361 return factory.createSetAccessorDeclaration( 1362 modifiers, 1363 name, 1364 [factory.createParameterDeclaration( 1365 /*modifiers*/ undefined, 1366 /*dotdotDotToken*/ undefined, 1367 "value" 1368 )], 1369 factory.createBlock([ 1370 factory.createExpressionStatement( 1371 factory.createAssignment( 1372 factory.createPropertyAccessExpression( 1373 factory.createThis(), 1374 factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage") 1375 ), 1376 factory.createIdentifier("value") 1377 ) 1378 ) 1379 ]) 1380 ); 1381 } 1382} 1383