Skip to content

Commit 448a480

Browse files
author
Andy Hanson
committed
Don't allow .ts to appear in an import
1 parent f24341f commit 448a480

16 files changed

+77
-62
lines changed

src/compiler/program.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -668,51 +668,62 @@ namespace ts {
668668
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
669669
* 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.
670670
*/
671-
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
672-
// First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts"
673-
const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
674-
if (resolvedByAddingOrKeepingExtension) {
675-
return resolvedByAddingOrKeepingExtension;
671+
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
672+
// If the candidate already has an extension load that or quit.
673+
if (hasTypeScriptFileExtension(candidate)) {
674+
// Don't allow `.ts` to appear at the end
675+
if (!fileExtensionIs(candidate, ".d.ts")) {
676+
return undefined;
677+
}
678+
return tryFile(candidate, failedLookupLocation, onlyRecordFailures, state);
676679
}
677-
// 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"
680+
681+
// Next, try adding an extension.
682+
// We don't allow an import of "foo.ts" to be matched by "foo.ts.ts", but we do allow "foo.js" to be matched by "foo.js.ts".
683+
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
684+
if (resolvedByAddingExtension) {
685+
return resolvedByAddingExtension;
686+
}
687+
688+
// If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
689+
// e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
678690
if (hasJavaScriptFileExtension(candidate)) {
679691
const extensionless = removeFileExtension(candidate);
680692
if (state.traceEnabled) {
681693
const extension = candidate.substring(extensionless.length);
682694
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
683695
}
684-
return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
696+
return tryAddingExtensions(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
685697
}
686698
}
687699

688-
function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
700+
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
701+
function tryAddingExtensions(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
689702
if (!onlyRecordFailures) {
690703
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
691704
const directory = getDirectoryPath(candidate);
692705
if (directory) {
693706
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
694707
}
695708
}
696-
return forEach(extensions, tryLoad);
709+
return forEach(extensions, ext =>
710+
!(state.skipTsx && isJsxOrTsxExtension(ext)) && tryFile(candidate + ext, failedLookupLocation, onlyRecordFailures, state));
711+
}
697712

698-
function tryLoad(ext: string): string {
699-
if (state.skipTsx && isJsxOrTsxExtension(ext)) {
700-
return undefined;
701-
}
702-
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
703-
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
704-
if (state.traceEnabled) {
705-
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
706-
}
707-
return fileName;
713+
/** Return the file if it exists. */
714+
function tryFile(fileName: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
715+
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
716+
if (state.traceEnabled) {
717+
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
708718
}
709-
else {
710-
if (state.traceEnabled) {
711-
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
712-
}
713-
failedLookupLocation.push(fileName);
714-
return undefined;
719+
return fileName;
720+
}
721+
else {
722+
if (state.traceEnabled) {
723+
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
715724
}
725+
failedLookupLocation.push(fileName);
726+
return undefined;
716727
}
717728
}
718729

src/harness/unittests/moduleResolution.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,8 @@ export = C;
465465
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
466466
"/a/B/c/moduleC.ts": "export var x",
467467
"/a/B/c/moduleD.ts": `
468-
import a = require("./moduleA.ts");
469-
import b = require("./moduleB.ts");
468+
import a = require("./moduleA");
469+
import b = require("./moduleB");
470470
`
471471
};
472472
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]);
@@ -477,8 +477,8 @@ import b = require("./moduleB.ts");
477477
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
478478
"/a/B/c/moduleC.ts": "export var x",
479479
"/a/B/c/moduleD.ts": `
480-
import a = require("./moduleA.ts");
481-
import b = require("./moduleB.ts");
480+
import a = require("./moduleA");
481+
import b = require("./moduleB");
482482
`
483483
};
484484
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []);

tests/baselines/reference/missingFunctionImplementation2.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tests/cases/compiler/missingFunctionImplementation2_b.ts(1,17): error TS2391: Fu
44

55
==== tests/cases/compiler/missingFunctionImplementation2_a.ts (1 errors) ====
66
export {};
7-
declare module "./missingFunctionImplementation2_b.ts" {
7+
declare module "./missingFunctionImplementation2_b" {
88
export function f(a, b): void;
99
~
1010
!!! error TS2384: Overload signatures must all be ambient or non-ambient.

tests/baselines/reference/missingFunctionImplementation2.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
//// [missingFunctionImplementation2_a.ts]
44
export {};
5-
declare module "./missingFunctionImplementation2_b.ts" {
5+
declare module "./missingFunctionImplementation2_b" {
66
export function f(a, b): void;
77
}
88

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
tests/cases/compiler/user.ts(1,15): error TS2307: Cannot find module './m.ts'.
2+
3+
4+
==== tests/cases/compiler/m.ts (0 errors) ====
5+
export default 0;
6+
7+
==== tests/cases/compiler/user.ts (1 errors) ====
8+
import x from "./m.ts";
9+
~~~~~~~~
10+
!!! error TS2307: Cannot find module './m.ts'.
11+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [tests/cases/compiler/moduleResolutionNoTs.ts] ////
2+
3+
//// [m.ts]
4+
export default 0;
5+
6+
//// [user.ts]
7+
import x from "./m.ts";
8+
9+
10+
//// [m.js]
11+
"use strict";
12+
exports.__esModule = true;
13+
exports["default"] = 0;
14+
//// [user.js]
15+
"use strict";

tests/baselines/reference/moduleResolutionWithExtensions.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ export default 0;
88
//// [b.ts]
99
import a from './a';
1010

11-
// Matching extension
12-
//// [c.ts]
13-
import a from './a.ts';
14-
1511
// '.js' extension: stripped and replaced with '.ts'
1612
//// [d.ts]
1713
import a from './a.js';
@@ -36,9 +32,6 @@ exports["default"] = 0;
3632
// No extension: '.ts' added
3733
//// [b.js]
3834
"use strict";
39-
// Matching extension
40-
//// [c.js]
41-
"use strict";
4235
// '.js' extension: stripped and replaced with '.ts'
4336
//// [d.js]
4437
"use strict";

tests/baselines/reference/moduleResolutionWithExtensions.symbols

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ No type information for this code.=== /src/b.ts ===
77
import a from './a';
88
>a : Symbol(a, Decl(b.ts, 0, 6))
99

10-
// Matching extension
11-
=== /src/c.ts ===
12-
import a from './a.ts';
13-
>a : Symbol(a, Decl(c.ts, 0, 6))
14-
1510
// '.js' extension: stripped and replaced with '.ts'
1611
=== /src/d.ts ===
1712
import a from './a.js';

tests/baselines/reference/moduleResolutionWithExtensions.trace.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@
55
"File '/src/a.ts' exist - use it as a name resolution result.",
66
"Resolving real path for '/src/a.ts', result '/src/a.ts'",
77
"======== Module name './a' was successfully resolved to '/src/a.ts'. ========",
8-
"======== Resolving module './a.ts' from '/src/c.ts'. ========",
9-
"Module resolution kind is not specified, using 'NodeJs'.",
10-
"Loading module as file / folder, candidate module location '/src/a.ts'.",
11-
"File '/src/a.ts' exist - use it as a name resolution result.",
12-
"Resolving real path for '/src/a.ts', result '/src/a.ts'",
13-
"======== Module name './a.ts' was successfully resolved to '/src/a.ts'. ========",
148
"======== Resolving module './a.js' from '/src/d.ts'. ========",
159
"Module resolution kind is not specified, using 'NodeJs'.",
1610
"Loading module as file / folder, candidate module location '/src/a.js'.",

tests/baselines/reference/moduleResolutionWithExtensions.types

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ No type information for this code.=== /src/b.ts ===
77
import a from './a';
88
>a : number
99

10-
// Matching extension
11-
=== /src/c.ts ===
12-
import a from './a.ts';
13-
>a : number
14-
1510
// '.js' extension: stripped and replaced with '.ts'
1611
=== /src/d.ts ===
1712
import a from './a.js';

tests/baselines/reference/staticInstanceResolution5.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tests/cases/compiler/staticInstanceResolution5_1.ts(6,16): error TS2304: Cannot
44

55

66
==== tests/cases/compiler/staticInstanceResolution5_1.ts (3 errors) ====
7-
import WinJS = require('staticInstanceResolution5_0.ts');
7+
import WinJS = require('staticInstanceResolution5_0');
88

99
// these 3 should be errors
1010
var x = (w1: WinJS) => { };

tests/baselines/reference/staticInstanceResolution5.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export class Promise {
88
}
99

1010
//// [staticInstanceResolution5_1.ts]
11-
import WinJS = require('staticInstanceResolution5_0.ts');
11+
import WinJS = require('staticInstanceResolution5_0');
1212

1313
// these 3 should be errors
1414
var x = (w1: WinJS) => { };

tests/cases/compiler/missingFunctionImplementation2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @Filename: missingFunctionImplementation2_a.ts
22
export {};
3-
declare module "./missingFunctionImplementation2_b.ts" {
3+
declare module "./missingFunctionImplementation2_b" {
44
export function f(a, b): void;
55
}
66

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// @filename: m.ts
2+
export default 0;
3+
4+
// @filename: user.ts
5+
import x from "./m.ts";

tests/cases/compiler/staticInstanceResolution5.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class Promise {
77
}
88

99
// @Filename: staticInstanceResolution5_1.ts
10-
import WinJS = require('staticInstanceResolution5_0.ts');
10+
import WinJS = require('staticInstanceResolution5_0');
1111

1212
// these 3 should be errors
1313
var x = (w1: WinJS) => { };

tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ export default 0;
77
// @Filename: /src/b.ts
88
import a from './a';
99

10-
// Matching extension
11-
// @Filename: /src/c.ts
12-
import a from './a.ts';
13-
1410
// '.js' extension: stripped and replaced with '.ts'
1511
// @Filename: /src/d.ts
1612
import a from './a.js';

0 commit comments

Comments
 (0)