Skip to content

Commit 299e85f

Browse files
author
Andy Hanson
committed
Allow a path mapping to provide a file extension
1 parent 84f8f8b commit 299e85f

11 files changed

+157
-23
lines changed

src/compiler/core.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,8 +2081,9 @@ namespace ts {
20812081
}
20822082
}
20832083

2084-
export function fail(message?: string): void {
2084+
export function fail(message?: string): never {
20852085
Debug.assert(/*expression*/ false, message);
2086+
return <never>null;
20862087
}
20872088
}
20882089

@@ -2210,22 +2211,17 @@ namespace ts {
22102211
* Path must have a valid extension.
22112212
*/
22122213
export function extensionFromPath(path: string): Extension {
2213-
if (fileExtensionIs(path, ".d.ts")) {
2214-
return Extension.Dts;
2215-
}
2216-
if (fileExtensionIs(path, ".ts")) {
2217-
return Extension.Ts;
2218-
}
2219-
if (fileExtensionIs(path, ".tsx")) {
2220-
return Extension.Tsx;
2221-
}
2222-
if (fileExtensionIs(path, ".js")) {
2223-
return Extension.Js;
2224-
}
2225-
if (fileExtensionIs(path, ".jsx")) {
2226-
return Extension.Jsx;
2214+
return tryGetTypeScriptExtensionFromPath(path) || tryGetJavaScriptExtensionFromPath(path) || Debug.fail(`File ${path} has unknown extension.`);
2215+
}
2216+
export function tryGetTypeScriptExtensionFromPath(path: string): Extension | undefined {
2217+
return tryExtension(path, ".d.ts", Extension.Dts) || tryExtension(path, ".ts", Extension.Ts) || tryExtension(path, ".tsx", Extension.Tsx);
2218+
}
2219+
function tryGetJavaScriptExtensionFromPath(path: string): Extension | undefined {
2220+
return tryExtension(path, ".js", Extension.Js) || tryExtension(path, ".jsx", Extension.Jsx);
2221+
}
2222+
function tryExtension(path: string, extensionString: string, extension: Extension): Extension | undefined {
2223+
if (fileExtensionIs(path, extensionString)) {
2224+
return extension;
22272225
}
2228-
Debug.fail(`File ${path} has unknown extension.`);
2229-
return Extension.Js;
22302226
}
22312227
}

src/compiler/moduleNameResolver.ts

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