Skip to content

Commit b534fb4

Browse files
authored
Merge pull request #27980 from EECOLOR/leading-slash-imports
match leading slash imports with path mappings - fixes #13730
2 parents 514e8a1 + 1a9c209 commit b534fb4

File tree

40 files changed

+1276
-24
lines changed

40 files changed

+1276
-24
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,9 @@ namespace ts {
730730
function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
731731
state: ModuleResolutionState): Resolved | undefined {
732732

733+
const resolved = tryLoadModuleUsingPathsIfEligible(extensions, moduleName, loader, state);
734+
if (resolved) return resolved.value;
735+
733736
if (!isExternalModuleNameRelative(moduleName)) {
734737
return tryLoadModuleUsingBaseUrl(extensions, moduleName, loader, state);
735738
}
@@ -738,6 +741,17 @@ namespace ts {
738741
}
739742
}
740743

744+
function tryLoadModuleUsingPathsIfEligible(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState) {
745+
const { baseUrl, paths } = state.compilerOptions;
746+
if (baseUrl && paths && !pathIsRelative(moduleName)) {
747+
if (state.traceEnabled) {
748+
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName);
749+
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
750+
}
751+
return tryLoadModuleUsingPaths(extensions, moduleName, baseUrl, paths, loader, /*onlyRecordFailures*/ false, state);
752+
}
753+
}
754+
741755
function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
742756
state: ModuleResolutionState): Resolved | undefined {
743757

@@ -816,22 +830,13 @@ namespace ts {
816830
}
817831

818832
function tryLoadModuleUsingBaseUrl(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined {
819-
const { baseUrl, paths } = state.compilerOptions;
833+
const { baseUrl } = state.compilerOptions;
820834
if (!baseUrl) {
821835
return undefined;
822836
}
823837
if (state.traceEnabled) {
824838
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName);
825839
}
826-
if (paths) {
827-
if (state.traceEnabled) {
828-
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
829-
}
830-
const resolved = tryLoadModuleUsingPaths(extensions, moduleName, baseUrl, paths, loader, /*onlyRecordFailures*/ false, state);
831-
if (resolved) {
832-
return resolved.value;
833-
}
834-
}
835840
const candidate = normalizePath(combinePaths(baseUrl, moduleName));
836841
if (state.traceEnabled) {
837842
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, baseUrl, candidate);

src/services/stringCompletions.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,9 @@ namespace ts.Completions.StringCompletions {
247247
const scriptPath = sourceFile.path;
248248
const scriptDirectory = getDirectoryPath(scriptPath);
249249

250-
const extensionOptions = getExtensionOptions(compilerOptions);
251-
if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) {
252-
if (compilerOptions.rootDirs) {
253-
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
254-
compilerOptions.rootDirs, literalValue, scriptDirectory, extensionOptions, compilerOptions, host, scriptPath);
255-
}
256-
else {
257-
return getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensionOptions, host, scriptPath);
258-
}
259-
}
260-
else {
261-
return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, compilerOptions, host, typeChecker);
262-
}
250+
return isPathRelativeToScript(literalValue) || !compilerOptions.baseUrl && (isRootedDiskPath(literalValue) || isUrl(literalValue))
251+
? getCompletionEntriesForRelativeModules(literalValue, scriptDirectory, compilerOptions, host, scriptPath)
252+
: getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, compilerOptions, host, typeChecker);
263253
}
264254

265255
interface ExtensionOptions {
@@ -269,6 +259,17 @@ namespace ts.Completions.StringCompletions {
269259
function getExtensionOptions(compilerOptions: CompilerOptions, includeExtensions = false): ExtensionOptions {
270260
return { extensions: getSupportedExtensionsForModuleResolution(compilerOptions), includeExtensions };
271261
}
262+
function getCompletionEntriesForRelativeModules(literalValue: string, scriptDirectory: string, compilerOptions: CompilerOptions, host: LanguageServiceHost, scriptPath: Path) {
263+
const extensionOptions = getExtensionOptions(compilerOptions);
264+
if (compilerOptions.rootDirs) {
265+
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
266+
compilerOptions.rootDirs, literalValue, scriptDirectory, extensionOptions, compilerOptions, host, scriptPath);
267+
}
268+
else {
269+
return getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensionOptions, host, scriptPath);
270+
}
271+
}
272+
272273
function getSupportedExtensionsForModuleResolution(compilerOptions: CompilerOptions): ReadonlyArray<Extension> {
273274
const extensions = getSupportedExtensions(compilerOptions);
274275
return compilerOptions.resolveJsonModule && getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeJs ?

src/testRunner/unittests/moduleResolution.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,9 @@ import b = require("./moduleB");
757757
],
758758
"somefolder/*": [
759759
"someanotherfolder/*"
760+
],
761+
"/rooted/*": [
762+
"generated/*"
760763
]
761764
}
762765
};
@@ -773,6 +776,7 @@ import b = require("./moduleB");
773776
"/root/folder1/file2/index.d.ts",
774777
// then first attempt on 'generated/*' was successful
775778
]);
779+
check("/rooted/folder1/file2", file2, []);
776780
check("folder2/file3", file3, [
777781
// first try '*'
778782
"/root/folder2/file3.ts",
@@ -900,6 +904,9 @@ import b = require("./moduleB");
900904
],
901905
"somefolder/*": [
902906
"someanotherfolder/*"
907+
],
908+
"/rooted/*": [
909+
"generated/*"
903910
]
904911
}
905912
};
@@ -911,6 +918,7 @@ import b = require("./moduleB");
911918
"/root/folder1/file2.d.ts",
912919
// success when using 'generated/*'
913920
]);
921+
check("/rooted/folder1/file2", file2, []);
914922
check("folder1/file3", file3, [
915923
// first try '*'
916924
"/root/folder1/file3.ts",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [tests/cases/compiler/pathMappingBasedModuleResolution_rootImport_aliasWithRoot.ts] ////
2+
3+
//// [foo.ts]
4+
export function foo() {}
5+
6+
//// [bar.js]
7+
export function bar() {}
8+
9+
//// [a.ts]
10+
import { foo } from "/foo";
11+
import { bar } from "/bar";
12+
13+
14+
//// [foo.js]
15+
"use strict";
16+
exports.__esModule = true;
17+
function foo() { }
18+
exports.foo = foo;
19+
//// [bar.js]
20+
"use strict";
21+
exports.__esModule = true;
22+
function bar() { }
23+
exports.bar = bar;
24+
//// [a.js]
25+
"use strict";
26+
exports.__esModule = true;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
=== /root/a.ts ===
2+
import { foo } from "/foo";
3+
>foo : Symbol(foo, Decl(a.ts, 0, 8))
4+
5+
import { bar } from "/bar";
6+
>bar : Symbol(bar, Decl(a.ts, 1, 8))
7+
8+
=== /root/src/foo.ts ===
9+
export function foo() {}
10+
>foo : Symbol(foo, Decl(foo.ts, 0, 0))
11+
12+
=== /root/src/bar.js ===
13+
export function bar() {}
14+
>bar : Symbol(bar, Decl(bar.js, 0, 0))
15+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[
2+
"======== Resolving module '/foo' from '/root/a.ts'. ========",
3+
"Module resolution kind is not specified, using 'NodeJs'.",
4+
"'baseUrl' option is set to '/root', using this value to resolve non-relative module name '/foo'.",
5+
"'paths' option is specified, looking for a pattern to match module name '/foo'.",
6+
"Module name '/foo', matched pattern '/*'.",
7+
"Trying substitution './src/*', candidate module location: './src/foo'.",
8+
"Loading module as file / folder, candidate module location '/root/src/foo', target file type 'TypeScript'.",
9+
"File '/root/src/foo.ts' exist - use it as a name resolution result.",
10+
"======== Module name '/foo' was successfully resolved to '/root/src/foo.ts'. ========",
11+
"======== Resolving module '/bar' from '/root/a.ts'. ========",
12+
"Module resolution kind is not specified, using 'NodeJs'.",
13+
"'baseUrl' option is set to '/root', using this value to resolve non-relative module name '/bar'.",
14+
"'paths' option is specified, looking for a pattern to match module name '/bar'.",
15+
"Module name '/bar', matched pattern '/*'.",
16+
"Trying substitution './src/*', candidate module location: './src/bar'.",
17+
"Loading module as file / folder, candidate module location '/root/src/bar', target file type 'TypeScript'.",
18+
"File '/root/src/bar.ts' does not exist.",
19+
"File '/root/src/bar.tsx' does not exist.",
20+
"File '/root/src/bar.d.ts' does not exist.",
21+
"Directory '/root/src/bar' does not exist, skipping all lookups in it.",
22+
"Loading module as file / folder, candidate module location '/bar', target file type 'TypeScript'.",
23+
"File '/bar.ts' does not exist.",
24+
"File '/bar.tsx' does not exist.",
25+
"File '/bar.d.ts' does not exist.",
26+
"Directory '/bar' does not exist, skipping all lookups in it.",
27+
"'baseUrl' option is set to '/root', using this value to resolve non-relative module name '/bar'.",
28+
"'paths' option is specified, looking for a pattern to match module name '/bar'.",
29+
"Module name '/bar', matched pattern '/*'.",
30+
"Trying substitution './src/*', candidate module location: './src/bar'.",
31+
"Loading module as file / folder, candidate module location '/root/src/bar', target file type 'JavaScript'.",
32+
"File '/root/src/bar.js' exist - use it as a name resolution result.",
33+
"======== Module name '/bar' was successfully resolved to '/root/src/bar.js'. ========"
34+
]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
=== /root/a.ts ===
2+
import { foo } from "/foo";
3+
>foo : () => void
4+
5+
import { bar } from "/bar";
6+
>bar : () => void
7+
8+
=== /root/src/foo.ts ===
9+
export function foo() {}
10+
>foo : () => void
11+
12+
=== /root/src/bar.js ===
13+
export function bar() {}
14+
>bar : () => void
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/pathMappingBasedModuleResolution_rootImport_aliasWithRoot_differentRootTypes.ts] ////
2+
3+
//// [foo.ts]
4+
export function foo() {}
5+
6+
//// [bar.js]
7+
export function bar() {}
8+
9+
//// [a.ts]
10+
import { foo as foo1 } from "/foo";
11+
import { bar as bar1 } from "/bar";
12+
import { foo as foo2 } from "c:/foo";
13+
import { bar as bar2 } from "c:/bar";
14+
import { foo as foo3 } from "c:\\foo";
15+
import { bar as bar3 } from "c:\\bar";
16+
import { foo as foo4 } from "//server/foo";
17+
import { bar as bar4 } from "//server/bar";
18+
import { foo as foo5 } from "\\\\server\\foo";
19+
import { bar as bar5 } from "\\\\server\\bar";
20+
import { foo as foo6 } from "file:///foo";
21+
import { bar as bar6 } from "file:///bar";
22+
import { foo as foo7 } from "file://c:/foo";
23+
import { bar as bar7 } from "file://c:/bar";
24+
import { foo as foo8 } from "file://server/foo";
25+
import { bar as bar8 } from "file://server/bar";
26+
import { foo as foo9 } from "http://server/foo";
27+
import { bar as bar9 } from "http://server/bar";
28+
29+
30+
//// [foo.js]
31+
"use strict";
32+
exports.__esModule = true;
33+
function foo() { }
34+
exports.foo = foo;
35+
//// [bar.js]
36+
"use strict";
37+
exports.__esModule = true;
38+
function bar() { }
39+
exports.bar = bar;
40+
//// [a.js]
41+
"use strict";
42+
exports.__esModule = true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
=== /root/a.ts ===
2+
import { foo as foo1 } from "/foo";
3+
>foo : Symbol(foo1, Decl(a.ts, 0, 8))
4+
>foo1 : Symbol(foo1, Decl(a.ts, 0, 8))
5+
6+
import { bar as bar1 } from "/bar";
7+
>bar : Symbol(bar1, Decl(a.ts, 1, 8))
8+
>bar1 : Symbol(bar1, Decl(a.ts, 1, 8))
9+
10+
import { foo as foo2 } from "c:/foo";
11+
>foo : Symbol(foo2, Decl(a.ts, 2, 8))
12+
>foo2 : Symbol(foo2, Decl(a.ts, 2, 8))
13+
14+
import { bar as bar2 } from "c:/bar";
15+
>bar : Symbol(bar2, Decl(a.ts, 3, 8))
16+
>bar2 : Symbol(bar2, Decl(a.ts, 3, 8))
17+
18+
import { foo as foo3 } from "c:\\foo";
19+
>foo : Symbol(foo3, Decl(a.ts, 4, 8))
20+
>foo3 : Symbol(foo3, Decl(a.ts, 4, 8))
21+
22+
import { bar as bar3 } from "c:\\bar";
23+
>bar : Symbol(bar3, Decl(a.ts, 5, 8))
24+
>bar3 : Symbol(bar3, Decl(a.ts, 5, 8))
25+
26+
import { foo as foo4 } from "//server/foo";
27+
>foo : Symbol(foo4, Decl(a.ts, 6, 8))
28+
>foo4 : Symbol(foo4, Decl(a.ts, 6, 8))
29+
30+
import { bar as bar4 } from "//server/bar";
31+
>bar : Symbol(bar4, Decl(a.ts, 7, 8))
32+
>bar4 : Symbol(bar4, Decl(a.ts, 7, 8))
33+
34+
import { foo as foo5 } from "\\\\server\\foo";
35+
>foo : Symbol(foo5, Decl(a.ts, 8, 8))
36+
>foo5 : Symbol(foo5, Decl(a.ts, 8, 8))
37+
38+
import { bar as bar5 } from "\\\\server\\bar";
39+
>bar : Symbol(bar5, Decl(a.ts, 9, 8))
40+
>bar5 : Symbol(bar5, Decl(a.ts, 9, 8))
41+
42+
import { foo as foo6 } from "file:///foo";
43+
>foo : Symbol(foo6, Decl(a.ts, 10, 8))
44+
>foo6 : Symbol(foo6, Decl(a.ts, 10, 8))
45+
46+
import { bar as bar6 } from "file:///bar";
47+
>bar : Symbol(bar6, Decl(a.ts, 11, 8))
48+
>bar6 : Symbol(bar6, Decl(a.ts, 11, 8))
49+
50+
import { foo as foo7 } from "file://c:/foo";
51+
>foo : Symbol(foo7, Decl(a.ts, 12, 8))
52+
>foo7 : Symbol(foo7, Decl(a.ts, 12, 8))
53+
54+
import { bar as bar7 } from "file://c:/bar";
55+
>bar : Symbol(bar7, Decl(a.ts, 13, 8))
56+
>bar7 : Symbol(bar7, Decl(a.ts, 13, 8))
57+
58+
import { foo as foo8 } from "file://server/foo";
59+
>foo : Symbol(foo8, Decl(a.ts, 14, 8))
60+
>foo8 : Symbol(foo8, Decl(a.ts, 14, 8))
61+
62+
import { bar as bar8 } from "file://server/bar";
63+
>bar : Symbol(bar8, Decl(a.ts, 15, 8))
64+
>bar8 : Symbol(bar8, Decl(a.ts, 15, 8))
65+
66+
import { foo as foo9 } from "http://server/foo";
67+
>foo : Symbol(foo9, Decl(a.ts, 16, 8))
68+
>foo9 : Symbol(foo9, Decl(a.ts, 16, 8))
69+
70+
import { bar as bar9 } from "http://server/bar";
71+
>bar : Symbol(bar9, Decl(a.ts, 17, 8))
72+
>bar9 : Symbol(bar9, Decl(a.ts, 17, 8))
73+
74+
=== /root/src/foo.ts ===
75+
export function foo() {}
76+
>foo : Symbol(foo, Decl(foo.ts, 0, 0))
77+
78+
=== /root/src/bar.js ===
79+
export function bar() {}
80+
>bar : Symbol(bar, Decl(bar.js, 0, 0))
81+

0 commit comments

Comments
 (0)