diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6d1330e962662..72d05e1cfd7c5 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1044,7 +1044,8 @@ namespace ts { return 0; } - export let directorySeparator = "/"; + export const directorySeparator = "/"; + const directorySeparatorCharCode = CharacterCodes.slash; function getNormalizedParts(normalizedSlashedPath: string, rootLength: number) { const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator); const normalized: string[] = []; @@ -1069,8 +1070,20 @@ namespace ts { export function normalizePath(path: string): string { path = normalizeSlashes(path); const rootLength = getRootLength(path); + const root = path.substr(0, rootLength); const normalized = getNormalizedParts(path, rootLength); - return path.substr(0, rootLength) + normalized.join(directorySeparator); + if (normalized.length) { + const joinedParts = root + normalized.join(directorySeparator); + return pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts; + } + else { + return root; + } + } + + /** A path ending with '/' refers to a directory only, never a file. */ + export function pathEndsWithDirectorySeparator(path: string): boolean { + return path.charCodeAt(path.length - 1) === directorySeparatorCharCode; } export function getDirectoryPath(path: Path): Path; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7c010285b7059..ab306e90f3a29 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -670,7 +670,7 @@ namespace ts { trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate); } - const resolvedFileName = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state); + const resolvedFileName = !pathEndsWithDirectorySeparator(candidate) && loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state); return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state); } diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.js b/tests/baselines/reference/importWithTrailingSlash.js similarity index 70% rename from tests/baselines/reference/relativeModuleWithoutSlash.js rename to tests/baselines/reference/importWithTrailingSlash.js index f83406499d485..7118dd95bb821 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.js +++ b/tests/baselines/reference/importWithTrailingSlash.js @@ -1,4 +1,4 @@ -//// [tests/cases/compiler/relativeModuleWithoutSlash.ts] //// +//// [tests/cases/compiler/importWithTrailingSlash.ts] //// //// [a.ts] @@ -11,13 +11,13 @@ export default { aIndex: 0 }; import a from "."; import aIndex from "./"; a.a; -aIndex.a; //aIndex.aIndex; See GH#9690 +aIndex.aIndex; //// [test.ts] import a from ".."; import aIndex from "../"; a.a; -aIndex.a; //aIndex.aIndex; +aIndex.aIndex; //// [a.js] @@ -33,10 +33,10 @@ exports["default"] = { aIndex: 0 }; var _1 = require("."); var _2 = require("./"); _1["default"].a; -_2["default"].a; //aIndex.aIndex; See GH#9690 +_2["default"].aIndex; //// [test.js] "use strict"; var __1 = require(".."); var _1 = require("../"); __1["default"].a; -_1["default"].a; //aIndex.aIndex; +_1["default"].aIndex; diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.trace.json b/tests/baselines/reference/importWithTrailingSlash.json similarity index 71% rename from tests/baselines/reference/relativeModuleWithoutSlash.trace.json rename to tests/baselines/reference/importWithTrailingSlash.json index cee5b0606766e..3c99d4eb6a010 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.trace.json +++ b/tests/baselines/reference/importWithTrailingSlash.json @@ -7,10 +7,11 @@ "======== Module name '.' was successfully resolved to '/a.ts'. ========", "======== Resolving module './' from '/a/test.ts'. ========", "Explicitly specified module resolution kind: 'NodeJs'.", - "Loading module as file / folder, candidate module location '/a'.", - "File '/a.ts' exist - use it as a name resolution result.", - "Resolving real path for '/a.ts', result '/a.ts'", - "======== Module name './' was successfully resolved to '/a.ts'. ========", + "Loading module as file / folder, candidate module location '/a/'.", + "File '/a/package.json' does not exist.", + "File '/a/index.ts' exist - use it as a name resolution result.", + "Resolving real path for '/a/index.ts', result '/a/index.ts'", + "======== Module name './' was successfully resolved to '/a/index.ts'. ========", "======== Resolving module '..' from '/a/b/test.ts'. ========", "Explicitly specified module resolution kind: 'NodeJs'.", "Loading module as file / folder, candidate module location '/a'.", @@ -19,8 +20,9 @@ "======== Module name '..' was successfully resolved to '/a.ts'. ========", "======== Resolving module '../' from '/a/b/test.ts'. ========", "Explicitly specified module resolution kind: 'NodeJs'.", - "Loading module as file / folder, candidate module location '/a'.", - "File '/a.ts' exist - use it as a name resolution result.", - "Resolving real path for '/a.ts', result '/a.ts'", - "======== Module name '../' was successfully resolved to '/a.ts'. ========" + "Loading module as file / folder, candidate module location '/a/'.", + "File '/a/package.json' does not exist.", + "File '/a/index.ts' exist - use it as a name resolution result.", + "Resolving real path for '/a/index.ts', result '/a/index.ts'", + "======== Module name '../' was successfully resolved to '/a/index.ts'. ========" ] \ No newline at end of file diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.symbols b/tests/baselines/reference/importWithTrailingSlash.symbols similarity index 73% rename from tests/baselines/reference/relativeModuleWithoutSlash.symbols rename to tests/baselines/reference/importWithTrailingSlash.symbols index 8c54a0682c7ef..4a9f8b415698f 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.symbols +++ b/tests/baselines/reference/importWithTrailingSlash.symbols @@ -19,10 +19,10 @@ a.a; >a : Symbol(a, Decl(test.ts, 0, 6)) >a : Symbol(a, Decl(a.ts, 1, 16)) -aIndex.a; //aIndex.aIndex; See GH#9690 ->aIndex.a : Symbol(a, Decl(a.ts, 1, 16)) +aIndex.aIndex; +>aIndex.aIndex : Symbol(aIndex, Decl(index.ts, 0, 16)) >aIndex : Symbol(aIndex, Decl(test.ts, 1, 6)) ->a : Symbol(a, Decl(a.ts, 1, 16)) +>aIndex : Symbol(aIndex, Decl(index.ts, 0, 16)) === /a/b/test.ts === import a from ".."; @@ -36,8 +36,8 @@ a.a; >a : Symbol(a, Decl(test.ts, 0, 6)) >a : Symbol(a, Decl(a.ts, 1, 16)) -aIndex.a; //aIndex.aIndex; ->aIndex.a : Symbol(a, Decl(a.ts, 1, 16)) +aIndex.aIndex; +>aIndex.aIndex : Symbol(aIndex, Decl(index.ts, 0, 16)) >aIndex : Symbol(aIndex, Decl(test.ts, 1, 6)) ->a : Symbol(a, Decl(a.ts, 1, 16)) +>aIndex : Symbol(aIndex, Decl(index.ts, 0, 16)) diff --git a/tests/baselines/reference/importWithTrailingSlash.trace.json b/tests/baselines/reference/importWithTrailingSlash.trace.json new file mode 100644 index 0000000000000..3c99d4eb6a010 --- /dev/null +++ b/tests/baselines/reference/importWithTrailingSlash.trace.json @@ -0,0 +1,28 @@ +[ + "======== Resolving module '.' from '/a/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location '/a'.", + "File '/a.ts' exist - use it as a name resolution result.", + "Resolving real path for '/a.ts', result '/a.ts'", + "======== Module name '.' was successfully resolved to '/a.ts'. ========", + "======== Resolving module './' from '/a/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location '/a/'.", + "File '/a/package.json' does not exist.", + "File '/a/index.ts' exist - use it as a name resolution result.", + "Resolving real path for '/a/index.ts', result '/a/index.ts'", + "======== Module name './' was successfully resolved to '/a/index.ts'. ========", + "======== Resolving module '..' from '/a/b/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location '/a'.", + "File '/a.ts' exist - use it as a name resolution result.", + "Resolving real path for '/a.ts', result '/a.ts'", + "======== Module name '..' was successfully resolved to '/a.ts'. ========", + "======== Resolving module '../' from '/a/b/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location '/a/'.", + "File '/a/package.json' does not exist.", + "File '/a/index.ts' exist - use it as a name resolution result.", + "Resolving real path for '/a/index.ts', result '/a/index.ts'", + "======== Module name '../' was successfully resolved to '/a/index.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.types b/tests/baselines/reference/importWithTrailingSlash.types similarity index 63% rename from tests/baselines/reference/relativeModuleWithoutSlash.types rename to tests/baselines/reference/importWithTrailingSlash.types index f17185e1e18a3..7429dcdf42e82 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.types +++ b/tests/baselines/reference/importWithTrailingSlash.types @@ -16,32 +16,32 @@ import a from "."; >a : { a: number; } import aIndex from "./"; ->aIndex : { a: number; } +>aIndex : { aIndex: number; } a.a; >a.a : number >a : { a: number; } >a : number -aIndex.a; //aIndex.aIndex; See GH#9690 ->aIndex.a : number ->aIndex : { a: number; } ->a : number +aIndex.aIndex; +>aIndex.aIndex : number +>aIndex : { aIndex: number; } +>aIndex : number === /a/b/test.ts === import a from ".."; >a : { a: number; } import aIndex from "../"; ->aIndex : { a: number; } +>aIndex : { aIndex: number; } a.a; >a.a : number >a : { a: number; } >a : number -aIndex.a; //aIndex.aIndex; ->aIndex.a : number ->aIndex : { a: number; } ->a : number +aIndex.aIndex; +>aIndex.aIndex : number +>aIndex : { aIndex: number; } +>aIndex : number diff --git a/tests/baselines/reference/importWithTrailingSlash_noResolve.errors.txt b/tests/baselines/reference/importWithTrailingSlash_noResolve.errors.txt new file mode 100644 index 0000000000000..780e1d2f1bc9c --- /dev/null +++ b/tests/baselines/reference/importWithTrailingSlash_noResolve.errors.txt @@ -0,0 +1,9 @@ +/a.ts(2,17): error TS2307: Cannot find module './foo/'. + + +==== /a.ts (1 errors) ==== + + import foo from "./foo/"; + ~~~~~~~~ +!!! error TS2307: Cannot find module './foo/'. + \ No newline at end of file diff --git a/tests/baselines/reference/importWithTrailingSlash_noResolve.js b/tests/baselines/reference/importWithTrailingSlash_noResolve.js new file mode 100644 index 0000000000000..b523c76af3b43 --- /dev/null +++ b/tests/baselines/reference/importWithTrailingSlash_noResolve.js @@ -0,0 +1,7 @@ +//// [a.ts] + +import foo from "./foo/"; + + +//// [a.js] +"use strict"; diff --git a/tests/baselines/reference/importWithTrailingSlash_noResolve.trace.json b/tests/baselines/reference/importWithTrailingSlash_noResolve.trace.json new file mode 100644 index 0000000000000..e010603106f4c --- /dev/null +++ b/tests/baselines/reference/importWithTrailingSlash_noResolve.trace.json @@ -0,0 +1,10 @@ +[ + "======== Resolving module './foo/' from '/a.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location '/foo/'.", + "File '/foo/package.json' does not exist.", + "File '/foo/index.ts' does not exist.", + "File '/foo/index.tsx' does not exist.", + "File '/foo/index.d.ts' does not exist.", + "======== Module name './foo/' was not resolved. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution7_classic.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution7_classic.trace.json index 2d72108210e21..a8cadb8f0671a 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution7_classic.trace.json +++ b/tests/baselines/reference/pathMappingBasedModuleResolution7_classic.trace.json @@ -14,7 +14,7 @@ "======== Module name './project/file2' was successfully resolved to 'c:/root/generated/src/project/file2.ts'. ========", "======== Resolving module 'module3' from 'c:/root/src/file1.ts'. ========", "Module resolution kind is not specified, using 'Classic'.", - "'baseUrl' option is set to 'c:/root', using this value to resolve non-relative module name 'module3'", + "'baseUrl' option is set to 'c:/root/', using this value to resolve non-relative module name 'module3'", "'paths' option is specified, looking for a pattern to match module name 'module3'.", "Module name 'module3', matched pattern '*'.", "Trying substitution '*', candidate module location: 'module3'.", @@ -32,7 +32,7 @@ "======== Module name 'module3' was successfully resolved to 'c:/module3.d.ts'. ========", "======== Resolving module 'module1' from 'c:/root/generated/src/project/file2.ts'. ========", "Module resolution kind is not specified, using 'Classic'.", - "'baseUrl' option is set to 'c:/root', using this value to resolve non-relative module name 'module1'", + "'baseUrl' option is set to 'c:/root/', using this value to resolve non-relative module name 'module1'", "'paths' option is specified, looking for a pattern to match module name 'module1'.", "Module name 'module1', matched pattern '*'.", "Trying substitution '*', candidate module location: 'module1'.", @@ -44,7 +44,7 @@ "======== Module name 'module1' was successfully resolved to 'c:/shared/module1.d.ts'. ========", "======== Resolving module 'templates/module2' from 'c:/root/generated/src/project/file2.ts'. ========", "Module resolution kind is not specified, using 'Classic'.", - "'baseUrl' option is set to 'c:/root', using this value to resolve non-relative module name 'templates/module2'", + "'baseUrl' option is set to 'c:/root/', using this value to resolve non-relative module name 'templates/module2'", "'paths' option is specified, looking for a pattern to match module name 'templates/module2'.", "Module name 'templates/module2', matched pattern 'templates/*'.", "Trying substitution 'generated/src/templates/*', candidate module location: 'generated/src/templates/module2'.", diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution7_node.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution7_node.trace.json index 6be7d349fed69..48633c85e3b48 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution7_node.trace.json +++ b/tests/baselines/reference/pathMappingBasedModuleResolution7_node.trace.json @@ -22,7 +22,7 @@ "======== Module name './project/file2' was successfully resolved to 'c:/root/generated/src/project/file2.ts'. ========", "======== Resolving module 'module3' from 'c:/root/src/file1.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "'baseUrl' option is set to 'c:/root', using this value to resolve non-relative module name 'module3'", + "'baseUrl' option is set to 'c:/root/', using this value to resolve non-relative module name 'module3'", "'paths' option is specified, looking for a pattern to match module name 'module3'.", "Module name 'module3', matched pattern '*'.", "Trying substitution '*', candidate module location: 'module3'.", @@ -79,7 +79,7 @@ "======== Module name 'module3' was successfully resolved to 'c:/node_modules/module3.d.ts'. ========", "======== Resolving module 'module1' from 'c:/root/generated/src/project/file2.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "'baseUrl' option is set to 'c:/root', using this value to resolve non-relative module name 'module1'", + "'baseUrl' option is set to 'c:/root/', using this value to resolve non-relative module name 'module1'", "'paths' option is specified, looking for a pattern to match module name 'module1'.", "Module name 'module1', matched pattern '*'.", "Trying substitution '*', candidate module location: 'module1'.", @@ -104,7 +104,7 @@ "======== Module name 'module1' was successfully resolved to 'c:/shared/module1/index.d.ts'. ========", "======== Resolving module 'templates/module2' from 'c:/root/generated/src/project/file2.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "'baseUrl' option is set to 'c:/root', using this value to resolve non-relative module name 'templates/module2'", + "'baseUrl' option is set to 'c:/root/', using this value to resolve non-relative module name 'templates/module2'", "'paths' option is specified, looking for a pattern to match module name 'templates/module2'.", "Module name 'templates/module2', matched pattern 'templates/*'.", "Trying substitution 'generated/src/templates/*', candidate module location: 'generated/src/templates/module2'.", diff --git a/tests/cases/compiler/relativeModuleWithoutSlash.ts b/tests/cases/compiler/importWithTrailingSlash.ts similarity index 78% rename from tests/cases/compiler/relativeModuleWithoutSlash.ts rename to tests/cases/compiler/importWithTrailingSlash.ts index 42b328e11755c..a5bceff8b13a0 100644 --- a/tests/cases/compiler/relativeModuleWithoutSlash.ts +++ b/tests/cases/compiler/importWithTrailingSlash.ts @@ -11,10 +11,10 @@ export default { aIndex: 0 }; import a from "."; import aIndex from "./"; a.a; -aIndex.a; //aIndex.aIndex; See GH#9690 +aIndex.aIndex; // @Filename: /a/b/test.ts import a from ".."; import aIndex from "../"; a.a; -aIndex.a; //aIndex.aIndex; +aIndex.aIndex; diff --git a/tests/cases/compiler/importWithTrailingSlash_noResolve.ts b/tests/cases/compiler/importWithTrailingSlash_noResolve.ts new file mode 100644 index 0000000000000..95541adfb114d --- /dev/null +++ b/tests/cases/compiler/importWithTrailingSlash_noResolve.ts @@ -0,0 +1,5 @@ +// @traceResolution: true +// @moduleResolution: node + +// @Filename: /a.ts +import foo from "./foo/";