Skip to content

Commit 0e879c0

Browse files
author
Andy
authored
Merge pull request #12103 from Microsoft/path_mapping_exact_extension
Allow a path mapping to provide a file extension
2 parents 1510674 + 43a4b22 commit 0e879c0

11 files changed

+179
-8
lines changed

src/compiler/core.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,13 @@ namespace ts {
22102210
* Path must have a valid extension.
22112211
*/
22122212
export function extensionFromPath(path: string): Extension {
2213+
const ext = tryGetExtensionFromPath(path);
2214+
if (ext !== undefined) {
2215+
return ext;
2216+
}
2217+
Debug.fail(`File ${path} has unknown extension.`);
2218+
}
2219+
export function tryGetExtensionFromPath(path: string): Extension | undefined {
22132220
if (fileExtensionIs(path, ".d.ts")) {
22142221
return Extension.Dts;
22152222
}
@@ -2225,7 +2232,5 @@ namespace ts {
22252232
if (fileExtensionIs(path, ".jsx")) {
22262233
return Extension.Jsx;
22272234
}
2228-
Debug.fail(`File ${path} has unknown extension.`);
2229-
return Extension.Js;
22302235
}
22312236
}

src/compiler/moduleNameResolver.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -515,18 +515,21 @@ namespace ts {
515515
if (state.traceEnabled) {
516516
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
517517
}
518-
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
518+
return forEach(state.compilerOptions.paths[matchedPatternText], subst => {
519519
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
520520
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
521521
if (state.traceEnabled) {
522522
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
523523
}
524-
const resolved = loader(extensions, candidate, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
525-
if (resolved) {
526-
return resolved;
524+
// A path mapping may have a ".ts" extension; in contrast to an import, which should omit it.
525+
const tsExtension = tryGetExtensionFromPath(candidate);
526+
if (tsExtension !== undefined) {
527+
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/false, state);
528+
return path && { path, extension: tsExtension };
527529
}
528-
}
529-
return undefined;
530+
531+
return loader(extensions, candidate, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
532+
});
530533
}
531534
else {
532535
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName));
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts] ////
2+
3+
//// [foo.ts]
4+
5+
export function foo() {}
6+
7+
//// [bar.js]
8+
export function bar() {}
9+
10+
//// [a.ts]
11+
import { foo } from "foo";
12+
import { bar } from "bar";
13+
14+
15+
//// [foo.js]
16+
"use strict";
17+
function foo() { }
18+
exports.foo = foo;
19+
//// [bar.js]
20+
"use strict";
21+
function bar() { }
22+
exports.bar = bar;
23+
//// [a.js]
24+
"use strict";
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== /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+
=== /foo/foo.ts ===
9+
10+
export function foo() {}
11+
>foo : Symbol(foo, Decl(foo.ts, 0, 0))
12+
13+
=== /bar/bar.js ===
14+
export function bar() {}
15+
>bar : Symbol(bar, Decl(bar.js, 0, 0))
16+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
"======== Resolving module 'foo' from '/a.ts'. ========",
3+
"Module resolution kind is not specified, using 'NodeJs'.",
4+
"'baseUrl' option is set to '/', 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 'foo'.",
7+
"Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.",
8+
"File '/foo/foo.ts' exist - use it as a name resolution result.",
9+
"======== Module name 'foo' was successfully resolved to '/foo/foo.ts'. ========",
10+
"======== Resolving module 'bar' from '/a.ts'. ========",
11+
"Module resolution kind is not specified, using 'NodeJs'.",
12+
"'baseUrl' option is set to '/', using this value to resolve non-relative module name 'bar'",
13+
"'paths' option is specified, looking for a pattern to match module name 'bar'.",
14+
"Module name 'bar', matched pattern 'bar'.",
15+
"Trying substitution 'bar/bar.js', candidate module location: 'bar/bar.js'.",
16+
"File '/bar/bar.js' exist - use it as a name resolution result.",
17+
"======== Module name 'bar' was successfully resolved to '/bar/bar.js'. ========"
18+
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== /a.ts ===
2+
import { foo } from "foo";
3+
>foo : () => void
4+
5+
import { bar } from "bar";
6+
>bar : () => void
7+
8+
=== /foo/foo.ts ===
9+
10+
export function foo() {}
11+
>foo : () => void
12+
13+
=== /bar/bar.js ===
14+
export function bar() {}
15+
>bar : () => void
16+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/a.ts(2,21): error TS2307: Cannot find module 'foo'.
2+
3+
4+
==== /a.ts (1 errors) ====
5+
6+
import { foo } from "foo";
7+
~~~~~
8+
!!! error TS2307: Cannot find module 'foo'.
9+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//// [a.ts]
2+
3+
import { foo } from "foo";
4+
5+
6+
//// [a.js]
7+
"use strict";
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
"======== Resolving module 'foo' from '/a.ts'. ========",
3+
"Module resolution kind is not specified, using 'NodeJs'.",
4+
"'baseUrl' option is set to '/', 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 'foo'.",
7+
"Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.",
8+
"File '/foo/foo.ts' does not exist.",
9+
"Loading module 'foo' from 'node_modules' folder.",
10+
"File '/node_modules/foo.ts' does not exist.",
11+
"File '/node_modules/foo.tsx' does not exist.",
12+
"File '/node_modules/foo.d.ts' does not exist.",
13+
"File '/node_modules/foo/package.json' does not exist.",
14+
"File '/node_modules/foo/index.ts' does not exist.",
15+
"File '/node_modules/foo/index.tsx' does not exist.",
16+
"File '/node_modules/foo/index.d.ts' does not exist.",
17+
"File '/node_modules/@types/foo.d.ts' does not exist.",
18+
"File '/node_modules/@types/foo/package.json' does not exist.",
19+
"File '/node_modules/@types/foo/index.d.ts' does not exist.",
20+
"'baseUrl' option is set to '/', using this value to resolve non-relative module name 'foo'",
21+
"'paths' option is specified, looking for a pattern to match module name 'foo'.",
22+
"Module name 'foo', matched pattern 'foo'.",
23+
"Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.",
24+
"File '/foo/foo.ts' does not exist.",
25+
"Loading module 'foo' from 'node_modules' folder.",
26+
"File '/node_modules/foo.js' does not exist.",
27+
"File '/node_modules/foo.jsx' does not exist.",
28+
"File '/node_modules/foo/package.json' does not exist.",
29+
"File '/node_modules/foo/index.js' does not exist.",
30+
"File '/node_modules/foo/index.jsx' does not exist.",
31+
"======== Module name 'foo' was not resolved. ========"
32+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @noImplicitReferences: true
2+
// @traceResolution: true
3+
// @allowJs: true
4+
5+
// @Filename: /foo/foo.ts
6+
export function foo() {}
7+
8+
// @Filename: /bar/bar.js
9+
export function bar() {}
10+
11+
// @Filename: /a.ts
12+
import { foo } from "foo";
13+
import { bar } from "bar";
14+
15+
// @Filename: /tsconfig.json
16+
{
17+
"compilerOptions": {
18+
"baseUrl": ".",
19+
"paths": {
20+
"foo": ["foo/foo.ts"],
21+
"bar": ["bar/bar.js"]
22+
},
23+
"allowJs": true,
24+
"outDir": "bin"
25+
}
26+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @noImplicitReferences: true
2+
// @traceResolution: true
3+
4+
// @Filename: /a.ts
5+
import { foo } from "foo";
6+
7+
// @Filename: /tsconfig.json
8+
{
9+
"compilerOptions": {
10+
"baseUrl": ".",
11+
"paths": {
12+
"foo": ["foo/foo.ts"]
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)