1/* @internal */ 2namespace ts { 3 export function getOriginalNodeId(node: Node) { 4 node = getOriginalNode(node); 5 return node ? getNodeId(node) : 0; 6 } 7 8 export interface ExternalModuleInfo { 9 externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules 10 externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers 11 exportSpecifiers: ESMap<string, ExportSpecifier[]>; // file-local export specifiers by name (no reexports) 12 exportedBindings: Identifier[][]; // exported names of local declarations 13 exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported 14 exportEquals: ExportAssignment | undefined; // an export= declaration if one was present 15 hasExportStarsToExportValues: boolean; // whether this module contains export* 16 } 17 18 function containsDefaultReference(node: NamedImportBindings | undefined) { 19 if (!node) return false; 20 if (!isNamedImports(node)) return false; 21 return some(node.elements, isNamedDefaultReference); 22 } 23 24 function isNamedDefaultReference(e: ImportSpecifier): boolean { 25 return e.propertyName !== undefined && e.propertyName.escapedText === InternalSymbolName.Default; 26 } 27 28 export function chainBundle(context: CoreTransformationContext, transformSourceFile: (x: SourceFile) => SourceFile): (x: SourceFile | Bundle) => SourceFile | Bundle { 29 return transformSourceFileOrBundle; 30 31 function transformSourceFileOrBundle(node: SourceFile | Bundle) { 32 return node.kind === SyntaxKind.SourceFile ? transformSourceFile(node) : transformBundle(node); 33 } 34 35 function transformBundle(node: Bundle) { 36 return context.factory.createBundle(map(node.sourceFiles, transformSourceFile), node.prepends); 37 } 38 } 39 40 export function getExportNeedsImportStarHelper(node: ExportDeclaration): boolean { 41 return !!getNamespaceDeclarationNode(node); 42 } 43 44 export function getImportNeedsImportStarHelper(node: ImportDeclaration): boolean { 45 if (!!getNamespaceDeclarationNode(node)) { 46 return true; 47 } 48 const bindings = node.importClause && node.importClause.namedBindings; 49 if (!bindings) { 50 return false; 51 } 52 if (!isNamedImports(bindings)) return false; 53 let defaultRefCount = 0; 54 for (const binding of bindings.elements) { 55 if (isNamedDefaultReference(binding)) { 56 defaultRefCount++; 57 } 58 } 59 // Import star is required if there's default named refs mixed with non-default refs, or if theres non-default refs and it has a default import 60 return (defaultRefCount > 0 && defaultRefCount !== bindings.elements.length) || (!!(bindings.elements.length - defaultRefCount) && isDefaultImport(node)); 61 } 62 63 export function getImportNeedsImportDefaultHelper(node: ImportDeclaration): boolean { 64 // Import default is needed if there's a default import or a default ref and no other refs (meaning an import star helper wasn't requested) 65 return !getImportNeedsImportStarHelper(node) && (isDefaultImport(node) || (!!node.importClause && isNamedImports(node.importClause.namedBindings!) && containsDefaultReference(node.importClause.namedBindings))); // TODO: GH#18217 66 } 67 68 export function collectExternalModuleInfo(context: TransformationContext, sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { 69 const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; 70 const exportSpecifiers = createMultiMap<ExportSpecifier>(); 71 const exportedBindings: Identifier[][] = []; 72 const uniqueExports = new Map<string, boolean>(); 73 let exportedNames: Identifier[] | undefined; 74 let hasExportDefault = false; 75 let exportEquals: ExportAssignment | undefined; 76 let hasExportStarsToExportValues = false; 77 let hasImportStar = false; 78 let hasImportDefault = false; 79 80 for (const node of sourceFile.statements) { 81 switch (node.kind) { 82 case SyntaxKind.ImportDeclaration: 83 // import "mod" 84 // import x from "mod" 85 // import * as x from "mod" 86 // import { x, y } from "mod" 87 externalImports.push(node as ImportDeclaration); 88 if (!hasImportStar && getImportNeedsImportStarHelper(node as ImportDeclaration)) { 89 hasImportStar = true; 90 } 91 if (!hasImportDefault && getImportNeedsImportDefaultHelper(node as ImportDeclaration)) { 92 hasImportDefault = true; 93 } 94 break; 95 96 case SyntaxKind.ImportEqualsDeclaration: 97 if ((node as ImportEqualsDeclaration).moduleReference.kind === SyntaxKind.ExternalModuleReference) { 98 // import x = require("mod") 99 externalImports.push(node as ImportEqualsDeclaration); 100 } 101 102 break; 103 104 case SyntaxKind.ExportDeclaration: 105 if ((node as ExportDeclaration).moduleSpecifier) { 106 if (!(node as ExportDeclaration).exportClause) { 107 // export * from "mod" 108 externalImports.push(node as ExportDeclaration); 109 hasExportStarsToExportValues = true; 110 } 111 else { 112 // export * as ns from "mod" 113 // export { x, y } from "mod" 114 externalImports.push(node as ExportDeclaration); 115 if (isNamedExports((node as ExportDeclaration).exportClause!)) { 116 addExportedNamesForExportDeclaration(node as ExportDeclaration); 117 } 118 else { 119 const name = ((node as ExportDeclaration).exportClause as NamespaceExport).name; 120 if (!uniqueExports.get(idText(name))) { 121 multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); 122 uniqueExports.set(idText(name), true); 123 exportedNames = append(exportedNames, name); 124 } 125 // we use the same helpers for `export * as ns` as we do for `import * as ns` 126 hasImportStar = true; 127 } 128 } 129 } 130 else { 131 // export { x, y } 132 addExportedNamesForExportDeclaration(node as ExportDeclaration); 133 } 134 break; 135 136 case SyntaxKind.ExportAssignment: 137 if ((node as ExportAssignment).isExportEquals && !exportEquals) { 138 // export = x 139 exportEquals = node as ExportAssignment; 140 } 141 break; 142 143 case SyntaxKind.VariableStatement: 144 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 145 for (const decl of (node as VariableStatement).declarationList.declarations) { 146 exportedNames = collectExportedVariableInfo(decl, uniqueExports, exportedNames); 147 } 148 } 149 break; 150 151 case SyntaxKind.FunctionDeclaration: 152 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 153 if (hasSyntacticModifier(node, ModifierFlags.Default)) { 154 // export default function() { } 155 if (!hasExportDefault) { 156 multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), context.factory.getDeclarationName(node as FunctionDeclaration)); 157 hasExportDefault = true; 158 } 159 } 160 else { 161 // export function x() { } 162 const name = (node as FunctionDeclaration).name!; 163 if (!uniqueExports.get(idText(name))) { 164 multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); 165 uniqueExports.set(idText(name), true); 166 exportedNames = append(exportedNames, name); 167 } 168 } 169 } 170 break; 171 172 case SyntaxKind.ClassDeclaration: 173 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 174 if (hasSyntacticModifier(node, ModifierFlags.Default)) { 175 // export default class { } 176 if (!hasExportDefault) { 177 multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), context.factory.getDeclarationName(node as ClassDeclaration)); 178 hasExportDefault = true; 179 } 180 } 181 else { 182 // export class x { } 183 const name = (node as ClassDeclaration).name; 184 if (name && !uniqueExports.get(idText(name))) { 185 multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); 186 uniqueExports.set(idText(name), true); 187 exportedNames = append(exportedNames, name); 188 } 189 } 190 } 191 break; 192 } 193 } 194 195 const externalHelpersImportDeclaration = createExternalHelpersImportDeclarationIfNeeded(context.factory, context.getEmitHelperFactory(), sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStar, hasImportDefault); 196 if (externalHelpersImportDeclaration) { 197 externalImports.unshift(externalHelpersImportDeclaration); 198 } 199 200 return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration }; 201 202 function addExportedNamesForExportDeclaration(node: ExportDeclaration) { 203 for (const specifier of cast(node.exportClause, isNamedExports).elements) { 204 if (!uniqueExports.get(idText(specifier.name))) { 205 const name = specifier.propertyName || specifier.name; 206 if (!node.moduleSpecifier) { 207 exportSpecifiers.add(idText(name), specifier); 208 } 209 210 const decl = resolver.getReferencedImportDeclaration(name) 211 || resolver.getReferencedValueDeclaration(name); 212 213 if (decl) { 214 multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); 215 } 216 217 uniqueExports.set(idText(specifier.name), true); 218 exportedNames = append(exportedNames, specifier.name); 219 } 220 } 221 } 222 } 223 224 function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: ESMap<string, boolean>, exportedNames: Identifier[] | undefined) { 225 if (isBindingPattern(decl.name)) { 226 for (const element of decl.name.elements) { 227 if (!isOmittedExpression(element)) { 228 exportedNames = collectExportedVariableInfo(element, uniqueExports, exportedNames); 229 } 230 } 231 } 232 else if (!isGeneratedIdentifier(decl.name)) { 233 const text = idText(decl.name); 234 if (!uniqueExports.get(text)) { 235 uniqueExports.set(text, true); 236 exportedNames = append(exportedNames, decl.name); 237 } 238 } 239 return exportedNames; 240 } 241 242 /** Use a sparse array as a multi-map. */ 243 function multiMapSparseArrayAdd<V>(map: V[][], key: number, value: V): V[] { 244 let values = map[key]; 245 if (values) { 246 values.push(value); 247 } 248 else { 249 map[key] = values = [value]; 250 } 251 return values; 252 } 253 254 /** 255 * Used in the module transformer to check if an expression is reasonably without sideeffect, 256 * and thus better to copy into multiple places rather than to cache in a temporary variable 257 * - this is mostly subjective beyond the requirement that the expression not be sideeffecting 258 */ 259 export function isSimpleCopiableExpression(expression: Expression) { 260 return isStringLiteralLike(expression) || 261 expression.kind === SyntaxKind.NumericLiteral || 262 isKeyword(expression.kind) || 263 isIdentifier(expression); 264 } 265 266 /** 267 * A simple inlinable expression is an expression which can be copied into multiple locations 268 * without risk of repeating any sideeffects and whose value could not possibly change between 269 * any such locations 270 */ 271 export function isSimpleInlineableExpression(expression: Expression) { 272 return !isIdentifier(expression) && isSimpleCopiableExpression(expression); 273 } 274 275 export function isCompoundAssignment(kind: BinaryOperator): kind is CompoundAssignmentOperator { 276 return kind >= SyntaxKind.FirstCompoundAssignment 277 && kind <= SyntaxKind.LastCompoundAssignment; 278 } 279 280 export function getNonAssignmentOperatorForCompoundAssignment(kind: CompoundAssignmentOperator): LogicalOperatorOrHigher | SyntaxKind.QuestionQuestionToken { 281 switch (kind) { 282 case SyntaxKind.PlusEqualsToken: return SyntaxKind.PlusToken; 283 case SyntaxKind.MinusEqualsToken: return SyntaxKind.MinusToken; 284 case SyntaxKind.AsteriskEqualsToken: return SyntaxKind.AsteriskToken; 285 case SyntaxKind.AsteriskAsteriskEqualsToken: return SyntaxKind.AsteriskAsteriskToken; 286 case SyntaxKind.SlashEqualsToken: return SyntaxKind.SlashToken; 287 case SyntaxKind.PercentEqualsToken: return SyntaxKind.PercentToken; 288 case SyntaxKind.LessThanLessThanEqualsToken: return SyntaxKind.LessThanLessThanToken; 289 case SyntaxKind.GreaterThanGreaterThanEqualsToken: return SyntaxKind.GreaterThanGreaterThanToken; 290 case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: return SyntaxKind.GreaterThanGreaterThanGreaterThanToken; 291 case SyntaxKind.AmpersandEqualsToken: return SyntaxKind.AmpersandToken; 292 case SyntaxKind.BarEqualsToken: return SyntaxKind.BarToken; 293 case SyntaxKind.CaretEqualsToken: return SyntaxKind.CaretToken; 294 case SyntaxKind.BarBarEqualsToken: return SyntaxKind.BarBarToken; 295 case SyntaxKind.AmpersandAmpersandEqualsToken: return SyntaxKind.AmpersandAmpersandToken; 296 case SyntaxKind.QuestionQuestionEqualsToken: return SyntaxKind.QuestionQuestionToken; 297 298 } 299 } 300 301 /** 302 * @returns Contained super() call from descending into the statement ignoring parentheses, if that call exists. 303 */ 304 export function getSuperCallFromStatement(statement: Statement) { 305 if (!isExpressionStatement(statement)) { 306 return undefined; 307 } 308 309 const expression = skipParentheses(statement.expression); 310 return isSuperCall(expression) 311 ? expression 312 : undefined; 313 } 314 315 /** 316 * @returns The index (after prologue statements) of a super call, or -1 if not found. 317 */ 318 export function findSuperStatementIndex(statements: NodeArray<Statement>, indexAfterLastPrologueStatement: number) { 319 for (let i = indexAfterLastPrologueStatement; i < statements.length; i += 1) { 320 const statement = statements[i]; 321 322 if (getSuperCallFromStatement(statement)) { 323 return i; 324 } 325 } 326 327 return -1; 328 } 329 330 /** 331 * Gets all the static or all the instance property declarations of a class 332 * 333 * @param node The class node. 334 * @param isStatic A value indicating whether to get properties from the static or instance side of the class. 335 */ 336 export function getProperties(node: ClassExpression | ClassDeclaration | StructDeclaration, requireInitializer: true, isStatic: boolean): readonly InitializedPropertyDeclaration[]; 337 export function getProperties(node: ClassExpression | ClassDeclaration | StructDeclaration, requireInitializer: boolean, isStatic: boolean): readonly PropertyDeclaration[]; 338 export function getProperties(node: ClassExpression | ClassDeclaration | StructDeclaration, requireInitializer: boolean, isStatic: boolean): readonly PropertyDeclaration[] { 339 return filter(node.members, m => isInitializedOrStaticProperty(m, requireInitializer, isStatic)) as PropertyDeclaration[]; 340 } 341 342 function isStaticPropertyDeclarationOrClassStaticBlockDeclaration(element: ClassElement): element is PropertyDeclaration | ClassStaticBlockDeclaration { 343 return isStaticPropertyDeclaration(element) || isClassStaticBlockDeclaration(element); 344 } 345 346 export function getStaticPropertiesAndClassStaticBlock(node: ClassExpression | ClassDeclaration): readonly (PropertyDeclaration | ClassStaticBlockDeclaration)[]; 347 export function getStaticPropertiesAndClassStaticBlock(node: ClassExpression | ClassDeclaration): readonly (PropertyDeclaration | ClassStaticBlockDeclaration)[]; 348 export function getStaticPropertiesAndClassStaticBlock(node: ClassExpression | ClassDeclaration): readonly (PropertyDeclaration | ClassStaticBlockDeclaration)[] { 349 return filter(node.members, isStaticPropertyDeclarationOrClassStaticBlockDeclaration); 350 } 351 352 /** 353 * Is a class element either a static or an instance property declaration with an initializer? 354 * 355 * @param member The class element node. 356 * @param isStatic A value indicating whether the member should be a static or instance member. 357 */ 358 function isInitializedOrStaticProperty(member: ClassElement, requireInitializer: boolean, isStatic: boolean) { 359 return isPropertyDeclaration(member) 360 && (!!member.initializer || !requireInitializer) 361 && hasStaticModifier(member) === isStatic; 362 } 363 364 function isStaticPropertyDeclaration(member: ClassElement) { 365 return isPropertyDeclaration(member) && hasStaticModifier(member); 366 } 367 368 /** 369 * Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer. 370 * 371 * @param member The class element node. 372 * @param isStatic A value indicating whether the member should be a static or instance member. 373 */ 374 export function isInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } { 375 return member.kind === SyntaxKind.PropertyDeclaration 376 && (member as PropertyDeclaration).initializer !== undefined; 377 } 378 379 /** 380 * Gets a value indicating whether a class element is a private instance method or accessor. 381 * 382 * @param member The class element node. 383 */ 384 export function isNonStaticMethodOrAccessorWithPrivateName(member: ClassElement): member is PrivateIdentifierMethodDeclaration | PrivateIdentifierAccessorDeclaration | PrivateIdentifierAutoAccessorPropertyDeclaration { 385 return !isStatic(member) && (isMethodOrAccessor(member) || isAutoAccessorPropertyDeclaration(member)) && isPrivateIdentifier(member.name); 386 } 387 388 /** 389 * Gets an array of arrays of decorators for the parameters of a function-like node. 390 * The offset into the result array should correspond to the offset of the parameter. 391 * 392 * @param node The function-like node. 393 */ 394 function getDecoratorsOfParameters(node: FunctionLikeDeclaration | undefined) { 395 let decorators: (readonly Decorator[] | undefined)[] | undefined; 396 if (node) { 397 const parameters = node.parameters; 398 const firstParameterIsThis = parameters.length > 0 && parameterIsThisKeyword(parameters[0]); 399 const firstParameterOffset = firstParameterIsThis ? 1 : 0; 400 const numParameters = firstParameterIsThis ? parameters.length - 1 : parameters.length; 401 for (let i = 0; i < numParameters; i++) { 402 const parameter = parameters[i + firstParameterOffset]; 403 if (decorators || hasDecorators(parameter)) { 404 if (!decorators) { 405 decorators = new Array(numParameters); 406 } 407 408 decorators[i] = getDecorators(parameter); 409 } 410 } 411 } 412 413 return decorators; 414 } 415 416 /** 417 * Gets an AllDecorators object containing the decorators for the class and the decorators for the 418 * parameters of the constructor of the class. 419 * 420 * @param node The class node. 421 */ 422 export function getAllDecoratorsOfClass(node: ClassLikeDeclaration): AllDecorators | undefined { 423 const decorators = getDecorators(node); 424 const parameters = getDecoratorsOfParameters(getFirstConstructorWithBody(node)); 425 if (!some(decorators) && !some(parameters)) { 426 return undefined; 427 } 428 429 return { 430 decorators, 431 parameters 432 }; 433 } 434 435 /** 436 * Gets an AllDecorators object containing the decorators for the member and its parameters. 437 * 438 * @param parent The class node that contains the member. 439 * @param member The class member. 440 */ 441 export function getAllDecoratorsOfClassElement(member: ClassElement, parent: ClassLikeDeclaration): AllDecorators | undefined { 442 switch (member.kind) { 443 case SyntaxKind.GetAccessor: 444 case SyntaxKind.SetAccessor: 445 return getAllDecoratorsOfAccessors(member as AccessorDeclaration, parent); 446 447 case SyntaxKind.MethodDeclaration: 448 return getAllDecoratorsOfMethod(member as MethodDeclaration); 449 450 case SyntaxKind.PropertyDeclaration: 451 return getAllDecoratorsOfProperty(member as PropertyDeclaration); 452 453 default: 454 return undefined; 455 } 456 } 457 458 /** 459 * Gets an AllDecorators object containing the decorators for the accessor and its parameters. 460 * 461 * @param parent The class node that contains the accessor. 462 * @param accessor The class accessor member. 463 */ 464 function getAllDecoratorsOfAccessors(accessor: AccessorDeclaration, parent: ClassExpression | ClassDeclaration | StructDeclaration): AllDecorators | undefined { 465 if (!accessor.body) { 466 return undefined; 467 } 468 469 const { firstAccessor, secondAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(parent.members, accessor); 470 const firstAccessorWithDecorators = 471 hasDecorators(firstAccessor) ? firstAccessor : 472 secondAccessor && hasDecorators(secondAccessor) ? secondAccessor : 473 undefined; 474 475 if (!firstAccessorWithDecorators || accessor !== firstAccessorWithDecorators) { 476 return undefined; 477 } 478 479 const decorators = getDecorators(firstAccessorWithDecorators); 480 const parameters = getDecoratorsOfParameters(setAccessor); 481 if (!some(decorators) && !some(parameters)) { 482 return undefined; 483 } 484 485 return { 486 decorators, 487 parameters, 488 getDecorators: getAccessor && getDecorators(getAccessor), 489 setDecorators: setAccessor && getDecorators(setAccessor) 490 }; 491 } 492 493 /** 494 * Gets an AllDecorators object containing the decorators for the method and its parameters. 495 * 496 * @param method The class method member. 497 */ 498 function getAllDecoratorsOfMethod(method: MethodDeclaration): AllDecorators | undefined { 499 if (!method.body) { 500 return undefined; 501 } 502 503 const decorators = getDecorators(method); 504 const parameters = getDecoratorsOfParameters(method); 505 if (!some(decorators) && !some(parameters)) { 506 return undefined; 507 } 508 509 return { decorators, parameters }; 510 } 511 512 /** 513 * Gets an AllDecorators object containing the decorators for the property. 514 * 515 * @param property The class property member. 516 */ 517 function getAllDecoratorsOfProperty(property: PropertyDeclaration): AllDecorators | undefined { 518 const decorators = getDecorators(property); 519 if (!some(decorators)) { 520 return undefined; 521 522 } 523 524 return { decorators }; 525 } 526 527} 528