diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cc6a9e6db9a..7c125d72e99 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -900,9 +900,7 @@ namespace ts { } export function fileExtensionIs(path: string, extension: string): boolean { - const pathLen = path.length; - const extLen = extension.length; - return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; + return path.length > extension.length && endsWith(path, extension); } export function fileExtensionIsAny(path: string, extensions: string[]): boolean { @@ -915,7 +913,6 @@ namespace ts { return false; } - // Reserved characters, forces escaping of any non-word (or digit), non-whitespace character. // It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future // proof. diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 151bac9ed62..c2a1e6ade66 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -112,13 +112,7 @@ namespace ts { } function moduleHasNonRelativeName(moduleName: string): boolean { - if (isRootedDiskPath(moduleName)) { - return false; - } - - const i = moduleName.lastIndexOf("./", 1); - const startsWithDotSlashOrDotDotSlash = i === 0 || (i === 1 && moduleName.charCodeAt(0) === CharacterCodes.dot); - return !startsWithDotSlashOrDotDotSlash; + return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName)); } interface ModuleResolutionState { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6f3a1d6d813..f970a94373a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1218,7 +1218,7 @@ namespace ts { export function isExternalModuleNameRelative(moduleName: string): boolean { // TypeScript 1.0 spec (April 2014): 11.2.1 // An external module name is "relative" if the first term is "." or "..". - return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\"; + return /^\.\.?($|[\\/])/.test(moduleName); } export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { @@ -3120,6 +3120,6 @@ namespace ts { export function endsWith(str: string, suffix: string): boolean { const expectedPos = str.length - suffix.length; - return str.indexOf(suffix, expectedPos) === expectedPos; + return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos; } } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 6afb0cfccdb..534778fdab8 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1377,31 +1377,27 @@ namespace Harness { writeByteOrderMark: boolean; } - function stringEndsWith(str: string, end: string) { - return str.substr(str.length - end.length) === end; - } - export function isTS(fileName: string) { - return stringEndsWith(fileName, ".ts"); + return ts.endsWith(fileName, ".ts"); } export function isTSX(fileName: string) { - return stringEndsWith(fileName, ".tsx"); + return ts.endsWith(fileName, ".tsx"); } export function isDTS(fileName: string) { - return stringEndsWith(fileName, ".d.ts"); + return ts.endsWith(fileName, ".d.ts"); } export function isJS(fileName: string) { - return stringEndsWith(fileName, ".js"); + return ts.endsWith(fileName, ".js"); } export function isJSX(fileName: string) { - return stringEndsWith(fileName, ".jsx"); + return ts.endsWith(fileName, ".jsx"); } export function isJSMap(fileName: string) { - return stringEndsWith(fileName, ".js.map") || stringEndsWith(fileName, ".jsx.map"); + return ts.endsWith(fileName, ".js.map") || ts.endsWith(fileName, ".jsx.map"); } /** Contains the code and errors of a compilation and some helper methods to check its status. */ diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index 93cc5130d72..3d20337eca7 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -514,16 +514,6 @@ namespace ts { return str === str.toLowerCase(); } - function startsWith(string: string, search: string) { - for (let i = 0, n = search.length; i < n; i++) { - if (string.charCodeAt(i) !== search.charCodeAt(i)) { - return false; - } - } - - return true; - } - // Assumes 'value' is already lowercase. function indexOfIgnoringCase(string: string, value: string): number { for (let i = 0, n = string.length - value.length; i <= n; i++) { diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.js b/tests/baselines/reference/relativeModuleWithoutSlash.js new file mode 100644 index 00000000000..f83406499d4 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.js @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/relativeModuleWithoutSlash.ts] //// + +//// [a.ts] + +export default { a: 0 }; + +//// [index.ts] +export default { aIndex: 0 }; + +//// [test.ts] +import a from "."; +import aIndex from "./"; +a.a; +aIndex.a; //aIndex.aIndex; See GH#9690 + +//// [test.ts] +import a from ".."; +import aIndex from "../"; +a.a; +aIndex.a; //aIndex.aIndex; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { a: 0 }; +//// [index.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { aIndex: 0 }; +//// [test.js] +"use strict"; +var _1 = require("."); +var _2 = require("./"); +_1["default"].a; +_2["default"].a; //aIndex.aIndex; See GH#9690 +//// [test.js] +"use strict"; +var __1 = require(".."); +var _1 = require("../"); +__1["default"].a; +_1["default"].a; //aIndex.aIndex; diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.symbols b/tests/baselines/reference/relativeModuleWithoutSlash.symbols new file mode 100644 index 00000000000..434c3aa6bd7 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.symbols @@ -0,0 +1,43 @@ +=== tests/cases/compiler/a.ts === + +export default { a: 0 }; +>a : Symbol(a, Decl(a.ts, 1, 16)) + +=== tests/cases/compiler/a/index.ts === +export default { aIndex: 0 }; +>aIndex : Symbol(aIndex, Decl(index.ts, 0, 16)) + +=== tests/cases/compiler/a/test.ts === +import a from "."; +>a : Symbol(a, Decl(test.ts, 0, 6)) + +import aIndex from "./"; +>aIndex : Symbol(aIndex, Decl(test.ts, 1, 6)) + +a.a; +>a.a : Symbol(a, Decl(a.ts, 1, 16)) +>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 : Symbol(aIndex, Decl(test.ts, 1, 6)) +>a : Symbol(a, Decl(a.ts, 1, 16)) + +=== tests/cases/compiler/a/b/test.ts === +import a from ".."; +>a : Symbol(a, Decl(test.ts, 0, 6)) + +import aIndex from "../"; +>aIndex : Symbol(aIndex, Decl(test.ts, 1, 6)) + +a.a; +>a.a : Symbol(a, Decl(a.ts, 1, 16)) +>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 : Symbol(aIndex, Decl(test.ts, 1, 6)) +>a : Symbol(a, Decl(a.ts, 1, 16)) + diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.trace.json b/tests/baselines/reference/relativeModuleWithoutSlash.trace.json new file mode 100644 index 00000000000..765389e4629 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.trace.json @@ -0,0 +1,26 @@ +[ + "======== Resolving module '.' from 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a'.", + "File 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "Resolving real path for 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts', result 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'", + "======== Module name '.' was successfully resolved to 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './' from 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a'.", + "File 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "Resolving real path for 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts', result 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'", + "======== Module name './' was successfully resolved to 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module '..' from 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a/b/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a'.", + "File 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "Resolving real path for 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts', result 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'", + "======== Module name '..' was successfully resolved to 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module '../' from 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a/b/test.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a'.", + "File 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "Resolving real path for 'C:/Users/anhans/work/TypeScript/tests/cases/compiler/a.ts', result 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'", + "======== Module name '../' was successfully resolved to 'c:/users/anhans/work/typescript/tests/cases/compiler/a.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.types b/tests/baselines/reference/relativeModuleWithoutSlash.types new file mode 100644 index 00000000000..3b58f097405 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.types @@ -0,0 +1,47 @@ +=== tests/cases/compiler/a.ts === + +export default { a: 0 }; +>{ a: 0 } : { a: number; } +>a : number +>0 : number + +=== tests/cases/compiler/a/index.ts === +export default { aIndex: 0 }; +>{ aIndex: 0 } : { aIndex: number; } +>aIndex : number +>0 : number + +=== tests/cases/compiler/a/test.ts === +import a from "."; +>a : { a: number; } + +import aIndex from "./"; +>aIndex : { a: 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 + +=== tests/cases/compiler/a/b/test.ts === +import a from ".."; +>a : { a: number; } + +import aIndex from "../"; +>aIndex : { a: number; } + +a.a; +>a.a : number +>a : { a: number; } +>a : number + +aIndex.a; //aIndex.aIndex; +>aIndex.a : number +>aIndex : { a: number; } +>a : number + diff --git a/tests/cases/compiler/relativeModuleWithoutSlash.ts b/tests/cases/compiler/relativeModuleWithoutSlash.ts new file mode 100644 index 00000000000..3b4f50da306 --- /dev/null +++ b/tests/cases/compiler/relativeModuleWithoutSlash.ts @@ -0,0 +1,20 @@ +// @traceResolution: true +// @moduleResolution: node + +// @Filename: a.ts +export default { a: 0 }; + +// @Filename: a/index.ts +export default { aIndex: 0 }; + +// @Filename: a/test.ts +import a from "."; +import aIndex from "./"; +a.a; +aIndex.a; //aIndex.aIndex; See GH#9690 + +// @Filename: a/b/test.ts +import a from ".."; +import aIndex from "../"; +a.a; +aIndex.a; //aIndex.aIndex;