@@ -56,14 +56,6 @@ interface IAstModuleReference {
56
56
* generating .d.ts rollups.
57
57
*/
58
58
export class ExportAnalyzer {
59
- // Captures "@a/b" or "d" from these examples:
60
- // @a /b
61
- // @a /b/c
62
- // d
63
- // d/
64
- // d/e
65
- private static _modulePathRegExp : RegExp = / ^ ( (?: @ [ ^ @ \/ \s ] + \/ ) ? [ ^ @ \/ \s ] + ) (?: .* ) $ / ;
66
-
67
59
private readonly _program : ts . Program ;
68
60
private readonly _typeChecker : ts . TypeChecker ;
69
61
private readonly _bundledPackageNames : ReadonlySet < string > ;
@@ -94,10 +86,12 @@ export class ExportAnalyzer {
94
86
*
95
87
* @param moduleReference - contextual information about the import statement that took us to this source file.
96
88
* or `undefined` if this source file is the initial entry point
89
+ * @param isExternal - whether the given `moduleReference` is external.
97
90
*/
98
91
public fetchAstModuleFromSourceFile (
99
92
sourceFile : ts . SourceFile ,
100
- moduleReference : IAstModuleReference | undefined
93
+ moduleReference : IAstModuleReference | undefined ,
94
+ isExternal : boolean
101
95
) : AstModule {
102
96
const moduleSymbol : ts . Symbol = this . _getModuleSymbolFromSourceFile ( sourceFile , moduleReference ) ;
103
97
@@ -107,14 +101,8 @@ export class ExportAnalyzer {
107
101
let astModule : AstModule | undefined = this . _astModulesByModuleSymbol . get ( moduleSymbol ) ;
108
102
if ( ! astModule ) {
109
103
// (If moduleReference === undefined, then this is the entry point of the local project being analyzed.)
110
- let externalModulePath : string | undefined = undefined ;
111
- if ( moduleReference !== undefined ) {
112
- // Match: "@microsoft/sp-lodash-subset" or "lodash/has"
113
- // but ignore: "../folder/LocalFile"
114
- if ( this . _isExternalModulePath ( moduleReference . moduleSpecifier ) ) {
115
- externalModulePath = moduleReference . moduleSpecifier ;
116
- }
117
- }
104
+ const externalModulePath : string | undefined =
105
+ moduleReference !== undefined && isExternal ? moduleReference . moduleSpecifier : undefined ;
118
106
119
107
astModule = new AstModule ( { sourceFile, moduleSymbol, externalModulePath } ) ;
120
108
@@ -266,29 +254,32 @@ export class ExportAnalyzer {
266
254
/**
267
255
* Returns true if the module specifier refers to an external package. Ignores packages listed in the
268
256
* "bundledPackages" setting from the api-extractor.json config file.
269
- *
270
- * @remarks
271
- * Examples:
272
- *
273
- * - NO: `./file1`
274
- * - YES: `library1/path/path`
275
- * - YES: `@my-scope/my-package`
276
257
*/
277
- private _isExternalModulePath ( moduleSpecifier : string ) : boolean {
278
- if ( ts . isExternalModuleNameRelative ( moduleSpecifier ) ) {
258
+ private _isExternalModulePath (
259
+ importOrExportDeclaration : ts . ImportDeclaration | ts . ExportDeclaration | ts . ImportTypeNode ,
260
+ moduleSpecifier : string
261
+ ) : boolean {
262
+ const resolvedModule : ts . ResolvedModuleFull = this . _getResolvedModule (
263
+ importOrExportDeclaration ,
264
+ moduleSpecifier
265
+ ) ;
266
+
267
+ // Either something like `jquery` or `@microsoft/api-extractor`.
268
+ const packageName : string | undefined = resolvedModule . packageId ?. name ;
269
+ if ( packageName !== undefined && this . _bundledPackageNames . has ( packageName ) ) {
279
270
return false ;
280
271
}
281
272
282
- const match : RegExpExecArray | null = ExportAnalyzer . _modulePathRegExp . exec ( moduleSpecifier ) ;
283
- if ( match ) {
284
- // Extract "@my-scope/my-package" from "@my-scope/my-package/path/module"
285
- const packageName : string = match [ 1 ] ;
286
- if ( this . _bundledPackageNames . has ( packageName ) ) {
287
- return false ;
288
- }
273
+ if ( resolvedModule . isExternalLibraryImport === undefined ) {
274
+ // This presumably means the compiler couldn't figure out whether the module was external, but we're not
275
+ // sure how this can happen.
276
+ throw new InternalError (
277
+ `Cannot determine whether the module ${ JSON . stringify ( moduleSpecifier ) } is external\n` +
278
+ SourceFileLocationFormatter . formatDeclaration ( importOrExportDeclaration )
279
+ ) ;
289
280
}
290
281
291
- return true ;
282
+ return resolvedModule . isExternalLibraryImport ;
292
283
}
293
284
294
285
/**
@@ -568,10 +559,7 @@ export class ExportAnalyzer {
568
559
569
560
// Ignore "export { A }" without a module specifier
570
561
if ( exportDeclaration . moduleSpecifier ) {
571
- const externalModulePath : string | undefined = this . _tryGetExternalModulePath (
572
- exportDeclaration ,
573
- declarationSymbol
574
- ) ;
562
+ const externalModulePath : string | undefined = this . _tryGetExternalModulePath ( exportDeclaration ) ;
575
563
576
564
if ( externalModulePath !== undefined ) {
577
565
return this . _fetchAstImport ( declarationSymbol , {
@@ -597,10 +585,7 @@ export class ExportAnalyzer {
597
585
TypeScriptHelpers . findFirstParent < ts . ImportDeclaration > ( declaration , ts . SyntaxKind . ImportDeclaration ) ;
598
586
599
587
if ( importDeclaration ) {
600
- const externalModulePath : string | undefined = this . _tryGetExternalModulePath (
601
- importDeclaration ,
602
- declarationSymbol
603
- ) ;
588
+ const externalModulePath : string | undefined = this . _tryGetExternalModulePath ( importDeclaration ) ;
604
589
605
590
if ( declaration . kind === ts . SyntaxKind . NamespaceImport ) {
606
591
// EXAMPLE:
@@ -852,22 +837,10 @@ export class ExportAnalyzer {
852
837
}
853
838
854
839
private _tryGetExternalModulePath (
855
- importOrExportDeclaration : ts . ImportDeclaration | ts . ExportDeclaration | ts . ImportTypeNode ,
856
- exportSymbol ?: ts . Symbol
840
+ importOrExportDeclaration : ts . ImportDeclaration | ts . ExportDeclaration | ts . ImportTypeNode
857
841
) : string | undefined {
858
- // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point'
859
- const moduleSpecifier : string | undefined =
860
- TypeScriptHelpers . getModuleSpecifier ( importOrExportDeclaration ) ;
861
- if ( ! moduleSpecifier ) {
862
- throw new InternalError (
863
- 'Unable to parse module specifier\n' +
864
- SourceFileLocationFormatter . formatDeclaration ( importOrExportDeclaration )
865
- ) ;
866
- }
867
-
868
- // Match: "@microsoft/sp-lodash-subset" or "lodash/has"
869
- // but ignore: "../folder/LocalFile"
870
- if ( this . _isExternalModulePath ( moduleSpecifier ) ) {
842
+ const moduleSpecifier : string = this . _getModuleSpecifier ( importOrExportDeclaration ) ;
843
+ if ( this . _isExternalModulePath ( importOrExportDeclaration , moduleSpecifier ) ) {
871
844
return moduleSpecifier ;
872
845
}
873
846
@@ -882,32 +855,12 @@ export class ExportAnalyzer {
882
855
importOrExportDeclaration : ts . ImportDeclaration | ts . ExportDeclaration ,
883
856
exportSymbol : ts . Symbol
884
857
) : AstModule {
885
- // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point'
886
- const moduleSpecifier : string | undefined =
887
- TypeScriptHelpers . getModuleSpecifier ( importOrExportDeclaration ) ;
888
- if ( ! moduleSpecifier ) {
889
- throw new InternalError (
890
- 'Unable to parse module specifier\n' +
891
- SourceFileLocationFormatter . formatDeclaration ( importOrExportDeclaration )
892
- ) ;
893
- }
894
-
895
- const resolvedModule : ts . ResolvedModuleFull | undefined = TypeScriptInternals . getResolvedModule (
896
- importOrExportDeclaration . getSourceFile ( ) ,
858
+ const moduleSpecifier : string = this . _getModuleSpecifier ( importOrExportDeclaration ) ;
859
+ const resolvedModule : ts . ResolvedModuleFull = this . _getResolvedModule (
860
+ importOrExportDeclaration ,
897
861
moduleSpecifier
898
862
) ;
899
863
900
- if ( resolvedModule === undefined ) {
901
- // This should not happen, since getResolvedModule() specifically looks up names that the compiler
902
- // found in export declarations for this source file
903
- //
904
- // Encountered in https://github.com/microsoft/rushstack/issues/1914
905
- throw new InternalError (
906
- `getResolvedModule() could not resolve module name ${ JSON . stringify ( moduleSpecifier ) } \n` +
907
- SourceFileLocationFormatter . formatDeclaration ( importOrExportDeclaration )
908
- ) ;
909
- }
910
-
911
864
// Map the filename back to the corresponding SourceFile. This circuitous approach is needed because
912
865
// we have no way to access the compiler's internal resolveExternalModuleName() function
913
866
const moduleSourceFile : ts . SourceFile | undefined = this . _program . getSourceFile (
@@ -922,13 +875,15 @@ export class ExportAnalyzer {
922
875
) ;
923
876
}
924
877
878
+ const isExternal : boolean = this . _isExternalModulePath ( importOrExportDeclaration , moduleSpecifier ) ;
925
879
const moduleReference : IAstModuleReference = {
926
880
moduleSpecifier : moduleSpecifier ,
927
881
moduleSpecifierSymbol : exportSymbol
928
882
} ;
929
883
const specifierAstModule : AstModule = this . fetchAstModuleFromSourceFile (
930
884
moduleSourceFile ,
931
- moduleReference
885
+ moduleReference ,
886
+ isExternal
932
887
) ;
933
888
934
889
return specifierAstModule ;
@@ -963,4 +918,44 @@ export class ExportAnalyzer {
963
918
964
919
return astImport ;
965
920
}
921
+
922
+ private _getResolvedModule (
923
+ importOrExportDeclaration : ts . ImportDeclaration | ts . ExportDeclaration | ts . ImportTypeNode ,
924
+ moduleSpecifier : string
925
+ ) : ts . ResolvedModuleFull {
926
+ const resolvedModule : ts . ResolvedModuleFull | undefined = TypeScriptInternals . getResolvedModule (
927
+ importOrExportDeclaration . getSourceFile ( ) ,
928
+ moduleSpecifier
929
+ ) ;
930
+
931
+ if ( resolvedModule === undefined ) {
932
+ // This should not happen, since getResolvedModule() specifically looks up names that the compiler
933
+ // found in export declarations for this source file
934
+ //
935
+ // Encountered in https://github.com/microsoft/rushstack/issues/1914
936
+ throw new InternalError (
937
+ `getResolvedModule() could not resolve module name ${ JSON . stringify ( moduleSpecifier ) } \n` +
938
+ SourceFileLocationFormatter . formatDeclaration ( importOrExportDeclaration )
939
+ ) ;
940
+ }
941
+
942
+ return resolvedModule ;
943
+ }
944
+
945
+ private _getModuleSpecifier (
946
+ importOrExportDeclaration : ts . ImportDeclaration | ts . ExportDeclaration | ts . ImportTypeNode
947
+ ) : string {
948
+ // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point'
949
+ const moduleSpecifier : string | undefined =
950
+ TypeScriptHelpers . getModuleSpecifier ( importOrExportDeclaration ) ;
951
+
952
+ if ( ! moduleSpecifier ) {
953
+ throw new InternalError (
954
+ 'Unable to parse module specifier\n' +
955
+ SourceFileLocationFormatter . formatDeclaration ( importOrExportDeclaration )
956
+ ) ;
957
+ }
958
+
959
+ return moduleSpecifier ;
960
+ }
966
961
}
0 commit comments