UNPKG

39.9 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3// See LICENSE in the project root for license information.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 var desc = Object.getOwnPropertyDescriptor(m, k);
7 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8 desc = { enumerable: true, get: function() { return m[k]; } };
9 }
10 Object.defineProperty(o, k2, desc);
11}) : (function(o, m, k, k2) {
12 if (k2 === undefined) k2 = k;
13 o[k2] = m[k];
14}));
15var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16 Object.defineProperty(o, "default", { enumerable: true, value: v });
17}) : function(o, v) {
18 o["default"] = v;
19});
20var __importStar = (this && this.__importStar) || function (mod) {
21 if (mod && mod.__esModule) return mod;
22 var result = {};
23 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 __setModuleDefault(result, mod);
25 return result;
26};
27Object.defineProperty(exports, "__esModule", { value: true });
28exports.ExportAnalyzer = void 0;
29const ts = __importStar(require("typescript"));
30const node_core_library_1 = require("@rushstack/node-core-library");
31const TypeScriptHelpers_1 = require("./TypeScriptHelpers");
32const AstSymbol_1 = require("./AstSymbol");
33const AstImport_1 = require("./AstImport");
34const AstModule_1 = require("./AstModule");
35const TypeScriptInternals_1 = require("./TypeScriptInternals");
36const SourceFileLocationFormatter_1 = require("./SourceFileLocationFormatter");
37const AstNamespaceImport_1 = require("./AstNamespaceImport");
38const SyntaxHelpers_1 = require("./SyntaxHelpers");
39/**
40 * The ExportAnalyzer is an internal part of AstSymbolTable that has been moved out into its own source file
41 * because it is a complex and mostly self-contained algorithm.
42 *
43 * Its job is to build up AstModule objects by crawling import statements to discover where declarations come from.
44 * This is conceptually the same as the compiler's own TypeChecker.getExportsOfModule(), except that when
45 * ExportAnalyzer encounters a declaration that was imported from an external package, it remembers how it was imported
46 * (i.e. the AstImport object). Today the compiler API does not expose this information, which is crucial for
47 * generating .d.ts rollups.
48 */
49class ExportAnalyzer {
50 constructor(program, typeChecker, bundledPackageNames, astSymbolTable) {
51 this._astModulesByModuleSymbol = new Map();
52 // Used with isImportableAmbientSourceFile()
53 this._importableAmbientSourceFiles = new Set();
54 this._astImportsByKey = new Map();
55 this._astNamespaceImportByModule = new Map();
56 this._program = program;
57 this._typeChecker = typeChecker;
58 this._bundledPackageNames = bundledPackageNames;
59 this._astSymbolTable = astSymbolTable;
60 }
61 /**
62 * For a given source file, this analyzes all of its exports and produces an AstModule object.
63 *
64 * @param moduleReference - contextual information about the import statement that took us to this source file.
65 * or `undefined` if this source file is the initial entry point
66 * @param isExternal - whether the given `moduleReference` is external.
67 */
68 fetchAstModuleFromSourceFile(sourceFile, moduleReference, isExternal) {
69 const moduleSymbol = this._getModuleSymbolFromSourceFile(sourceFile, moduleReference);
70 // Don't traverse into a module that we already processed before:
71 // The compiler allows m1 to have "export * from 'm2'" and "export * from 'm3'",
72 // even if m2 and m3 both have "export * from 'm4'".
73 let astModule = this._astModulesByModuleSymbol.get(moduleSymbol);
74 if (!astModule) {
75 // (If moduleReference === undefined, then this is the entry point of the local project being analyzed.)
76 const externalModulePath = moduleReference !== undefined && isExternal ? moduleReference.moduleSpecifier : undefined;
77 astModule = new AstModule_1.AstModule({ sourceFile, moduleSymbol, externalModulePath });
78 this._astModulesByModuleSymbol.set(moduleSymbol, astModule);
79 if (astModule.isExternal) {
80 // It's an external package, so do the special simplified analysis that doesn't crawl into referenced modules
81 for (const exportedSymbol of this._typeChecker.getExportsOfModule(moduleSymbol)) {
82 if (externalModulePath === undefined) {
83 throw new node_core_library_1.InternalError('Failed assertion: externalModulePath=undefined but astModule.isExternal=true');
84 }
85 const followedSymbol = TypeScriptHelpers_1.TypeScriptHelpers.followAliases(exportedSymbol, this._typeChecker);
86 // Ignore virtual symbols that don't have any declarations
87 const arbitraryDeclaration = TypeScriptHelpers_1.TypeScriptHelpers.tryGetADeclaration(followedSymbol);
88 if (arbitraryDeclaration) {
89 const astSymbol = this._astSymbolTable.fetchAstSymbol({
90 followedSymbol: followedSymbol,
91 isExternal: astModule.isExternal,
92 includeNominalAnalysis: true,
93 addIfMissing: true
94 });
95 if (!astSymbol) {
96 throw new Error(`Unsupported export ${JSON.stringify(exportedSymbol.name)}:\n` +
97 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(arbitraryDeclaration));
98 }
99 astModule.cachedExportedEntities.set(exportedSymbol.name, astSymbol);
100 }
101 }
102 }
103 else {
104 // The module is part of the local project, so do the full analysis
105 if (moduleSymbol.exports) {
106 // The "export * from 'module-name';" declarations are all attached to a single virtual symbol
107 // whose name is InternalSymbolName.ExportStar
108 const exportStarSymbol = moduleSymbol.exports.get(ts.InternalSymbolName.ExportStar);
109 if (exportStarSymbol) {
110 for (const exportStarDeclaration of exportStarSymbol.getDeclarations() || []) {
111 if (ts.isExportDeclaration(exportStarDeclaration)) {
112 const starExportedModule = this._fetchSpecifierAstModule(exportStarDeclaration, exportStarSymbol);
113 if (starExportedModule !== undefined) {
114 astModule.starExportedModules.add(starExportedModule);
115 }
116 }
117 else {
118 // Ignore ExportDeclaration nodes that don't match the expected pattern
119 // TODO: Should we report a warning?
120 }
121 }
122 }
123 }
124 }
125 }
126 return astModule;
127 }
128 /**
129 * Retrieves the symbol for the module corresponding to the ts.SourceFile that is being imported/exported.
130 *
131 * @remarks
132 * The `module` keyword can be used to declare multiple TypeScript modules inside a single source file.
133 * (This is a deprecated construct and mainly used for typings such as `@types/node`.) In this situation,
134 * `moduleReference` helps us to fish out the correct module symbol.
135 */
136 _getModuleSymbolFromSourceFile(sourceFile, moduleReference) {
137 const moduleSymbol = TypeScriptInternals_1.TypeScriptInternals.tryGetSymbolForDeclaration(sourceFile, this._typeChecker);
138 if (moduleSymbol !== undefined) {
139 // This is the normal case. The SourceFile acts is a module and has a symbol.
140 return moduleSymbol;
141 }
142 if (moduleReference !== undefined) {
143 // But there is also an elaborate case where the source file contains one or more "module" declarations,
144 // and our moduleReference took us to one of those.
145 // eslint-disable-next-line no-bitwise
146 if ((moduleReference.moduleSpecifierSymbol.flags & ts.SymbolFlags.Alias) !== 0) {
147 // Follow the import/export declaration to one hop the exported item inside the target module
148 let followedSymbol = TypeScriptInternals_1.TypeScriptInternals.getImmediateAliasedSymbol(moduleReference.moduleSpecifierSymbol, this._typeChecker);
149 if (followedSymbol === undefined) {
150 // This is a workaround for a compiler bug where getImmediateAliasedSymbol() sometimes returns undefined
151 followedSymbol = this._typeChecker.getAliasedSymbol(moduleReference.moduleSpecifierSymbol);
152 }
153 if (followedSymbol !== undefined && followedSymbol !== moduleReference.moduleSpecifierSymbol) {
154 // The parent of the exported symbol will be the module that we're importing from
155 const parent = TypeScriptInternals_1.TypeScriptInternals.getSymbolParent(followedSymbol);
156 if (parent !== undefined) {
157 // Make sure the thing we found is a module
158 // eslint-disable-next-line no-bitwise
159 if ((parent.flags & ts.SymbolFlags.ValueModule) !== 0) {
160 // Record that that this is an ambient module that can also be imported from
161 this._importableAmbientSourceFiles.add(sourceFile);
162 return parent;
163 }
164 }
165 }
166 }
167 }
168 throw new node_core_library_1.InternalError('Unable to determine module for: ' + sourceFile.fileName);
169 }
170 /**
171 * Implementation of {@link AstSymbolTable.fetchAstModuleExportInfo}.
172 */
173 fetchAstModuleExportInfo(entryPointAstModule) {
174 if (entryPointAstModule.isExternal) {
175 throw new Error('fetchAstModuleExportInfo() is not supported for external modules');
176 }
177 if (entryPointAstModule.astModuleExportInfo === undefined) {
178 const astModuleExportInfo = new AstModule_1.AstModuleExportInfo();
179 this._collectAllExportsRecursive(astModuleExportInfo, entryPointAstModule, new Set());
180 entryPointAstModule.astModuleExportInfo = astModuleExportInfo;
181 }
182 return entryPointAstModule.astModuleExportInfo;
183 }
184 /**
185 * Returns true if the module specifier refers to an external package. Ignores packages listed in the
186 * "bundledPackages" setting from the api-extractor.json config file.
187 */
188 _isExternalModulePath(importOrExportDeclaration, moduleSpecifier) {
189 var _a;
190 const specifier = ts.isImportTypeNode(importOrExportDeclaration)
191 ? importOrExportDeclaration.argument
192 : importOrExportDeclaration.moduleSpecifier;
193 const mode = specifier && ts.isStringLiteralLike(specifier)
194 ? TypeScriptInternals_1.TypeScriptInternals.getModeForUsageLocation(importOrExportDeclaration.getSourceFile(), specifier)
195 : undefined;
196 const resolvedModule = TypeScriptInternals_1.TypeScriptInternals.getResolvedModule(importOrExportDeclaration.getSourceFile(), moduleSpecifier, mode);
197 if (resolvedModule === undefined) {
198 // The TS compiler API `getResolvedModule` cannot resolve ambient modules. Thus, to match API Extractor's
199 // previous behavior, simply treat all ambient modules as external. This bug is tracked by
200 // https://github.com/microsoft/rushstack/issues/3335.
201 return true;
202 }
203 // Either something like `jquery` or `@microsoft/api-extractor`.
204 const packageName = (_a = resolvedModule.packageId) === null || _a === void 0 ? void 0 : _a.name;
205 if (packageName !== undefined && this._bundledPackageNames.has(packageName)) {
206 return false;
207 }
208 if (resolvedModule.isExternalLibraryImport === undefined) {
209 // This presumably means the compiler couldn't figure out whether the module was external, but we're not
210 // sure how this can happen.
211 throw new node_core_library_1.InternalError(`Cannot determine whether the module ${JSON.stringify(moduleSpecifier)} is external\n` +
212 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration));
213 }
214 return resolvedModule.isExternalLibraryImport;
215 }
216 /**
217 * Returns true if when we analyzed sourceFile, we found that it contains an "export=" statement that allows
218 * it to behave /either/ as an ambient module /or/ as a regular importable module. In this case,
219 * `AstSymbolTable._fetchAstSymbol()` will analyze its symbols even though `TypeScriptHelpers.isAmbient()`
220 * returns true.
221 */
222 isImportableAmbientSourceFile(sourceFile) {
223 return this._importableAmbientSourceFiles.has(sourceFile);
224 }
225 _collectAllExportsRecursive(astModuleExportInfo, astModule, visitedAstModules) {
226 if (visitedAstModules.has(astModule)) {
227 return;
228 }
229 visitedAstModules.add(astModule);
230 if (astModule.isExternal) {
231 astModuleExportInfo.starExportedExternalModules.add(astModule);
232 }
233 else {
234 // Fetch each of the explicit exports for this module
235 if (astModule.moduleSymbol.exports) {
236 astModule.moduleSymbol.exports.forEach((exportSymbol, exportName) => {
237 switch (exportName) {
238 case ts.InternalSymbolName.ExportStar:
239 case ts.InternalSymbolName.ExportEquals:
240 break;
241 default:
242 // Don't collect the "export default" symbol unless this is the entry point module
243 if (exportName !== ts.InternalSymbolName.Default || visitedAstModules.size === 1) {
244 if (!astModuleExportInfo.exportedLocalEntities.has(exportSymbol.name)) {
245 const astEntity = this._getExportOfAstModule(exportSymbol.name, astModule);
246 if (astEntity instanceof AstSymbol_1.AstSymbol && !astEntity.isExternal) {
247 this._astSymbolTable.analyze(astEntity);
248 }
249 if (astEntity instanceof AstNamespaceImport_1.AstNamespaceImport && !astEntity.astModule.isExternal) {
250 this._astSymbolTable.analyze(astEntity);
251 }
252 astModuleExportInfo.exportedLocalEntities.set(exportSymbol.name, astEntity);
253 }
254 }
255 break;
256 }
257 });
258 }
259 for (const starExportedModule of astModule.starExportedModules) {
260 this._collectAllExportsRecursive(astModuleExportInfo, starExportedModule, visitedAstModules);
261 }
262 }
263 }
264 /**
265 * For a given symbol (which was encountered in the specified sourceFile), this fetches the AstEntity that it
266 * refers to. For example, if a particular interface describes the return value of a function, this API can help
267 * us determine a TSDoc declaration reference for that symbol (if the symbol is exported).
268 */
269 fetchReferencedAstEntity(symbol, referringModuleIsExternal) {
270 // eslint-disable-next-line no-bitwise
271 if ((symbol.flags & ts.SymbolFlags.FunctionScopedVariable) !== 0) {
272 // If a symbol refers back to part of its own definition, don't follow that rabbit hole
273 // Example:
274 //
275 // function f(x: number): typeof x {
276 // return 123;
277 // }
278 return undefined;
279 }
280 let current = symbol;
281 if (referringModuleIsExternal) {
282 current = TypeScriptHelpers_1.TypeScriptHelpers.followAliases(symbol, this._typeChecker);
283 }
284 else {
285 for (;;) {
286 // Is this symbol an import/export that we need to follow to find the real declaration?
287 for (const declaration of current.declarations || []) {
288 let matchedAstEntity;
289 matchedAstEntity = this._tryMatchExportDeclaration(declaration, current);
290 if (matchedAstEntity !== undefined) {
291 return matchedAstEntity;
292 }
293 matchedAstEntity = this._tryMatchImportDeclaration(declaration, current);
294 if (matchedAstEntity !== undefined) {
295 return matchedAstEntity;
296 }
297 }
298 // eslint-disable-next-line no-bitwise
299 if (!(current.flags & ts.SymbolFlags.Alias)) {
300 break;
301 }
302 const currentAlias = TypeScriptInternals_1.TypeScriptInternals.getImmediateAliasedSymbol(current, this._typeChecker);
303 // Stop if we reach the end of the chain
304 if (!currentAlias || currentAlias === current) {
305 break;
306 }
307 current = currentAlias;
308 }
309 }
310 // Otherwise, assume it is a normal declaration
311 const astSymbol = this._astSymbolTable.fetchAstSymbol({
312 followedSymbol: current,
313 isExternal: referringModuleIsExternal,
314 includeNominalAnalysis: false,
315 addIfMissing: true
316 });
317 return astSymbol;
318 }
319 fetchReferencedAstEntityFromImportTypeNode(node, referringModuleIsExternal) {
320 const externalModulePath = this._tryGetExternalModulePath(node);
321 if (externalModulePath) {
322 let exportName;
323 if (node.qualifier) {
324 // Example input:
325 // import('api-extractor-lib1-test').Lib1GenericType<number>
326 //
327 // Extracted qualifier:
328 // Lib1GenericType
329 exportName = node.qualifier.getText().trim();
330 }
331 else {
332 // Example input:
333 // import('api-extractor-lib1-test')
334 //
335 // Extracted qualifier:
336 // apiExtractorLib1Test
337 exportName = SyntaxHelpers_1.SyntaxHelpers.makeCamelCaseIdentifier(externalModulePath);
338 }
339 return this._fetchAstImport(undefined, {
340 importKind: AstImport_1.AstImportKind.ImportType,
341 exportName: exportName,
342 modulePath: externalModulePath,
343 isTypeOnly: false
344 });
345 }
346 // Internal reference: AstSymbol
347 const rightMostToken = node.qualifier
348 ? node.qualifier.kind === ts.SyntaxKind.QualifiedName
349 ? node.qualifier.right
350 : node.qualifier
351 : node;
352 // There is no symbol property in a ImportTypeNode, obtain the associated export symbol
353 const exportSymbol = this._typeChecker.getSymbolAtLocation(rightMostToken);
354 if (!exportSymbol) {
355 throw new node_core_library_1.InternalError(`Symbol not found for identifier: ${node.getText()}\n` +
356 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(node));
357 }
358 let followedSymbol = exportSymbol;
359 for (;;) {
360 const referencedAstEntity = this.fetchReferencedAstEntity(followedSymbol, referringModuleIsExternal);
361 if (referencedAstEntity) {
362 return referencedAstEntity;
363 }
364 const followedSymbolNode = followedSymbol.declarations && followedSymbol.declarations[0];
365 if (followedSymbolNode && followedSymbolNode.kind === ts.SyntaxKind.ImportType) {
366 return this.fetchReferencedAstEntityFromImportTypeNode(followedSymbolNode, referringModuleIsExternal);
367 }
368 // eslint-disable-next-line no-bitwise
369 if (!(followedSymbol.flags & ts.SymbolFlags.Alias)) {
370 break;
371 }
372 const currentAlias = this._typeChecker.getAliasedSymbol(followedSymbol);
373 if (!currentAlias || currentAlias === followedSymbol) {
374 break;
375 }
376 followedSymbol = currentAlias;
377 }
378 const astSymbol = this._astSymbolTable.fetchAstSymbol({
379 followedSymbol: followedSymbol,
380 isExternal: referringModuleIsExternal,
381 includeNominalAnalysis: false,
382 addIfMissing: true
383 });
384 return astSymbol;
385 }
386 _tryMatchExportDeclaration(declaration, declarationSymbol) {
387 const exportDeclaration = TypeScriptHelpers_1.TypeScriptHelpers.findFirstParent(declaration, ts.SyntaxKind.ExportDeclaration);
388 if (exportDeclaration) {
389 let exportName = undefined;
390 if (declaration.kind === ts.SyntaxKind.ExportSpecifier) {
391 // EXAMPLE:
392 // "export { A } from './file-a';"
393 //
394 // ExportDeclaration:
395 // ExportKeyword: pre=[export] sep=[ ]
396 // NamedExports:
397 // FirstPunctuation: pre=[{] sep=[ ]
398 // SyntaxList:
399 // ExportSpecifier: <------------- declaration
400 // Identifier: pre=[A] sep=[ ]
401 // CloseBraceToken: pre=[}] sep=[ ]
402 // FromKeyword: pre=[from] sep=[ ]
403 // StringLiteral: pre=['./file-a']
404 // SemicolonToken: pre=[;]
405 // Example: " ExportName as RenamedName"
406 const exportSpecifier = declaration;
407 exportName = (exportSpecifier.propertyName || exportSpecifier.name).getText().trim();
408 }
409 else if (declaration.kind === ts.SyntaxKind.NamespaceExport) {
410 // EXAMPLE:
411 // "export * as theLib from 'the-lib';"
412 //
413 // ExportDeclaration:
414 // ExportKeyword: pre=[export] sep=[ ]
415 // NamespaceExport:
416 // AsteriskToken: pre=[*] sep=[ ]
417 // AsKeyword: pre=[as] sep=[ ]
418 // Identifier: pre=[theLib] sep=[ ]
419 // FromKeyword: pre=[from] sep=[ ]
420 // StringLiteral: pre=['the-lib']
421 // SemicolonToken: pre=[;]
422 // Issue tracking this feature: https://github.com/microsoft/rushstack/issues/2780
423 throw new Error(`The "export * as ___" syntax is not supported yet; as a workaround,` +
424 ` use "import * as ___" with a separate "export { ___ }" declaration\n` +
425 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(declaration));
426 }
427 else {
428 throw new node_core_library_1.InternalError(`Unimplemented export declaration kind: ${declaration.getText()}\n` +
429 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(declaration));
430 }
431 // Ignore "export { A }" without a module specifier
432 if (exportDeclaration.moduleSpecifier) {
433 const externalModulePath = this._tryGetExternalModulePath(exportDeclaration);
434 if (externalModulePath !== undefined) {
435 return this._fetchAstImport(declarationSymbol, {
436 importKind: AstImport_1.AstImportKind.NamedImport,
437 modulePath: externalModulePath,
438 exportName: exportName,
439 isTypeOnly: false
440 });
441 }
442 return this._getExportOfSpecifierAstModule(exportName, exportDeclaration, declarationSymbol);
443 }
444 }
445 return undefined;
446 }
447 _tryMatchImportDeclaration(declaration, declarationSymbol) {
448 const importDeclaration = TypeScriptHelpers_1.TypeScriptHelpers.findFirstParent(declaration, ts.SyntaxKind.ImportDeclaration);
449 if (importDeclaration) {
450 const externalModulePath = this._tryGetExternalModulePath(importDeclaration);
451 if (declaration.kind === ts.SyntaxKind.NamespaceImport) {
452 // EXAMPLE:
453 // "import * as theLib from 'the-lib';"
454 //
455 // ImportDeclaration:
456 // ImportKeyword: pre=[import] sep=[ ]
457 // ImportClause:
458 // NamespaceImport: <------------- declaration
459 // AsteriskToken: pre=[*] sep=[ ]
460 // AsKeyword: pre=[as] sep=[ ]
461 // Identifier: pre=[theLib] sep=[ ]
462 // FromKeyword: pre=[from] sep=[ ]
463 // StringLiteral: pre=['the-lib']
464 // SemicolonToken: pre=[;]
465 if (externalModulePath === undefined) {
466 const astModule = this._fetchSpecifierAstModule(importDeclaration, declarationSymbol);
467 let namespaceImport = this._astNamespaceImportByModule.get(astModule);
468 if (namespaceImport === undefined) {
469 namespaceImport = new AstNamespaceImport_1.AstNamespaceImport({
470 namespaceName: declarationSymbol.name,
471 astModule: astModule,
472 declaration: declaration
473 });
474 this._astNamespaceImportByModule.set(astModule, namespaceImport);
475 }
476 return namespaceImport;
477 }
478 // Here importSymbol=undefined because {@inheritDoc} and such are not going to work correctly for
479 // a package or source file.
480 return this._fetchAstImport(undefined, {
481 importKind: AstImport_1.AstImportKind.StarImport,
482 exportName: declarationSymbol.name,
483 modulePath: externalModulePath,
484 isTypeOnly: ExportAnalyzer._getIsTypeOnly(importDeclaration)
485 });
486 }
487 if (declaration.kind === ts.SyntaxKind.ImportSpecifier) {
488 // EXAMPLE:
489 // "import { A, B } from 'the-lib';"
490 //
491 // ImportDeclaration:
492 // ImportKeyword: pre=[import] sep=[ ]
493 // ImportClause:
494 // NamedImports:
495 // FirstPunctuation: pre=[{] sep=[ ]
496 // SyntaxList:
497 // ImportSpecifier: <------------- declaration
498 // Identifier: pre=[A]
499 // CommaToken: pre=[,] sep=[ ]
500 // ImportSpecifier:
501 // Identifier: pre=[B] sep=[ ]
502 // CloseBraceToken: pre=[}] sep=[ ]
503 // FromKeyword: pre=[from] sep=[ ]
504 // StringLiteral: pre=['the-lib']
505 // SemicolonToken: pre=[;]
506 // Example: " ExportName as RenamedName"
507 const importSpecifier = declaration;
508 const exportName = (importSpecifier.propertyName || importSpecifier.name).getText().trim();
509 if (externalModulePath !== undefined) {
510 return this._fetchAstImport(declarationSymbol, {
511 importKind: AstImport_1.AstImportKind.NamedImport,
512 modulePath: externalModulePath,
513 exportName: exportName,
514 isTypeOnly: ExportAnalyzer._getIsTypeOnly(importDeclaration)
515 });
516 }
517 return this._getExportOfSpecifierAstModule(exportName, importDeclaration, declarationSymbol);
518 }
519 else if (declaration.kind === ts.SyntaxKind.ImportClause) {
520 // EXAMPLE:
521 // "import A, { B } from './A';"
522 //
523 // ImportDeclaration:
524 // ImportKeyword: pre=[import] sep=[ ]
525 // ImportClause: <------------- declaration (referring to A)
526 // Identifier: pre=[A]
527 // CommaToken: pre=[,] sep=[ ]
528 // NamedImports:
529 // FirstPunctuation: pre=[{] sep=[ ]
530 // SyntaxList:
531 // ImportSpecifier:
532 // Identifier: pre=[B] sep=[ ]
533 // CloseBraceToken: pre=[}] sep=[ ]
534 // FromKeyword: pre=[from] sep=[ ]
535 // StringLiteral: pre=['./A']
536 // SemicolonToken: pre=[;]
537 const importClause = declaration;
538 const exportName = importClause.name
539 ? importClause.name.getText().trim()
540 : ts.InternalSymbolName.Default;
541 if (externalModulePath !== undefined) {
542 return this._fetchAstImport(declarationSymbol, {
543 importKind: AstImport_1.AstImportKind.DefaultImport,
544 modulePath: externalModulePath,
545 exportName,
546 isTypeOnly: ExportAnalyzer._getIsTypeOnly(importDeclaration)
547 });
548 }
549 return this._getExportOfSpecifierAstModule(ts.InternalSymbolName.Default, importDeclaration, declarationSymbol);
550 }
551 else {
552 throw new node_core_library_1.InternalError(`Unimplemented import declaration kind: ${declaration.getText()}\n` +
553 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(declaration));
554 }
555 }
556 if (ts.isImportEqualsDeclaration(declaration)) {
557 // EXAMPLE:
558 // import myLib = require('my-lib');
559 //
560 // ImportEqualsDeclaration:
561 // ImportKeyword: pre=[import] sep=[ ]
562 // Identifier: pre=[myLib] sep=[ ]
563 // FirstAssignment: pre=[=] sep=[ ]
564 // ExternalModuleReference:
565 // RequireKeyword: pre=[require]
566 // OpenParenToken: pre=[(]
567 // StringLiteral: pre=['my-lib']
568 // CloseParenToken: pre=[)]
569 // SemicolonToken: pre=[;]
570 if (ts.isExternalModuleReference(declaration.moduleReference)) {
571 if (ts.isStringLiteralLike(declaration.moduleReference.expression)) {
572 const variableName = TypeScriptInternals_1.TypeScriptInternals.getTextOfIdentifierOrLiteral(declaration.name);
573 const externalModuleName = TypeScriptInternals_1.TypeScriptInternals.getTextOfIdentifierOrLiteral(declaration.moduleReference.expression);
574 return this._fetchAstImport(declarationSymbol, {
575 importKind: AstImport_1.AstImportKind.EqualsImport,
576 modulePath: externalModuleName,
577 exportName: variableName,
578 isTypeOnly: false
579 });
580 }
581 }
582 }
583 return undefined;
584 }
585 static _getIsTypeOnly(importDeclaration) {
586 if (importDeclaration.importClause) {
587 return !!importDeclaration.importClause.isTypeOnly;
588 }
589 return false;
590 }
591 _getExportOfSpecifierAstModule(exportName, importOrExportDeclaration, exportSymbol) {
592 const specifierAstModule = this._fetchSpecifierAstModule(importOrExportDeclaration, exportSymbol);
593 const astEntity = this._getExportOfAstModule(exportName, specifierAstModule);
594 return astEntity;
595 }
596 _getExportOfAstModule(exportName, astModule) {
597 const visitedAstModules = new Set();
598 const astEntity = this._tryGetExportOfAstModule(exportName, astModule, visitedAstModules);
599 if (astEntity === undefined) {
600 throw new node_core_library_1.InternalError(`Unable to analyze the export ${JSON.stringify(exportName)} in\n` + astModule.sourceFile.fileName);
601 }
602 return astEntity;
603 }
604 /**
605 * Implementation of {@link AstSymbolTable.tryGetExportOfAstModule}.
606 */
607 tryGetExportOfAstModule(exportName, astModule) {
608 const visitedAstModules = new Set();
609 return this._tryGetExportOfAstModule(exportName, astModule, visitedAstModules);
610 }
611 _tryGetExportOfAstModule(exportName, astModule, visitedAstModules) {
612 if (visitedAstModules.has(astModule)) {
613 return undefined;
614 }
615 visitedAstModules.add(astModule);
616 let astEntity = astModule.cachedExportedEntities.get(exportName);
617 if (astEntity !== undefined) {
618 return astEntity;
619 }
620 // Try the explicit exports
621 const escapedExportName = ts.escapeLeadingUnderscores(exportName);
622 if (astModule.moduleSymbol.exports) {
623 const exportSymbol = astModule.moduleSymbol.exports.get(escapedExportName);
624 if (exportSymbol) {
625 astEntity = this.fetchReferencedAstEntity(exportSymbol, astModule.isExternal);
626 if (astEntity !== undefined) {
627 astModule.cachedExportedEntities.set(exportName, astEntity); // cache for next time
628 return astEntity;
629 }
630 }
631 }
632 // Try each of the star imports
633 for (const starExportedModule of astModule.starExportedModules) {
634 astEntity = this._tryGetExportOfAstModule(exportName, starExportedModule, visitedAstModules);
635 if (astEntity !== undefined) {
636 if (starExportedModule.externalModulePath !== undefined) {
637 // This entity was obtained from an external module, so return an AstImport instead
638 const astSymbol = astEntity;
639 return this._fetchAstImport(astSymbol.followedSymbol, {
640 importKind: AstImport_1.AstImportKind.NamedImport,
641 modulePath: starExportedModule.externalModulePath,
642 exportName: exportName,
643 isTypeOnly: false
644 });
645 }
646 return astEntity;
647 }
648 }
649 return undefined;
650 }
651 _tryGetExternalModulePath(importOrExportDeclaration) {
652 const moduleSpecifier = this._getModuleSpecifier(importOrExportDeclaration);
653 if (this._isExternalModulePath(importOrExportDeclaration, moduleSpecifier)) {
654 return moduleSpecifier;
655 }
656 return undefined;
657 }
658 /**
659 * Given an ImportDeclaration of the form `export { X } from "___";`, this interprets the module specifier (`"___"`)
660 * and fetches the corresponding AstModule object.
661 */
662 _fetchSpecifierAstModule(importOrExportDeclaration, exportSymbol) {
663 const moduleSpecifier = this._getModuleSpecifier(importOrExportDeclaration);
664 const mode = importOrExportDeclaration.moduleSpecifier &&
665 ts.isStringLiteralLike(importOrExportDeclaration.moduleSpecifier)
666 ? TypeScriptInternals_1.TypeScriptInternals.getModeForUsageLocation(importOrExportDeclaration.getSourceFile(), importOrExportDeclaration.moduleSpecifier)
667 : undefined;
668 const resolvedModule = TypeScriptInternals_1.TypeScriptInternals.getResolvedModule(importOrExportDeclaration.getSourceFile(), moduleSpecifier, mode);
669 if (resolvedModule === undefined) {
670 // Encountered in https://github.com/microsoft/rushstack/issues/1914.
671 //
672 // It's also possible for this to occur with ambient modules. However, in practice this doesn't happen
673 // as API Extractor treats all ambient modules as external per the logic in `_isExternalModulePath`, and
674 // thus this code path is never reached for ambient modules.
675 throw new node_core_library_1.InternalError(`getResolvedModule() could not resolve module name ${JSON.stringify(moduleSpecifier)}\n` +
676 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration));
677 }
678 // Map the filename back to the corresponding SourceFile. This circuitous approach is needed because
679 // we have no way to access the compiler's internal resolveExternalModuleName() function
680 const moduleSourceFile = this._program.getSourceFile(resolvedModule.resolvedFileName);
681 if (!moduleSourceFile) {
682 // This should not happen, since getResolvedModule() specifically looks up names that the compiler
683 // found in export declarations for this source file
684 throw new node_core_library_1.InternalError(`getSourceFile() failed to locate ${JSON.stringify(resolvedModule.resolvedFileName)}\n` +
685 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration));
686 }
687 const isExternal = this._isExternalModulePath(importOrExportDeclaration, moduleSpecifier);
688 const moduleReference = {
689 moduleSpecifier: moduleSpecifier,
690 moduleSpecifierSymbol: exportSymbol
691 };
692 const specifierAstModule = this.fetchAstModuleFromSourceFile(moduleSourceFile, moduleReference, isExternal);
693 return specifierAstModule;
694 }
695 _fetchAstImport(importSymbol, options) {
696 const key = AstImport_1.AstImport.getKey(options);
697 let astImport = this._astImportsByKey.get(key);
698 if (!astImport) {
699 astImport = new AstImport_1.AstImport(options);
700 this._astImportsByKey.set(key, astImport);
701 if (importSymbol) {
702 const followedSymbol = TypeScriptHelpers_1.TypeScriptHelpers.followAliases(importSymbol, this._typeChecker);
703 astImport.astSymbol = this._astSymbolTable.fetchAstSymbol({
704 followedSymbol: followedSymbol,
705 isExternal: true,
706 includeNominalAnalysis: false,
707 addIfMissing: true
708 });
709 }
710 }
711 else {
712 // If we encounter at least one import that does not use the type-only form,
713 // then the .d.ts rollup will NOT use "import type".
714 if (!options.isTypeOnly) {
715 astImport.isTypeOnlyEverywhere = false;
716 }
717 }
718 return astImport;
719 }
720 _getModuleSpecifier(importOrExportDeclaration) {
721 // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point'
722 const moduleSpecifier = TypeScriptHelpers_1.TypeScriptHelpers.getModuleSpecifier(importOrExportDeclaration);
723 if (!moduleSpecifier) {
724 throw new node_core_library_1.InternalError('Unable to parse module specifier\n' +
725 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration));
726 }
727 return moduleSpecifier;
728 }
729}
730exports.ExportAnalyzer = ExportAnalyzer;
731//# sourceMappingURL=ExportAnalyzer.js.map
\No newline at end of file