1
1
// Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers.
2
2
/* @internal */
3
3
namespace ts . moduleSpecifiers {
4
- const enum RelativePreference { Relative , NonRelative , Auto }
4
+ const enum RelativePreference { Relative , NonRelative , Shortest , ExternalNonRelative }
5
5
// See UserPreferences#importPathEnding
6
6
const enum Ending { Minimal , Index , JsExtension }
7
7
@@ -13,7 +13,11 @@ namespace ts.moduleSpecifiers {
13
13
14
14
function getPreferences ( { importModuleSpecifierPreference, importModuleSpecifierEnding } : UserPreferences , compilerOptions : CompilerOptions , importingSourceFile : SourceFile ) : Preferences {
15
15
return {
16
- relativePreference : importModuleSpecifierPreference === "relative" ? RelativePreference . Relative : importModuleSpecifierPreference === "non-relative" ? RelativePreference . NonRelative : RelativePreference . Auto ,
16
+ relativePreference :
17
+ importModuleSpecifierPreference === "relative" ? RelativePreference . Relative :
18
+ importModuleSpecifierPreference === "non-relative" ? RelativePreference . NonRelative :
19
+ importModuleSpecifierPreference === "external-non-relative" ? RelativePreference . ExternalNonRelative :
20
+ RelativePreference . Shortest ,
17
21
ending : getEnding ( ) ,
18
22
} ;
19
23
function getEnding ( ) : Ending {
@@ -147,17 +151,19 @@ namespace ts.moduleSpecifiers {
147
151
148
152
interface Info {
149
153
readonly getCanonicalFileName : GetCanonicalFileName ;
154
+ readonly importingSourceFileName : Path
150
155
readonly sourceDirectory : Path ;
151
156
}
152
157
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
153
158
function getInfo ( importingSourceFileName : Path , host : ModuleSpecifierResolutionHost ) : Info {
154
159
const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ? host . useCaseSensitiveFileNames ( ) : true ) ;
155
160
const sourceDirectory = getDirectoryPath ( importingSourceFileName ) ;
156
- return { getCanonicalFileName, sourceDirectory } ;
161
+ return { getCanonicalFileName, importingSourceFileName , sourceDirectory } ;
157
162
}
158
163
159
- function getLocalModuleSpecifier ( moduleFileName : string , { getCanonicalFileName , sourceDirectory } : Info , compilerOptions : CompilerOptions , host : ModuleSpecifierResolutionHost , { ending, relativePreference } : Preferences ) : string {
164
+ function getLocalModuleSpecifier ( moduleFileName : string , info : Info , compilerOptions : CompilerOptions , host : ModuleSpecifierResolutionHost , { ending, relativePreference } : Preferences ) : string {
160
165
const { baseUrl, paths, rootDirs, bundledPackageName } = compilerOptions ;
166
+ const { sourceDirectory, getCanonicalFileName } = info ;
161
167
162
168
const relativePath = rootDirs && tryGetModuleNameFromRootDirs ( rootDirs , moduleFileName , sourceDirectory , getCanonicalFileName , ending , compilerOptions ) ||
163
169
removeExtensionAndIndexPostFix ( ensurePathIsNonModuleName ( getRelativePathFromDirectory ( sourceDirectory , moduleFileName , getCanonicalFileName ) ) , ending , compilerOptions ) ;
@@ -174,13 +180,34 @@ namespace ts.moduleSpecifiers {
174
180
const bundledPkgReference = bundledPackageName ? combinePaths ( bundledPackageName , relativeToBaseUrl ) : relativeToBaseUrl ;
175
181
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( bundledPkgReference , ending , compilerOptions ) ;
176
182
const fromPaths = paths && tryGetModuleNameFromPaths ( removeFileExtension ( bundledPkgReference ) , importRelativeToBaseUrl , paths ) ;
177
- const nonRelative = fromPaths === undefined ? importRelativeToBaseUrl : fromPaths ;
183
+ const nonRelative = fromPaths === undefined ? importRelativeToBaseUrl : fromPaths . path ;
178
184
179
185
if ( relativePreference === RelativePreference . NonRelative ) {
180
186
return nonRelative ;
181
187
}
182
188
183
- if ( relativePreference !== RelativePreference . Auto ) Debug . assertNever ( relativePreference ) ;
189
+ if ( relativePreference === RelativePreference . ExternalNonRelative ) {
190
+ Debug . assertIsDefined ( host . getNearestAncestorDirectoryWithPackageJson ) ;
191
+ const projectDirectory = host . getCurrentDirectory ( ) ;
192
+ const modulePath = toPath ( moduleFileName , projectDirectory , getCanonicalFileName ) ;
193
+ const sourceIsInternal = startsWith ( sourceDirectory , projectDirectory ) ;
194
+ const targetIsInternal = startsWith ( modulePath , projectDirectory ) ;
195
+ if ( sourceIsInternal && ! targetIsInternal || ! sourceIsInternal && targetIsInternal ) {
196
+ // 1. The import path crosses the boundary of the tsconfig.json-containing directory.
197
+ return nonRelative ;
198
+ }
199
+
200
+ const nearestTargetPackageJson = host . getNearestAncestorDirectoryWithPackageJson ( getDirectoryPath ( modulePath ) ) ;
201
+ const nearestSourcePackageJson = host . getNearestAncestorDirectoryWithPackageJson ( sourceDirectory ) ;
202
+ if ( nearestSourcePackageJson !== nearestTargetPackageJson ) {
203
+ // 2. The importing and imported files are part of different packages.
204
+ return nonRelative ;
205
+ }
206
+
207
+ return relativePath ;
208
+ }
209
+
210
+ if ( relativePreference !== RelativePreference . Shortest ) Debug . assertNever ( relativePreference ) ;
184
211
185
212
// Prefer a relative import over a baseUrl import if it has fewer components.
186
213
return isPathRelativeToParent ( nonRelative ) || countPathComponents ( relativePath ) < countPathComponents ( nonRelative ) ? relativePath : nonRelative ;
@@ -325,7 +352,7 @@ namespace ts.moduleSpecifiers {
325
352
}
326
353
}
327
354
328
- function tryGetModuleNameFromPaths ( relativeToBaseUrlWithIndex : string , relativeToBaseUrl : string , paths : MapLike < readonly string [ ] > ) : string | undefined {
355
+ function tryGetModuleNameFromPaths ( relativeToBaseUrlWithIndex : string , relativeToBaseUrl : string , paths : MapLike < readonly string [ ] > ) : { key : string , path : string } | undefined {
329
356
for ( const key in paths ) {
330
357
for ( const patternText of paths [ key ] ) {
331
358
const pattern = removeFileExtension ( normalizePath ( patternText ) ) ;
@@ -338,11 +365,11 @@ namespace ts.moduleSpecifiers {
338
365
endsWith ( relativeToBaseUrl , suffix ) ||
339
366
! suffix && relativeToBaseUrl === removeTrailingDirectorySeparator ( prefix ) ) {
340
367
const matchedStar = relativeToBaseUrl . substr ( prefix . length , relativeToBaseUrl . length - suffix . length ) ;
341
- return key . replace ( "*" , matchedStar ) ;
368
+ return { key, path : key . replace ( "*" , matchedStar ) } ;
342
369
}
343
370
}
344
371
else if ( pattern === relativeToBaseUrl || pattern === relativeToBaseUrlWithIndex ) {
345
- return key ;
372
+ return { key, path : key } ;
346
373
}
347
374
}
348
375
}
@@ -431,7 +458,7 @@ namespace ts.moduleSpecifiers {
431
458
versionPaths . paths
432
459
) ;
433
460
if ( fromPaths !== undefined ) {
434
- moduleFileToTry = combinePaths ( packageRootPath , fromPaths ) ;
461
+ moduleFileToTry = combinePaths ( packageRootPath , fromPaths . path ) ;
435
462
}
436
463
}
437
464
0 commit comments