1import { 2 AccessorDeclaration, addEmitFlags, AdditiveOperator, AdditiveOperatorOrHigher, AssertionLevel, 3 AssignmentOperatorOrHigher, BinaryExpression, BinaryOperator, BinaryOperatorToken, BindingOrAssignmentElement, 4 BindingOrAssignmentElementRestIndicator, BindingOrAssignmentElementTarget, BindingOrAssignmentPattern, 5 BitwiseOperator, BitwiseOperatorOrHigher, Block, BooleanLiteral, CharacterCodes, CommaListExpression, 6 compareStringsCaseSensitive, CompilerOptions, Debug, Declaration, EmitFlags, EmitHelperFactory, EmitHost, 7 EmitResolver, EntityName, EqualityOperator, EqualityOperatorOrHigher, ExclamationToken, ExponentiationOperator, 8 ExportDeclaration, Expression, ExpressionStatement, externalHelpersModuleNameText, first, firstOrUndefined, 9 ForInitializer, GeneratedIdentifier, GeneratedIdentifierFlags, GeneratedNamePart, GeneratedPrivateIdentifier, 10 GetAccessorDeclaration, 11 getAllAccessorDeclarations, getEmitFlags, getEmitHelpers, getEmitModuleKind, getESModuleInterop, 12 getExternalModuleName, getExternalModuleNameFromPath, getJSDocType, getJSDocTypeTag, getModifiers, 13 getNamespaceDeclarationNode, getOrCreateEmitNode, getOriginalNode, getParseTreeNode, 14 getSourceTextOfNodeFromSourceFile, HasIllegalDecorators, HasIllegalModifiers, HasIllegalType, 15 HasIllegalTypeParameters, Identifier, idText, ImportCall, ImportDeclaration, ImportEqualsDeclaration, 16 isAssignmentExpression, isAssignmentOperator, isBlock, isComputedPropertyName, isDeclarationBindingElement, 17 isDefaultImport, isEffectiveExternalModule, isExclamationToken, isExportNamespaceAsDefaultDeclaration, 18 isFileLevelUniqueName, isGeneratedIdentifier, isGeneratedPrivateIdentifier, isIdentifier, isInJSFile, 19 isLiteralExpression, isMemberName, isMinusToken, isObjectLiteralElementLike, isParenthesizedExpression, isPlusToken, 20 isPostfixUnaryExpression, isPrefixUnaryExpression, isPrivateIdentifier, isPrologueDirective, isPropertyAssignment, 21 isPropertyName, isQualifiedName, isQuestionToken, isReadonlyKeyword, isShorthandPropertyAssignment, isSourceFile, 22 isSpreadAssignment, isSpreadElement, isStringLiteral, isThisTypeNode, isTypeNode, isTypeParameterDeclaration, 23 isVariableDeclarationList, JSDocNamespaceBody, JSDocTypeAssertion, JsxOpeningFragment, JsxOpeningLikeElement, 24 LeftHandSideExpression, LiteralExpression, LogicalOperator, LogicalOperatorOrHigher, map, MemberExpression, 25 MethodDeclaration, MinusToken, ModifiersArray, ModuleKind, ModuleName, MultiplicativeOperator, 26 MultiplicativeOperatorOrHigher, Mutable, NamedImportBindings, Node, NodeArray, NodeFactory, NullLiteral, 27 NumericLiteral, ObjectLiteralElementLike, ObjectLiteralExpression, or, OuterExpression, OuterExpressionKinds, 28 outFile, parseNodeFactory, PlusToken, PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier, 29 PropertyAssignment, PropertyDeclaration, PropertyName, pushIfUnique, QuestionToken, ReadonlyKeyword, 30 RelationalOperator, RelationalOperatorOrHigher, SetAccessorDeclaration, setOriginalNode, setParent, setStartsOnNewLine, setTextRange, 31 ShiftOperator, ShiftOperatorOrHigher, ShorthandPropertyAssignment, some, SourceFile, Statement, StringLiteral, 32 SyntaxKind, TextRange, ThisTypeNode, Token, TypeNode, TypeParameterDeclaration, 33} from "../_namespaces/ts"; 34 35// Compound nodes 36 37/** @internal */ 38export function createEmptyExports(factory: NodeFactory) { 39 return factory.createExportDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([]), /*moduleSpecifier*/ undefined); 40} 41 42/** @internal */ 43export function createMemberAccessForPropertyName(factory: NodeFactory, target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression { 44 if (isComputedPropertyName(memberName)) { 45 return setTextRange(factory.createElementAccessExpression(target, memberName.expression), location); 46 } 47 else { 48 const expression = setTextRange( 49 isMemberName(memberName) 50 ? factory.createPropertyAccessExpression(target, memberName) 51 : factory.createElementAccessExpression(target, memberName), 52 memberName 53 ); 54 getOrCreateEmitNode(expression).flags |= EmitFlags.NoNestedSourceMaps; 55 return expression; 56 } 57} 58 59function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment) { 60 // To ensure the emit resolver can properly resolve the namespace, we need to 61 // treat this identifier as if it were a source tree node by clearing the `Synthesized` 62 // flag and setting a parent node. 63 const react = parseNodeFactory.createIdentifier(reactNamespace || "React"); 64 // Set the parent that is in parse tree 65 // this makes sure that parent chain is intact for checker to traverse complete scope tree 66 setParent(react, getParseTreeNode(parent)); 67 return react; 68} 69 70function createJsxFactoryExpressionFromEntityName(factory: NodeFactory, jsxFactory: EntityName, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { 71 if (isQualifiedName(jsxFactory)) { 72 const left = createJsxFactoryExpressionFromEntityName(factory, jsxFactory.left, parent); 73 const right = factory.createIdentifier(idText(jsxFactory.right)) as Mutable<Identifier>; 74 right.escapedText = jsxFactory.right.escapedText; 75 return factory.createPropertyAccessExpression(left, right); 76 } 77 else { 78 return createReactNamespace(idText(jsxFactory), parent); 79 } 80} 81 82/** @internal */ 83export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { 84 return jsxFactoryEntity ? 85 createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) : 86 factory.createPropertyAccessExpression( 87 createReactNamespace(reactNamespace, parent), 88 "createElement" 89 ); 90} 91 92function createJsxFragmentFactoryExpression(factory: NodeFactory, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { 93 return jsxFragmentFactoryEntity ? 94 createJsxFactoryExpressionFromEntityName(factory, jsxFragmentFactoryEntity, parent) : 95 factory.createPropertyAccessExpression( 96 createReactNamespace(reactNamespace, parent), 97 "Fragment" 98 ); 99} 100 101/** @internal */ 102export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression { 103 const argumentsList = [tagName]; 104 if (props) { 105 argumentsList.push(props); 106 } 107 108 if (children && children.length > 0) { 109 if (!props) { 110 argumentsList.push(factory.createNull()); 111 } 112 113 if (children.length > 1) { 114 for (const child of children) { 115 startOnNewLine(child); 116 argumentsList.push(child); 117 } 118 } 119 else { 120 argumentsList.push(children[0]); 121 } 122 } 123 124 return setTextRange( 125 factory.createCallExpression( 126 callee, 127 /*typeArguments*/ undefined, 128 argumentsList 129 ), 130 location 131 ); 132} 133 134/** @internal */ 135export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression { 136 const tagName = createJsxFragmentFactoryExpression(factory, jsxFragmentFactoryEntity, reactNamespace, parentElement); 137 const argumentsList = [tagName, factory.createNull()]; 138 139 if (children && children.length > 0) { 140 if (children.length > 1) { 141 for (const child of children) { 142 startOnNewLine(child); 143 argumentsList.push(child); 144 } 145 } 146 else { 147 argumentsList.push(children[0]); 148 } 149 } 150 151 return setTextRange( 152 factory.createCallExpression( 153 createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement), 154 /*typeArguments*/ undefined, 155 argumentsList 156 ), 157 location 158 ); 159} 160 161// Utilities 162 163/** @internal */ 164export function createForOfBindingStatement(factory: NodeFactory, node: ForInitializer, boundValue: Expression): Statement { 165 if (isVariableDeclarationList(node)) { 166 const firstDeclaration = first(node.declarations); 167 const updatedDeclaration = factory.updateVariableDeclaration( 168 firstDeclaration, 169 firstDeclaration.name, 170 /*exclamationToken*/ undefined, 171 /*type*/ undefined, 172 boundValue 173 ); 174 return setTextRange( 175 factory.createVariableStatement( 176 /*modifiers*/ undefined, 177 factory.updateVariableDeclarationList(node, [updatedDeclaration]) 178 ), 179 /*location*/ node 180 ); 181 } 182 else { 183 const updatedExpression = setTextRange(factory.createAssignment(node, boundValue), /*location*/ node); 184 return setTextRange(factory.createExpressionStatement(updatedExpression), /*location*/ node); 185 } 186} 187 188/** @internal */ 189export function insertLeadingStatement(factory: NodeFactory, dest: Statement, source: Statement): Block { 190 if (isBlock(dest)) { 191 return factory.updateBlock(dest, setTextRange(factory.createNodeArray([source, ...dest.statements]), dest.statements)); 192 } 193 else { 194 return factory.createBlock(factory.createNodeArray([dest, source]), /*multiLine*/ true); 195 } 196} 197 198/** @internal */ 199export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName | Expression): Expression { 200 if (isQualifiedName(node)) { 201 const left = createExpressionFromEntityName(factory, node.left); 202 // TODO(rbuckton): Does this need to be parented? 203 const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent); 204 return setTextRange(factory.createPropertyAccessExpression(left, right), node); 205 } 206 else { 207 // TODO(rbuckton): Does this need to be parented? 208 return setParent(setTextRange(factory.cloneNode(node), node), node.parent); 209 } 210} 211 212/** @internal */ 213export function createExpressionForPropertyName(factory: NodeFactory, memberName: Exclude<PropertyName, PrivateIdentifier>): Expression { 214 if (isIdentifier(memberName)) { 215 return factory.createStringLiteralFromNode(memberName); 216 } 217 else if (isComputedPropertyName(memberName)) { 218 // TODO(rbuckton): Does this need to be parented? 219 return setParent(setTextRange(factory.cloneNode(memberName.expression), memberName.expression), memberName.expression.parent); 220 } 221 else { 222 // TODO(rbuckton): Does this need to be parented? 223 return setParent(setTextRange(factory.cloneNode(memberName), memberName), memberName.parent); 224 } 225} 226 227function createExpressionForAccessorDeclaration(factory: NodeFactory, properties: NodeArray<Declaration>, property: AccessorDeclaration & { readonly name: Exclude<PropertyName, PrivateIdentifier>; }, receiver: Expression, multiLine: boolean) { 228 const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property); 229 if (property === firstAccessor) { 230 return setTextRange( 231 factory.createObjectDefinePropertyCall( 232 receiver, 233 createExpressionForPropertyName(factory, property.name), 234 factory.createPropertyDescriptor({ 235 enumerable: factory.createFalse(), 236 configurable: true, 237 get: getAccessor && setTextRange( 238 setOriginalNode( 239 factory.createFunctionExpression( 240 getModifiers(getAccessor), 241 /*asteriskToken*/ undefined, 242 /*name*/ undefined, 243 /*typeParameters*/ undefined, 244 getAccessor.parameters, 245 /*type*/ undefined, 246 getAccessor.body! // TODO: GH#18217 247 ), 248 getAccessor 249 ), 250 getAccessor 251 ), 252 set: setAccessor && setTextRange( 253 setOriginalNode( 254 factory.createFunctionExpression( 255 getModifiers(setAccessor), 256 /*asteriskToken*/ undefined, 257 /*name*/ undefined, 258 /*typeParameters*/ undefined, 259 setAccessor.parameters, 260 /*type*/ undefined, 261 setAccessor.body! // TODO: GH#18217 262 ), 263 setAccessor 264 ), 265 setAccessor 266 ) 267 }, !multiLine) 268 ), 269 firstAccessor 270 ); 271 } 272 273 return undefined; 274} 275 276function createExpressionForPropertyAssignment(factory: NodeFactory, property: PropertyAssignment, receiver: Expression) { 277 return setOriginalNode( 278 setTextRange( 279 factory.createAssignment( 280 createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), 281 property.initializer 282 ), 283 property 284 ), 285 property 286 ); 287} 288 289function createExpressionForShorthandPropertyAssignment(factory: NodeFactory, property: ShorthandPropertyAssignment, receiver: Expression) { 290 return setOriginalNode( 291 setTextRange( 292 factory.createAssignment( 293 createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), 294 factory.cloneNode(property.name) 295 ), 296 /*location*/ property 297 ), 298 /*original*/ property 299 ); 300} 301 302function createExpressionForMethodDeclaration(factory: NodeFactory, method: MethodDeclaration, receiver: Expression) { 303 return setOriginalNode( 304 setTextRange( 305 factory.createAssignment( 306 createMemberAccessForPropertyName(factory, receiver, method.name, /*location*/ method.name), 307 setOriginalNode( 308 setTextRange( 309 factory.createFunctionExpression( 310 getModifiers(method), 311 method.asteriskToken, 312 /*name*/ undefined, 313 /*typeParameters*/ undefined, 314 method.parameters, 315 /*type*/ undefined, 316 method.body! // TODO: GH#18217 317 ), 318 /*location*/ method 319 ), 320 /*original*/ method 321 ) 322 ), 323 /*location*/ method 324 ), 325 /*original*/ method 326 ); 327} 328 329/** @internal */ 330export function createExpressionForObjectLiteralElementLike(factory: NodeFactory, node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined { 331 if (property.name && isPrivateIdentifier(property.name)) { 332 Debug.failBadSyntaxKind(property.name, "Private identifiers are not allowed in object literals."); 333 } 334 switch (property.kind) { 335 case SyntaxKind.GetAccessor: 336 case SyntaxKind.SetAccessor: 337 return createExpressionForAccessorDeclaration(factory, node.properties, property as typeof property & { readonly name: Exclude<PropertyName, PrivateIdentifier> }, receiver, !!node.multiLine); 338 case SyntaxKind.PropertyAssignment: 339 return createExpressionForPropertyAssignment(factory, property, receiver); 340 case SyntaxKind.ShorthandPropertyAssignment: 341 return createExpressionForShorthandPropertyAssignment(factory, property, receiver); 342 case SyntaxKind.MethodDeclaration: 343 return createExpressionForMethodDeclaration(factory, property, receiver); 344 } 345} 346 347/** 348 * Expand the read and increment/decrement operations a pre- or post-increment or pre- or post-decrement expression. 349 * 350 * ```ts 351 * // input 352 * <expression>++ 353 * // output (if result is not discarded) 354 * var <temp>; 355 * (<temp> = <expression>, <resultVariable> = <temp>++, <temp>) 356 * // output (if result is discarded) 357 * var <temp>; 358 * (<temp> = <expression>, <temp>++, <temp>) 359 * 360 * // input 361 * ++<expression> 362 * // output (if result is not discarded) 363 * var <temp>; 364 * (<temp> = <expression>, <resultVariable> = ++<temp>) 365 * // output (if result is discarded) 366 * var <temp>; 367 * (<temp> = <expression>, ++<temp>) 368 * ``` 369 * 370 * It is up to the caller to supply a temporary variable for `<resultVariable>` if one is needed. 371 * The temporary variable `<temp>` is injected so that `++` and `--` work uniformly with `number` and `bigint`. 372 * The result of the expression is always the final result of incrementing or decrementing the expression, so that it can be used for storage. 373 * 374 * @param factory {@link NodeFactory} used to create the expanded representation. 375 * @param node The original prefix or postfix unary node. 376 * @param expression The expression to use as the value to increment or decrement 377 * @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. 378 * 379 * @internal 380 */ 381export function expandPreOrPostfixIncrementOrDecrementExpression(factory: NodeFactory, node: PrefixUnaryExpression | PostfixUnaryExpression, expression: Expression, recordTempVariable: (node: Identifier) => void, resultVariable: Identifier | undefined) { 382 const operator = node.operator; 383 Debug.assert(operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken, "Expected 'node' to be a pre- or post-increment or pre- or post-decrement expression"); 384 385 const temp = factory.createTempVariable(recordTempVariable); 386 expression = factory.createAssignment(temp, expression); 387 setTextRange(expression, node.operand); 388 389 let operation: Expression = isPrefixUnaryExpression(node) ? 390 factory.createPrefixUnaryExpression(operator, temp) : 391 factory.createPostfixUnaryExpression(temp, operator); 392 setTextRange(operation, node); 393 394 if (resultVariable) { 395 operation = factory.createAssignment(resultVariable, operation); 396 setTextRange(operation, node); 397 } 398 399 expression = factory.createComma(expression, operation); 400 setTextRange(expression, node); 401 402 if (isPostfixUnaryExpression(node)) { 403 expression = factory.createComma(expression, temp); 404 setTextRange(expression, node); 405 } 406 407 return expression; 408} 409 410/** 411 * Gets whether an identifier should only be referred to by its internal name. 412 * 413 * @internal 414 */ 415export function isInternalName(node: Identifier) { 416 return (getEmitFlags(node) & EmitFlags.InternalName) !== 0; 417} 418 419/** 420 * Gets whether an identifier should only be referred to by its local name. 421 * 422 * @internal 423 */ 424export function isLocalName(node: Identifier) { 425 return (getEmitFlags(node) & EmitFlags.LocalName) !== 0; 426} 427 428/** 429 * Gets whether an identifier should only be referred to by its export representation if the 430 * name points to an exported symbol. 431 * 432 * @internal 433 */ 434export function isExportName(node: Identifier) { 435 return (getEmitFlags(node) & EmitFlags.ExportName) !== 0; 436} 437 438function isUseStrictPrologue(node: ExpressionStatement): boolean { 439 return isStringLiteral(node.expression) && node.expression.text === "use strict"; 440} 441 442/** @internal */ 443export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined { 444 for (const statement of statements) { 445 if (isPrologueDirective(statement)) { 446 if (isUseStrictPrologue(statement)) { 447 return statement; 448 } 449 } 450 else { 451 break; 452 } 453 } 454 return undefined; 455} 456 457/** @internal */ 458export function startsWithUseStrict(statements: readonly Statement[]) { 459 const firstStatement = firstOrUndefined(statements); 460 return firstStatement !== undefined 461 && isPrologueDirective(firstStatement) 462 && isUseStrictPrologue(firstStatement); 463} 464 465/** @internal */ 466export function isCommaSequence(node: Expression): node is BinaryExpression & {operatorToken: Token<SyntaxKind.CommaToken>} | CommaListExpression { 467 return node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.CommaToken || 468 node.kind === SyntaxKind.CommaListExpression; 469} 470 471/** @internal */ 472export function isJSDocTypeAssertion(node: Node): node is JSDocTypeAssertion { 473 return isParenthesizedExpression(node) 474 && isInJSFile(node) 475 && !!getJSDocTypeTag(node); 476} 477 478/** @internal */ 479export function getJSDocTypeAssertionType(node: JSDocTypeAssertion) { 480 const type = getJSDocType(node); 481 Debug.assertIsDefined(type); 482 return type; 483} 484 485/** @internal */ 486export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression { 487 switch (node.kind) { 488 case SyntaxKind.ParenthesizedExpression: 489 if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node)) { 490 return false; 491 } 492 return (kinds & OuterExpressionKinds.Parentheses) !== 0; 493 case SyntaxKind.TypeAssertionExpression: 494 case SyntaxKind.AsExpression: 495 case SyntaxKind.SatisfiesExpression: 496 return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; 497 case SyntaxKind.NonNullExpression: 498 return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; 499 case SyntaxKind.PartiallyEmittedExpression: 500 return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; 501 } 502 return false; 503} 504 505/** @internal */ 506export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; 507/** @internal */ 508export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; 509/** @internal */ 510export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { 511 while (isOuterExpression(node, kinds)) { 512 node = node.expression; 513 } 514 return node; 515} 516 517/** @internal */ 518export function skipAssertions(node: Expression): Expression; 519/** @internal */ 520export function skipAssertions(node: Node): Node; 521/** @internal */ 522export function skipAssertions(node: Node): Node { 523 return skipOuterExpressions(node, OuterExpressionKinds.Assertions); 524} 525 526/** @internal */ 527export function startOnNewLine<T extends Node>(node: T): T { 528 return setStartsOnNewLine(node, /*newLine*/ true); 529} 530 531/** @internal */ 532export function getExternalHelpersModuleName(node: SourceFile) { 533 const parseNode = getOriginalNode(node, isSourceFile); 534 const emitNode = parseNode && parseNode.emitNode; 535 return emitNode && emitNode.externalHelpersModuleName; 536} 537 538/** @internal */ 539export function hasRecordedExternalHelpers(sourceFile: SourceFile) { 540 const parseNode = getOriginalNode(sourceFile, isSourceFile); 541 const emitNode = parseNode && parseNode.emitNode; 542 return !!emitNode && (!!emitNode.externalHelpersModuleName || !!emitNode.externalHelpers); 543} 544 545/** @internal */ 546export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: NodeFactory, helperFactory: EmitHelperFactory, sourceFile: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStar?: boolean, hasImportDefault?: boolean) { 547 if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) { 548 let namedBindings: NamedImportBindings | undefined; 549 const moduleKind = getEmitModuleKind(compilerOptions); 550 if ((moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) || sourceFile.impliedNodeFormat === ModuleKind.ESNext) { 551 // use named imports 552 const helpers = getEmitHelpers(sourceFile); 553 if (helpers) { 554 const helperNames: string[] = []; 555 for (const helper of helpers) { 556 if (!helper.scoped) { 557 const importName = helper.importName; 558 if (importName) { 559 pushIfUnique(helperNames, importName); 560 } 561 } 562 } 563 if (some(helperNames)) { 564 helperNames.sort(compareStringsCaseSensitive); 565 // Alias the imports if the names are used somewhere in the file. 566 // NOTE: We don't need to care about global import collisions as this is a module. 567 namedBindings = nodeFactory.createNamedImports( 568 map(helperNames, name => isFileLevelUniqueName(sourceFile, name) 569 ? nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, nodeFactory.createIdentifier(name)) 570 : nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name)) 571 ) 572 ); 573 const parseNode = getOriginalNode(sourceFile, isSourceFile); 574 const emitNode = getOrCreateEmitNode(parseNode); 575 emitNode.externalHelpers = true; 576 } 577 } 578 } 579 else { 580 // use a namespace import 581 const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(nodeFactory, sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStar || hasImportDefault); 582 if (externalHelpersModuleName) { 583 namedBindings = nodeFactory.createNamespaceImport(externalHelpersModuleName); 584 } 585 } 586 if (namedBindings) { 587 const externalHelpersImportDeclaration = nodeFactory.createImportDeclaration( 588 /*modifiers*/ undefined, 589 nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings), 590 nodeFactory.createStringLiteral(externalHelpersModuleNameText), 591 /*assertClause*/ undefined 592 ); 593 addEmitFlags(externalHelpersImportDeclaration, EmitFlags.NeverApplyImportHelper); 594 return externalHelpersImportDeclaration; 595 } 596 } 597} 598 599/** @internal */ 600export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactory, node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) { 601 if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) { 602 const externalHelpersModuleName = getExternalHelpersModuleName(node); 603 if (externalHelpersModuleName) { 604 return externalHelpersModuleName; 605 } 606 607 const moduleKind = getEmitModuleKind(compilerOptions); 608 let create = (hasExportStarsToExportValues || (getESModuleInterop(compilerOptions) && hasImportStarOrImportDefault)) 609 && moduleKind !== ModuleKind.System 610 && (moduleKind < ModuleKind.ES2015 || node.impliedNodeFormat === ModuleKind.CommonJS); 611 if (!create) { 612 const helpers = getEmitHelpers(node); 613 if (helpers) { 614 for (const helper of helpers) { 615 if (!helper.scoped) { 616 create = true; 617 break; 618 } 619 } 620 } 621 } 622 623 if (create) { 624 const parseNode = getOriginalNode(node, isSourceFile); 625 const emitNode = getOrCreateEmitNode(parseNode); 626 return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = factory.createUniqueName(externalHelpersModuleNameText)); 627 } 628 } 629} 630 631/** 632 * Get the name of that target module from an import or export declaration 633 * 634 * @internal 635 */ 636export function getLocalNameForExternalImport(factory: NodeFactory, node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile): Identifier | undefined { 637 const namespaceDeclaration = getNamespaceDeclarationNode(node); 638 if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) { 639 const name = namespaceDeclaration.name; 640 return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name)); 641 } 642 if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) { 643 return factory.getGeneratedNameForNode(node); 644 } 645 if (node.kind === SyntaxKind.ExportDeclaration && node.moduleSpecifier) { 646 return factory.getGeneratedNameForNode(node); 647 } 648 return undefined; 649} 650 651/** 652 * Get the name of a target module from an import/export declaration as should be written in the emitted output. 653 * The emitted output name can be different from the input if: 654 * 1. The module has a /// <amd-module name="<new name>" /> 655 * 2. --out or --outFile is used, making the name relative to the rootDir 656 * 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System). 657 * Otherwise, a new StringLiteral node representing the module name will be returned. 658 * 659 * @internal 660 */ 661export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { 662 const moduleName = getExternalModuleName(importNode); 663 if (moduleName && isStringLiteral(moduleName)) { 664 return tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions) 665 || tryRenameExternalModule(factory, moduleName, sourceFile) 666 || factory.cloneNode(moduleName); 667 } 668 669 return undefined; 670} 671 672/** 673 * Some bundlers (SystemJS builder) sometimes want to rename dependencies. 674 * Here we check if alternative name was provided for a given moduleName and return it if possible. 675 */ 676function tryRenameExternalModule(factory: NodeFactory, moduleName: LiteralExpression, sourceFile: SourceFile) { 677 const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); 678 return rename ? factory.createStringLiteral(rename) : undefined; 679} 680 681/** 682 * Get the name of a module as should be written in the emitted output. 683 * The emitted output name can be different from the input if: 684 * 1. The module has a /// <amd-module name="<new name>" /> 685 * 2. --out or --outFile is used, making the name relative to the rootDir 686 * Otherwise, a new StringLiteral node representing the module name will be returned. 687 * 688 * @internal 689 */ 690export function tryGetModuleNameFromFile(factory: NodeFactory, file: SourceFile | undefined, host: EmitHost, options: CompilerOptions): StringLiteral | undefined { 691 if (!file) { 692 return undefined; 693 } 694 if (file.moduleName) { 695 return factory.createStringLiteral(file.moduleName); 696 } 697 if (!file.isDeclarationFile && outFile(options)) { 698 return factory.createStringLiteral(getExternalModuleNameFromPath(host, file.fileName)); 699 } 700 return undefined; 701} 702 703function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) { 704 return tryGetModuleNameFromFile(factory, resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); 705} 706 707/** 708 * Gets the initializer of an BindingOrAssignmentElement. 709 * 710 * @internal 711 */ 712export function getInitializerOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Expression | undefined { 713 if (isDeclarationBindingElement(bindingElement)) { 714 // `1` in `let { a = 1 } = ...` 715 // `1` in `let { a: b = 1 } = ...` 716 // `1` in `let { a: {b} = 1 } = ...` 717 // `1` in `let { a: [b] = 1 } = ...` 718 // `1` in `let [a = 1] = ...` 719 // `1` in `let [{a} = 1] = ...` 720 // `1` in `let [[a] = 1] = ...` 721 return bindingElement.initializer; 722 } 723 724 if (isPropertyAssignment(bindingElement)) { 725 // `1` in `({ a: b = 1 } = ...)` 726 // `1` in `({ a: {b} = 1 } = ...)` 727 // `1` in `({ a: [b] = 1 } = ...)` 728 const initializer = bindingElement.initializer; 729 return isAssignmentExpression(initializer, /*excludeCompoundAssignment*/ true) 730 ? initializer.right 731 : undefined; 732 } 733 734 if (isShorthandPropertyAssignment(bindingElement)) { 735 // `1` in `({ a = 1 } = ...)` 736 return bindingElement.objectAssignmentInitializer; 737 } 738 739 if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { 740 // `1` in `[a = 1] = ...` 741 // `1` in `[{a} = 1] = ...` 742 // `1` in `[[a] = 1] = ...` 743 return bindingElement.right; 744 } 745 746 if (isSpreadElement(bindingElement)) { 747 // Recovery consistent with existing emit. 748 return getInitializerOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement); 749 } 750} 751 752/** 753 * Gets the name of an BindingOrAssignmentElement. 754 * 755 * @internal 756 */ 757export function getTargetOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementTarget | undefined { 758 if (isDeclarationBindingElement(bindingElement)) { 759 // `a` in `let { a } = ...` 760 // `a` in `let { a = 1 } = ...` 761 // `b` in `let { a: b } = ...` 762 // `b` in `let { a: b = 1 } = ...` 763 // `a` in `let { ...a } = ...` 764 // `{b}` in `let { a: {b} } = ...` 765 // `{b}` in `let { a: {b} = 1 } = ...` 766 // `[b]` in `let { a: [b] } = ...` 767 // `[b]` in `let { a: [b] = 1 } = ...` 768 // `a` in `let [a] = ...` 769 // `a` in `let [a = 1] = ...` 770 // `a` in `let [...a] = ...` 771 // `{a}` in `let [{a}] = ...` 772 // `{a}` in `let [{a} = 1] = ...` 773 // `[a]` in `let [[a]] = ...` 774 // `[a]` in `let [[a] = 1] = ...` 775 return bindingElement.name; 776 } 777 778 if (isObjectLiteralElementLike(bindingElement)) { 779 switch (bindingElement.kind) { 780 case SyntaxKind.PropertyAssignment: 781 // `b` in `({ a: b } = ...)` 782 // `b` in `({ a: b = 1 } = ...)` 783 // `{b}` in `({ a: {b} } = ...)` 784 // `{b}` in `({ a: {b} = 1 } = ...)` 785 // `[b]` in `({ a: [b] } = ...)` 786 // `[b]` in `({ a: [b] = 1 } = ...)` 787 // `b.c` in `({ a: b.c } = ...)` 788 // `b.c` in `({ a: b.c = 1 } = ...)` 789 // `b[0]` in `({ a: b[0] } = ...)` 790 // `b[0]` in `({ a: b[0] = 1 } = ...)` 791 return getTargetOfBindingOrAssignmentElement(bindingElement.initializer as BindingOrAssignmentElement); 792 793 case SyntaxKind.ShorthandPropertyAssignment: 794 // `a` in `({ a } = ...)` 795 // `a` in `({ a = 1 } = ...)` 796 return bindingElement.name; 797 798 case SyntaxKind.SpreadAssignment: 799 // `a` in `({ ...a } = ...)` 800 return getTargetOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement); 801 } 802 803 } 804 805 if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) { 806 // `a` in `[a = 1] = ...` 807 // `{a}` in `[{a} = 1] = ...` 808 // `[a]` in `[[a] = 1] = ...` 809 // `a.b` in `[a.b = 1] = ...` 810 // `a[0]` in `[a[0] = 1] = ...` 811 return getTargetOfBindingOrAssignmentElement(bindingElement.left as BindingOrAssignmentElement); 812 } 813 814 if (isSpreadElement(bindingElement)) { 815 // `a` in `[...a] = ...` 816 return getTargetOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement); 817 } 818 819 // `a` in `[a] = ...` 820 // `{a}` in `[{a}] = ...` 821 // `[a]` in `[[a]] = ...` 822 // `a.b` in `[a.b] = ...` 823 // `a[0]` in `[a[0]] = ...` 824 return bindingElement; 825} 826 827/** 828 * Determines whether an BindingOrAssignmentElement is a rest element. 829 * 830 * @internal 831 */ 832export function getRestIndicatorOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementRestIndicator | undefined { 833 switch (bindingElement.kind) { 834 case SyntaxKind.Parameter: 835 case SyntaxKind.BindingElement: 836 // `...` in `let [...a] = ...` 837 return bindingElement.dotDotDotToken; 838 839 case SyntaxKind.SpreadElement: 840 case SyntaxKind.SpreadAssignment: 841 // `...` in `[...a] = ...` 842 return bindingElement; 843 } 844 845 return undefined; 846} 847 848/** 849 * Gets the property name of a BindingOrAssignmentElement 850 * 851 * @internal 852 */ 853export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined { 854 const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement); 855 Debug.assert(!!propertyName || isSpreadAssignment(bindingElement), "Invalid property name for binding element."); 856 return propertyName; 857} 858 859/** @internal */ 860export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined { 861 switch (bindingElement.kind) { 862 case SyntaxKind.BindingElement: 863 // `a` in `let { a: b } = ...` 864 // `[a]` in `let { [a]: b } = ...` 865 // `"a"` in `let { "a": b } = ...` 866 // `1` in `let { 1: b } = ...` 867 if (bindingElement.propertyName) { 868 const propertyName = bindingElement.propertyName; 869 if (isPrivateIdentifier(propertyName)) { 870 return Debug.failBadSyntaxKind(propertyName); 871 } 872 return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression) 873 ? propertyName.expression 874 : propertyName; 875 } 876 877 break; 878 879 case SyntaxKind.PropertyAssignment: 880 // `a` in `({ a: b } = ...)` 881 // `[a]` in `({ [a]: b } = ...)` 882 // `"a"` in `({ "a": b } = ...)` 883 // `1` in `({ 1: b } = ...)` 884 if (bindingElement.name) { 885 const propertyName = bindingElement.name; 886 if (isPrivateIdentifier(propertyName)) { 887 return Debug.failBadSyntaxKind(propertyName); 888 } 889 return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression) 890 ? propertyName.expression 891 : propertyName; 892 } 893 894 break; 895 896 case SyntaxKind.SpreadAssignment: 897 // `a` in `({ ...a } = ...)` 898 if (bindingElement.name && isPrivateIdentifier(bindingElement.name)) { 899 return Debug.failBadSyntaxKind(bindingElement.name); 900 } 901 return bindingElement.name; 902 } 903 904 const target = getTargetOfBindingOrAssignmentElement(bindingElement); 905 if (target && isPropertyName(target)) { 906 return target; 907 } 908} 909 910function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral { 911 const kind = node.kind; 912 return kind === SyntaxKind.StringLiteral 913 || kind === SyntaxKind.NumericLiteral; 914} 915 916/** 917 * Gets the elements of a BindingOrAssignmentPattern 918 * 919 * @internal 920 */ 921export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): readonly BindingOrAssignmentElement[] { 922 switch (name.kind) { 923 case SyntaxKind.ObjectBindingPattern: 924 case SyntaxKind.ArrayBindingPattern: 925 case SyntaxKind.ArrayLiteralExpression: 926 // `a` in `{a}` 927 // `a` in `[a]` 928 return name.elements as readonly BindingOrAssignmentElement[]; 929 930 case SyntaxKind.ObjectLiteralExpression: 931 // `a` in `{a}` 932 return name.properties as readonly BindingOrAssignmentElement[]; 933 } 934} 935 936/** @internal */ 937export function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) { 938 if (fullName) { 939 let rightNode = fullName; 940 while (true) { 941 if (isIdentifier(rightNode) || !rightNode.body) { 942 return isIdentifier(rightNode) ? rightNode : rightNode.name; 943 } 944 rightNode = rightNode.body; 945 } 946 } 947} 948 949/** @internal */ 950export function canHaveIllegalType(node: Node): node is HasIllegalType { 951 const kind = node.kind; 952 return kind === SyntaxKind.Constructor 953 || kind === SyntaxKind.SetAccessor; 954} 955 956/** @internal */ 957export function canHaveIllegalTypeParameters(node: Node): node is HasIllegalTypeParameters { 958 const kind = node.kind; 959 return kind === SyntaxKind.Constructor 960 || kind === SyntaxKind.GetAccessor 961 || kind === SyntaxKind.SetAccessor; 962} 963 964/** @internal */ 965export function canHaveIllegalDecorators(node: Node): node is HasIllegalDecorators { 966 const kind = node.kind; 967 return kind === SyntaxKind.PropertyAssignment 968 || kind === SyntaxKind.ShorthandPropertyAssignment 969 || kind === SyntaxKind.FunctionDeclaration 970 || kind === SyntaxKind.Constructor 971 || kind === SyntaxKind.IndexSignature 972 || kind === SyntaxKind.ClassStaticBlockDeclaration 973 || kind === SyntaxKind.MissingDeclaration 974 || kind === SyntaxKind.VariableStatement 975 || kind === SyntaxKind.InterfaceDeclaration 976 || kind === SyntaxKind.TypeAliasDeclaration 977 || kind === SyntaxKind.EnumDeclaration 978 || kind === SyntaxKind.ModuleDeclaration 979 || kind === SyntaxKind.ImportEqualsDeclaration 980 || kind === SyntaxKind.ImportDeclaration 981 || kind === SyntaxKind.NamespaceExportDeclaration 982 || kind === SyntaxKind.ExportDeclaration 983 || kind === SyntaxKind.ExportAssignment; 984} 985 986/** @internal */ 987export function canHaveIllegalModifiers(node: Node): node is HasIllegalModifiers { 988 const kind = node.kind; 989 return kind === SyntaxKind.ClassStaticBlockDeclaration 990 || kind === SyntaxKind.PropertyAssignment 991 || kind === SyntaxKind.ShorthandPropertyAssignment 992 || kind === SyntaxKind.FunctionType 993 || kind === SyntaxKind.MissingDeclaration 994 || kind === SyntaxKind.NamespaceExportDeclaration; 995} 996 997/** @internal */ 998export const isTypeNodeOrTypeParameterDeclaration = or(isTypeNode, isTypeParameterDeclaration) as (node: Node) => node is TypeNode | TypeParameterDeclaration; 999/** @internal */ 1000export const isQuestionOrExclamationToken = or(isQuestionToken, isExclamationToken) as (node: Node) => node is QuestionToken | ExclamationToken; 1001/** @internal */ 1002export const isIdentifierOrThisTypeNode = or(isIdentifier, isThisTypeNode) as (node: Node) => node is Identifier | ThisTypeNode; 1003/** @internal */ 1004export const isReadonlyKeywordOrPlusOrMinusToken = or(isReadonlyKeyword, isPlusToken, isMinusToken) as (node: Node) => node is ReadonlyKeyword | PlusToken | MinusToken; 1005/** @internal */ 1006export const isQuestionOrPlusOrMinusToken = or(isQuestionToken, isPlusToken, isMinusToken) as (node: Node) => node is QuestionToken | PlusToken | MinusToken; 1007/** @internal */ 1008export const isModuleName = or(isIdentifier, isStringLiteral) as (node: Node) => node is ModuleName; 1009 1010/** @internal */ 1011export function isLiteralTypeLikeExpression(node: Node): node is NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression { 1012 const kind = node.kind; 1013 return kind === SyntaxKind.NullKeyword 1014 || kind === SyntaxKind.TrueKeyword 1015 || kind === SyntaxKind.FalseKeyword 1016 || isLiteralExpression(node) 1017 || isPrefixUnaryExpression(node); 1018} 1019 1020function isExponentiationOperator(kind: SyntaxKind): kind is ExponentiationOperator { 1021 return kind === SyntaxKind.AsteriskAsteriskToken; 1022} 1023 1024function isMultiplicativeOperator(kind: SyntaxKind): kind is MultiplicativeOperator { 1025 return kind === SyntaxKind.AsteriskToken 1026 || kind === SyntaxKind.SlashToken 1027 || kind === SyntaxKind.PercentToken; 1028} 1029 1030function isMultiplicativeOperatorOrHigher(kind: SyntaxKind): kind is MultiplicativeOperatorOrHigher { 1031 return isExponentiationOperator(kind) 1032 || isMultiplicativeOperator(kind); 1033} 1034 1035function isAdditiveOperator(kind: SyntaxKind): kind is AdditiveOperator { 1036 return kind === SyntaxKind.PlusToken 1037 || kind === SyntaxKind.MinusToken; 1038} 1039 1040function isAdditiveOperatorOrHigher(kind: SyntaxKind): kind is AdditiveOperatorOrHigher { 1041 return isAdditiveOperator(kind) 1042 || isMultiplicativeOperatorOrHigher(kind); 1043} 1044 1045function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator { 1046 return kind === SyntaxKind.LessThanLessThanToken 1047 || kind === SyntaxKind.GreaterThanGreaterThanToken 1048 || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken; 1049} 1050 1051function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher { 1052 return isShiftOperator(kind) 1053 || isAdditiveOperatorOrHigher(kind); 1054} 1055 1056function isRelationalOperator(kind: SyntaxKind): kind is RelationalOperator { 1057 return kind === SyntaxKind.LessThanToken 1058 || kind === SyntaxKind.LessThanEqualsToken 1059 || kind === SyntaxKind.GreaterThanToken 1060 || kind === SyntaxKind.GreaterThanEqualsToken 1061 || kind === SyntaxKind.InstanceOfKeyword 1062 || kind === SyntaxKind.InKeyword; 1063} 1064 1065function isRelationalOperatorOrHigher(kind: SyntaxKind): kind is RelationalOperatorOrHigher { 1066 return isRelationalOperator(kind) 1067 || isShiftOperatorOrHigher(kind); 1068} 1069 1070function isEqualityOperator(kind: SyntaxKind): kind is EqualityOperator { 1071 return kind === SyntaxKind.EqualsEqualsToken 1072 || kind === SyntaxKind.EqualsEqualsEqualsToken 1073 || kind === SyntaxKind.ExclamationEqualsToken 1074 || kind === SyntaxKind.ExclamationEqualsEqualsToken; 1075} 1076 1077function isEqualityOperatorOrHigher(kind: SyntaxKind): kind is EqualityOperatorOrHigher { 1078 return isEqualityOperator(kind) 1079 || isRelationalOperatorOrHigher(kind); 1080} 1081 1082function isBitwiseOperator(kind: SyntaxKind): kind is BitwiseOperator { 1083 return kind === SyntaxKind.AmpersandToken 1084 || kind === SyntaxKind.BarToken 1085 || kind === SyntaxKind.CaretToken; 1086} 1087 1088function isBitwiseOperatorOrHigher(kind: SyntaxKind): kind is BitwiseOperatorOrHigher { 1089 return isBitwiseOperator(kind) 1090 || isEqualityOperatorOrHigher(kind); 1091} 1092 1093// NOTE: The version in utilities includes ExclamationToken, which is not a binary operator. 1094function isLogicalOperator(kind: SyntaxKind): kind is LogicalOperator { 1095 return kind === SyntaxKind.AmpersandAmpersandToken 1096 || kind === SyntaxKind.BarBarToken; 1097} 1098 1099function isLogicalOperatorOrHigher(kind: SyntaxKind): kind is LogicalOperatorOrHigher { 1100 return isLogicalOperator(kind) 1101 || isBitwiseOperatorOrHigher(kind); 1102} 1103 1104function isAssignmentOperatorOrHigher(kind: SyntaxKind): kind is AssignmentOperatorOrHigher { 1105 return kind === SyntaxKind.QuestionQuestionToken 1106 || isLogicalOperatorOrHigher(kind) 1107 || isAssignmentOperator(kind); 1108} 1109 1110function isBinaryOperator(kind: SyntaxKind): kind is BinaryOperator { 1111 return isAssignmentOperatorOrHigher(kind) 1112 || kind === SyntaxKind.CommaToken; 1113} 1114 1115/** @internal */ 1116export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken { 1117 return isBinaryOperator(node.kind); 1118} 1119 1120type BinaryExpressionState = <TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, outerState: TOuterState) => number; 1121 1122namespace BinaryExpressionState { 1123 /** 1124 * Handles walking into a `BinaryExpression`. 1125 * @param machine State machine handler functions 1126 * @param frame The current frame 1127 * @returns The new frame 1128 */ 1129 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 { 1130 const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined; 1131 Debug.assertEqual(stateStack[stackIndex], enter); 1132 userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState); 1133 stateStack[stackIndex] = nextState(machine, enter); 1134 return stackIndex; 1135 } 1136 1137 /** 1138 * Handles walking the `left` side of a `BinaryExpression`. 1139 * @param machine State machine handler functions 1140 * @param frame The current frame 1141 * @returns The new frame 1142 */ 1143 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 { 1144 Debug.assertEqual(stateStack[stackIndex], left); 1145 Debug.assertIsDefined(machine.onLeft); 1146 stateStack[stackIndex] = nextState(machine, left); 1147 const nextNode = machine.onLeft(nodeStack[stackIndex].left, userStateStack[stackIndex], nodeStack[stackIndex]); 1148 if (nextNode) { 1149 checkCircularity(stackIndex, nodeStack, nextNode); 1150 return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); 1151 } 1152 return stackIndex; 1153 } 1154 1155 /** 1156 * Handles walking the `operatorToken` of a `BinaryExpression`. 1157 * @param machine State machine handler functions 1158 * @param frame The current frame 1159 * @returns The new frame 1160 */ 1161 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 { 1162 Debug.assertEqual(stateStack[stackIndex], operator); 1163 Debug.assertIsDefined(machine.onOperator); 1164 stateStack[stackIndex] = nextState(machine, operator); 1165 machine.onOperator(nodeStack[stackIndex].operatorToken, userStateStack[stackIndex], nodeStack[stackIndex]); 1166 return stackIndex; 1167 } 1168 1169 /** 1170 * Handles walking the `right` side of a `BinaryExpression`. 1171 * @param machine State machine handler functions 1172 * @param frame The current frame 1173 * @returns The new frame 1174 */ 1175 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 { 1176 Debug.assertEqual(stateStack[stackIndex], right); 1177 Debug.assertIsDefined(machine.onRight); 1178 stateStack[stackIndex] = nextState(machine, right); 1179 const nextNode = machine.onRight(nodeStack[stackIndex].right, userStateStack[stackIndex], nodeStack[stackIndex]); 1180 if (nextNode) { 1181 checkCircularity(stackIndex, nodeStack, nextNode); 1182 return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); 1183 } 1184 return stackIndex; 1185 } 1186 1187 /** 1188 * Handles walking out of a `BinaryExpression`. 1189 * @param machine State machine handler functions 1190 * @param frame The current frame 1191 * @returns The new frame 1192 */ 1193 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 { 1194 Debug.assertEqual(stateStack[stackIndex], exit); 1195 stateStack[stackIndex] = nextState(machine, exit); 1196 const result = machine.onExit(nodeStack[stackIndex], userStateStack[stackIndex]); 1197 if (stackIndex > 0) { 1198 stackIndex--; 1199 if (machine.foldState) { 1200 const side = stateStack[stackIndex] === exit ? "right" : "left"; 1201 userStateStack[stackIndex] = machine.foldState(userStateStack[stackIndex], result, side); 1202 } 1203 } 1204 else { 1205 resultHolder.value = result; 1206 } 1207 return stackIndex; 1208 } 1209 1210 /** 1211 * Handles a frame that is already done. 1212 * @returns The `done` state. 1213 */ 1214 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 { 1215 Debug.assertEqual(stateStack[stackIndex], done); 1216 return stackIndex; 1217 } 1218 1219 export function nextState<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, currentState: BinaryExpressionState) { 1220 switch (currentState) { 1221 case enter: 1222 if (machine.onLeft) return left; 1223 // falls through 1224 case left: 1225 if (machine.onOperator) return operator; 1226 // falls through 1227 case operator: 1228 if (machine.onRight) return right; 1229 // falls through 1230 case right: return exit; 1231 case exit: return done; 1232 case done: return done; 1233 default: Debug.fail("Invalid state"); 1234 } 1235 } 1236 1237 function pushStack<TState>(stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], node: BinaryExpression) { 1238 stackIndex++; 1239 stateStack[stackIndex] = enter; 1240 nodeStack[stackIndex] = node; 1241 userStateStack[stackIndex] = undefined!; 1242 return stackIndex; 1243 } 1244 1245 function checkCircularity(stackIndex: number, nodeStack: BinaryExpression[], node: BinaryExpression) { 1246 if (Debug.shouldAssert(AssertionLevel.Aggressive)) { 1247 while (stackIndex >= 0) { 1248 Debug.assert(nodeStack[stackIndex] !== node, "Circular traversal detected."); 1249 stackIndex--; 1250 } 1251 } 1252 } 1253} 1254 1255/** 1256 * Holds state machine handler functions 1257 */ 1258class BinaryExpressionStateMachine<TOuterState, TState, TResult> { 1259 constructor( 1260 readonly onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, 1261 readonly onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1262 readonly onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1263 readonly onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1264 readonly onExit: (node: BinaryExpression, userState: TState) => TResult, 1265 readonly foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1266 ) { 1267 } 1268} 1269 1270/** 1271 * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. 1272 * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. 1273 * @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. 1274 * @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. 1275 * @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. 1276 * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. 1277 * @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. 1278 * 1279 * @internal 1280 */ 1281 export function createBinaryExpressionTrampoline<TState, TResult>( 1282 onEnter: (node: BinaryExpression, prev: TState | undefined) => TState, 1283 onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1284 onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1285 onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1286 onExit: (node: BinaryExpression, userState: TState) => TResult, 1287 foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1288): (node: BinaryExpression) => TResult; 1289/** 1290 * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. 1291 * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. 1292 * @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. 1293 * @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. 1294 * @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. 1295 * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. 1296 * @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. 1297 * 1298 * @internal 1299 */ 1300export function createBinaryExpressionTrampoline<TOuterState, TState, TResult>( 1301 onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, 1302 onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1303 onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1304 onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1305 onExit: (node: BinaryExpression, userState: TState) => TResult, 1306 foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1307): (node: BinaryExpression, outerState: TOuterState) => TResult; 1308/** @internal */ 1309export function createBinaryExpressionTrampoline<TOuterState, TState, TResult>( 1310 onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, 1311 onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1312 onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, 1313 onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, 1314 onExit: (node: BinaryExpression, userState: TState) => TResult, 1315 foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, 1316) { 1317 const machine = new BinaryExpressionStateMachine(onEnter, onLeft, onOperator, onRight, onExit, foldState); 1318 return trampoline; 1319 1320 function trampoline(node: BinaryExpression, outerState?: TOuterState) { 1321 const resultHolder: { value: TResult } = { value: undefined! }; 1322 const stateStack: BinaryExpressionState[] = [BinaryExpressionState.enter]; 1323 const nodeStack: BinaryExpression[] = [node]; 1324 const userStateStack: TState[] = [undefined!]; 1325 let stackIndex = 0; 1326 while (stateStack[stackIndex] !== BinaryExpressionState.done) { 1327 stackIndex = stateStack[stackIndex](machine, stackIndex, stateStack, nodeStack, userStateStack, resultHolder, outerState); 1328 } 1329 Debug.assertEqual(stackIndex, 0); 1330 return resultHolder.value; 1331 } 1332} 1333 1334/** 1335 * If `nodes` is not undefined, creates an empty `NodeArray` that preserves the `pos` and `end` of `nodes`. 1336 * @internal 1337 */ 1338export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T>): NodeArray<T>; 1339/** @internal */ 1340export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined; 1341/** @internal */ 1342export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined { 1343 if (nodes === undefined) return undefined; 1344 if (nodes.length === 0) return nodes; 1345 return setTextRange(factory.createNodeArray([], nodes.hasTrailingComma), nodes); 1346} 1347 1348/** 1349 * Gets the node from which a name should be generated. 1350 * 1351 * @internal 1352 */ 1353export function getNodeForGeneratedName(name: GeneratedIdentifier | GeneratedPrivateIdentifier) { 1354 if (name.autoGenerateFlags & GeneratedIdentifierFlags.Node) { 1355 const autoGenerateId = name.autoGenerateId; 1356 let node = name as Node; 1357 let original = node.original; 1358 while (original) { 1359 node = original; 1360 1361 // if "node" is a different generated name (having a different "autoGenerateId"), use it and stop traversing. 1362 if (isMemberName(node) 1363 && !!(node.autoGenerateFlags! & GeneratedIdentifierFlags.Node) 1364 && node.autoGenerateId !== autoGenerateId) { 1365 break; 1366 } 1367 1368 original = node.original; 1369 } 1370 // otherwise, return the original node for the source 1371 return node; 1372 } 1373 return name; 1374} 1375 1376/** 1377 * Formats a prefix or suffix of a generated name. 1378 * 1379 * @internal 1380 */ 1381export function formatGeneratedNamePart(part: string | undefined): string; 1382/** 1383 * Formats a prefix or suffix of a generated name. If the part is a {@link GeneratedNamePart}, calls {@link generateName} to format the source node. 1384 * 1385 * @internal 1386 */ 1387export function formatGeneratedNamePart(part: string | GeneratedNamePart | undefined, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string; 1388/** @internal */ 1389export function formatGeneratedNamePart(part: string | GeneratedNamePart | undefined, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string { 1390 return typeof part === "object" ? formatGeneratedName(/*privateName*/ false, part.prefix, part.node, part.suffix, generateName!) : 1391 typeof part === "string" ? part.length > 0 && part.charCodeAt(0) === CharacterCodes.hash ? part.slice(1) : part : 1392 ""; 1393} 1394 1395function formatIdentifier(name: string | Identifier | PrivateIdentifier, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) { 1396 return typeof name === "string" ? name : 1397 formatIdentifierWorker(name, Debug.checkDefined(generateName)); 1398} 1399 1400function formatIdentifierWorker(node: Identifier | PrivateIdentifier, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) { 1401 return isGeneratedPrivateIdentifier(node) ? generateName(node).slice(1) : 1402 isGeneratedIdentifier(node) ? generateName(node) : 1403 isPrivateIdentifier(node) ? (node.escapedText as string).slice(1) : 1404 idText(node); 1405} 1406 1407/** 1408 * Formats a generated name. 1409 * @param privateName When `true`, inserts a `#` character at the start of the result. 1410 * @param prefix The prefix (if any) to include before the base name. 1411 * @param baseName The base name for the generated name. 1412 * @param suffix The suffix (if any) to include after the base name. 1413 * 1414 * @internal 1415 */ 1416export function formatGeneratedName(privateName: boolean, prefix: string | undefined, baseName: string, suffix: string | undefined): string; 1417/** 1418 * Formats a generated name. 1419 * @param privateName When `true`, inserts a `#` character at the start of the result. 1420 * @param prefix The prefix (if any) to include before the base name. 1421 * @param baseName The base name for the generated name. 1422 * @param suffix The suffix (if any) to include after the base name. 1423 * @param generateName Called to format the source node of {@link prefix} when it is a {@link GeneratedNamePart}. 1424 * 1425 * @internal 1426 */ 1427export function formatGeneratedName(privateName: boolean, prefix: string | GeneratedNamePart | undefined, baseName: string | Identifier | PrivateIdentifier, suffix: string | GeneratedNamePart | undefined, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string; 1428/** @internal */ 1429export function formatGeneratedName(privateName: boolean, prefix: string | GeneratedNamePart | undefined, baseName: string | Identifier | PrivateIdentifier, suffix: string | GeneratedNamePart | undefined, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) { 1430 prefix = formatGeneratedNamePart(prefix, generateName!); 1431 suffix = formatGeneratedNamePart(suffix, generateName!); 1432 baseName = formatIdentifier(baseName, generateName); 1433 return `${privateName ? "#" : ""}${prefix}${baseName}${suffix}`; 1434} 1435 1436 1437/** 1438 * Creates a private backing field for an `accessor` {@link PropertyDeclaration}. 1439 * 1440 * @internal 1441 */ 1442export function createAccessorPropertyBackingField(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, initializer: Expression | undefined) { 1443 return factory.updatePropertyDeclaration( 1444 node, 1445 modifiers, 1446 factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage"), 1447 /*questionOrExclamationToken*/ undefined, 1448 /*type*/ undefined, 1449 initializer 1450 ); 1451} 1452 1453/** 1454 * Creates a {@link GetAccessorDeclaration} that reads from a private backing field. 1455 * 1456 * @internal 1457 */ 1458export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): GetAccessorDeclaration { 1459 return factory.createGetAccessorDeclaration( 1460 modifiers, 1461 name, 1462 [], 1463 /*type*/ undefined, 1464 factory.createBlock([ 1465 factory.createReturnStatement( 1466 factory.createPropertyAccessExpression( 1467 factory.createThis(), 1468 factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage") 1469 ) 1470 ) 1471 ]) 1472 ); 1473} 1474 1475/** 1476 * Creates a {@link SetAccessorDeclaration} that writes to a private backing field. 1477 * 1478 * @internal 1479 */ 1480export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): SetAccessorDeclaration { 1481 return factory.createSetAccessorDeclaration( 1482 modifiers, 1483 name, 1484 [factory.createParameterDeclaration( 1485 /*modifiers*/ undefined, 1486 /*dotdotDotToken*/ undefined, 1487 "value" 1488 )], 1489 factory.createBlock([ 1490 factory.createExpressionStatement( 1491 factory.createAssignment( 1492 factory.createPropertyAccessExpression( 1493 factory.createThis(), 1494 factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage") 1495 ), 1496 factory.createIdentifier("value") 1497 ) 1498 ) 1499 ]) 1500 ); 1501} 1502