Skip to content

Commit 512c671

Browse files
author
Andy
authored
Merge pull request #8895 from Microsoft/strip_extension
Allow an import of "foo.js" to be matched by a file "foo.ts"
2 parents 3954e99 + 704f987 commit 512c671

15 files changed

+255
-23
lines changed

src/compiler/core.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -849,13 +849,22 @@ namespace ts {
849849
const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
850850
export function removeFileExtension(path: string): string {
851851
for (const ext of extensionsToRemove) {
852-
if (fileExtensionIs(path, ext)) {
853-
return path.substr(0, path.length - ext.length);
852+
const extensionless = tryRemoveExtension(path, ext);
853+
if (extensionless !== undefined) {
854+
return extensionless;
854855
}
855856
}
856857
return path;
857858
}
858859

860+
export function tryRemoveExtension(path: string, extension: string): string {
861+
return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined;
862+
}
863+
864+
export function isJsxOrTsxExtension(ext: string): boolean {
865+
return ext === ".jsx" || ext === ".tsx";
866+
}
867+
859868
export interface ObjectAllocator {
860869
getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node;
861870
getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile;

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -2772,6 +2772,10 @@
27722772
"category": "Error",
27732773
"code": 6131
27742774
},
2775+
"File name '{0}' has a '{1}' extension - stripping it": {
2776+
"category": "Message",
2777+
"code": 6132
2778+
},
27752779

27762780
"Variable '{0}' implicitly has an '{1}' type.": {
27772781
"category": "Error",

src/compiler/program.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -619,8 +619,25 @@ namespace ts {
619619
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
620620
*/
621621
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
622+
// First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts"
623+
const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
624+
if (resolvedByAddingOrKeepingExtension) {
625+
return resolvedByAddingOrKeepingExtension;
626+
}
627+
// Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
628+
if (hasJavaScriptFileExtension(candidate)) {
629+
const extensionless = removeFileExtension(candidate);
630+
if (state.traceEnabled) {
631+
const extension = candidate.substring(extensionless.length);
632+
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
633+
}
634+
return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
635+
}
636+
}
637+
638+
function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
622639
if (!onlyRecordFailures) {
623-
// check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing
640+
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
624641
const directory = getDirectoryPath(candidate);
625642
if (directory) {
626643
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
@@ -629,7 +646,7 @@ namespace ts {
629646
return forEach(extensions, tryLoad);
630647

631648
function tryLoad(ext: string): string {
632-
if (ext === ".tsx" && state.skipTsx) {
649+
if (state.skipTsx && isJsxOrTsxExtension(ext)) {
633650
return undefined;
634651
}
635652
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//// [tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts] ////
2+
3+
//// [a.ts]
4+
5+
export default 0;
6+
7+
// No extension: '.ts' added
8+
//// [b.ts]
9+
import a from './a';
10+
11+
// Matching extension
12+
//// [c.ts]
13+
import a from './a.ts';
14+
15+
// '.js' extension: stripped and replaced with '.ts'
16+
//// [d.ts]
17+
import a from './a.js';
18+
19+
//// [jquery.d.ts]
20+
declare var x: number;
21+
export default x;
22+
23+
// No extension: '.d.ts' added
24+
//// [jquery_user_1.ts]
25+
import j from "./jquery";
26+
27+
// '.js' extension: stripped and replaced with '.d.ts'
28+
//// [jquery_user_1.ts]
29+
import j from "./jquery.js"
30+
31+
32+
//// [a.js]
33+
"use strict";
34+
exports.__esModule = true;
35+
exports["default"] = 0;
36+
// No extension: '.ts' added
37+
//// [b.js]
38+
"use strict";
39+
// Matching extension
40+
//// [c.js]
41+
"use strict";
42+
// '.js' extension: stripped and replaced with '.ts'
43+
//// [d.js]
44+
"use strict";
45+
//// [jquery_user_1.js]
46+
"use strict";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== /src/a.ts ===
2+
3+
No type information for this code.export default 0;
4+
No type information for this code.
5+
No type information for this code.// No extension: '.ts' added
6+
No type information for this code.=== /src/b.ts ===
7+
import a from './a';
8+
>a : Symbol(a, Decl(b.ts, 0, 6))
9+
10+
// Matching extension
11+
=== /src/c.ts ===
12+
import a from './a.ts';
13+
>a : Symbol(a, Decl(c.ts, 0, 6))
14+
15+
// '.js' extension: stripped and replaced with '.ts'
16+
=== /src/d.ts ===
17+
import a from './a.js';
18+
>a : Symbol(a, Decl(d.ts, 0, 6))
19+
20+
=== /src/jquery.d.ts ===
21+
declare var x: number;
22+
>x : Symbol(x, Decl(jquery.d.ts, 0, 11))
23+
24+
export default x;
25+
>x : Symbol(x, Decl(jquery.d.ts, 0, 11))
26+
27+
// No extension: '.d.ts' added
28+
=== /src/jquery_user_1.ts ===
29+
import j from "./jquery";
30+
>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6))
31+
32+
// '.js' extension: stripped and replaced with '.d.ts'
33+
=== /src/jquery_user_1.ts ===
34+
import j from "./jquery.js"
35+
>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6))
36+
>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6))
37+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
"======== Resolving module './a' from '/src/b.ts'. ========",
3+
"Module resolution kind is not specified, using 'NodeJs'.",
4+
"Loading module as file / folder, candidate module location '/src/a'.",
5+
"File '/src/a.ts' exist - use it as a name resolution result.",
6+
"======== Module name './a' was successfully resolved to '/src/a.ts'. ========",
7+
"======== Resolving module './a.ts' from '/src/c.ts'. ========",
8+
"Module resolution kind is not specified, using 'NodeJs'.",
9+
"Loading module as file / folder, candidate module location '/src/a.ts'.",
10+
"File '/src/a.ts' exist - use it as a name resolution result.",
11+
"======== Module name './a.ts' was successfully resolved to '/src/a.ts'. ========",
12+
"======== Resolving module './a.js' from '/src/d.ts'. ========",
13+
"Module resolution kind is not specified, using 'NodeJs'.",
14+
"Loading module as file / folder, candidate module location '/src/a.js'.",
15+
"File '/src/a.js.ts' does not exist.",
16+
"File '/src/a.js.tsx' does not exist.",
17+
"File '/src/a.js.d.ts' does not exist.",
18+
"File name '/src/a.js' has a '.js' extension - stripping it",
19+
"File '/src/a.ts' exist - use it as a name resolution result.",
20+
"======== Module name './a.js' was successfully resolved to '/src/a.ts'. ========",
21+
"======== Resolving module './jquery.js' from '/src/jquery_user_1.ts'. ========",
22+
"Module resolution kind is not specified, using 'NodeJs'.",
23+
"Loading module as file / folder, candidate module location '/src/jquery.js'.",
24+
"File '/src/jquery.js.ts' does not exist.",
25+
"File '/src/jquery.js.tsx' does not exist.",
26+
"File '/src/jquery.js.d.ts' does not exist.",
27+
"File name '/src/jquery.js' has a '.js' extension - stripping it",
28+
"File '/src/jquery.ts' does not exist.",
29+
"File '/src/jquery.tsx' does not exist.",
30+
"File '/src/jquery.d.ts' exist - use it as a name resolution result.",
31+
"======== Module name './jquery.js' was successfully resolved to '/src/jquery.d.ts'. ========"
32+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== /src/a.ts ===
2+
3+
No type information for this code.export default 0;
4+
No type information for this code.
5+
No type information for this code.// No extension: '.ts' added
6+
No type information for this code.=== /src/b.ts ===
7+
import a from './a';
8+
>a : number
9+
10+
// Matching extension
11+
=== /src/c.ts ===
12+
import a from './a.ts';
13+
>a : number
14+
15+
// '.js' extension: stripped and replaced with '.ts'
16+
=== /src/d.ts ===
17+
import a from './a.js';
18+
>a : number
19+
20+
=== /src/jquery.d.ts ===
21+
declare var x: number;
22+
>x : number
23+
24+
export default x;
25+
>x : number
26+
27+
// No extension: '.d.ts' added
28+
=== /src/jquery_user_1.ts ===
29+
import j from "./jquery";
30+
>j : number
31+
32+
// '.js' extension: stripped and replaced with '.d.ts'
33+
=== /src/jquery_user_1.ts ===
34+
import j from "./jquery.js"
35+
>j : number
36+
>j : number
37+

tests/baselines/reference/nameWithFileExtension.errors.txt

-12
This file was deleted.

tests/baselines/reference/nameWithFileExtension.js

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import foo = require('./foo_0.js');
88
var x = foo.foo + 42;
99

1010

11+
//// [foo_0.js]
12+
"use strict";
13+
exports.foo = 42;
1114
//// [foo_1.js]
1215
"use strict";
1316
var foo = require('./foo_0.js');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/conformance/externalModules/foo_1.ts ===
2+
import foo = require('./foo_0.js');
3+
>foo : Symbol(foo, Decl(foo_1.ts, 0, 0))
4+
5+
var x = foo.foo + 42;
6+
>x : Symbol(x, Decl(foo_1.ts, 1, 3))
7+
>foo.foo : Symbol(foo.foo, Decl(foo_0.ts, 0, 10))
8+
>foo : Symbol(foo, Decl(foo_1.ts, 0, 0))
9+
>foo : Symbol(foo.foo, Decl(foo_0.ts, 0, 10))
10+
11+
=== tests/cases/conformance/externalModules/foo_0.ts ===
12+
export var foo = 42;
13+
>foo : Symbol(foo, Decl(foo_0.ts, 0, 10))
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/conformance/externalModules/foo_1.ts ===
2+
import foo = require('./foo_0.js');
3+
>foo : typeof foo
4+
5+
var x = foo.foo + 42;
6+
>x : number
7+
>foo.foo + 42 : number
8+
>foo.foo : number
9+
>foo : typeof foo
10+
>foo : number
11+
>42 : number
12+
13+
=== tests/cases/conformance/externalModules/foo_0.ts ===
14+
export var foo = 42;
15+
>foo : number
16+
>42 : number
17+

tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
decl.ts(1,26): error TS2307: Cannot find module './foo/bar.js'.
1+
decl.ts(1,26): error TS2307: Cannot find module './foo/bar.tx'.
22
decl.ts(2,26): error TS2307: Cannot find module 'baz'.
33
decl.ts(3,26): error TS2307: Cannot find module './baz'.
44

55

66
==== decl.ts (3 errors) ====
7-
import modErr = require("./foo/bar.js");
7+
import modErr = require("./foo/bar.tx");
88
~~~~~~~~~~~~~~
9-
!!! error TS2307: Cannot find module './foo/bar.js'.
9+
!!! error TS2307: Cannot find module './foo/bar.tx'.
1010
import modErr1 = require("baz");
1111
~~~~~
1212
!!! error TS2307: Cannot find module 'baz'.

tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
decl.ts(1,26): error TS2307: Cannot find module './foo/bar.js'.
1+
decl.ts(1,26): error TS2307: Cannot find module './foo/bar.tx'.
22
decl.ts(2,26): error TS2307: Cannot find module 'baz'.
33
decl.ts(3,26): error TS2307: Cannot find module './baz'.
44

55

66
==== decl.ts (3 errors) ====
7-
import modErr = require("./foo/bar.js");
7+
import modErr = require("./foo/bar.tx");
88
~~~~~~~~~~~~~~
9-
!!! error TS2307: Cannot find module './foo/bar.js'.
9+
!!! error TS2307: Cannot find module './foo/bar.tx'.
1010
import modErr1 = require("baz");
1111
~~~~~
1212
!!! error TS2307: Cannot find module 'baz'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @traceResolution: true
2+
3+
// @Filename: /src/a.ts
4+
export default 0;
5+
6+
// No extension: '.ts' added
7+
// @Filename: /src/b.ts
8+
import a from './a';
9+
10+
// Matching extension
11+
// @Filename: /src/c.ts
12+
import a from './a.ts';
13+
14+
// '.js' extension: stripped and replaced with '.ts'
15+
// @Filename: /src/d.ts
16+
import a from './a.js';
17+
18+
// @Filename: /src/jquery.d.ts
19+
declare var x: number;
20+
export default x;
21+
22+
// No extension: '.d.ts' added
23+
// @Filename: /src/jquery_user_1.ts
24+
import j from "./jquery";
25+
26+
// '.js' extension: stripped and replaced with '.d.ts'
27+
// @Filename: /src/jquery_user_1.ts
28+
import j from "./jquery.js"

tests/cases/projects/NoModule/decl.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import modErr = require("./foo/bar.js");
1+
import modErr = require("./foo/bar.tx");
22
import modErr1 = require("baz");
33
import modErr2 = require("./baz");
44

0 commit comments

Comments
 (0)