diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cc6a9e6db9a..3a7765094a1 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 stringEndsWith(path, extension); } export function fileExtensionIsAny(path: string, extensions: string[]): boolean { @@ -915,6 +913,17 @@ namespace ts { return false; } + // Should act like String.prototype.startsWith + export function stringStartsWith(s: string, start: string): boolean { + return s.length > start.length && s.substr(0, start.length) === start; + } + + // Should act like String.prototype.endsWith + export function stringEndsWith(s: string, end: string): boolean { + const sLen = s.length; + const endLen = end.length; + return sLen > endLen && s.substr(sLen - endLen, endLen) === end; + } // 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 diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 320b628c31a..3f36ea8b1c1 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..8dbc052ddcf 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1218,7 +1218,23 @@ 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) === "..\\"; + if (moduleName.charCodeAt(0) === CharacterCodes.dot) { + if (moduleName.length === 1) { + return true; + } + switch (moduleName.charCodeAt(1)) { + case CharacterCodes.slash: + case CharacterCodes.backslash: + return true; + case CharacterCodes.dot: + if (moduleName.length === 2) { + return true; + } + const ch2 = moduleName.charCodeAt(2); + return ch2 === CharacterCodes.slash || ch2 === CharacterCodes.backslash; + } + } + return false; } export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.js b/tests/baselines/reference/relativeModuleWithoutSlash.js new file mode 100644 index 00000000000..de0a03c7923 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/relativeModuleWithoutSlash.ts] //// + +//// [index.ts] +export default 0; + +//// [index.ts] +export default 1; + +//// [a.ts] +import parent from ".."; +import here from "."; +parent + here; + + +//// [index.js] +"use strict"; +exports.__esModule = true; +exports["default"] = 0; +//// [index.js] +"use strict"; +exports.__esModule = true; +exports["default"] = 1; +//// [a.js] +"use strict"; +var __1 = require(".."); +var _1 = require("."); +__1["default"] + _1["default"]; diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.symbols b/tests/baselines/reference/relativeModuleWithoutSlash.symbols new file mode 100644 index 00000000000..280a844f2f7 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.symbols @@ -0,0 +1,17 @@ +=== tests/cases/compiler/index.ts === +export default 0; +No type information for this code. +No type information for this code.=== tests/cases/compiler/a/index.ts === +export default 1; +No type information for this code. +No type information for this code.=== tests/cases/compiler/a/a.ts === +import parent from ".."; +>parent : Symbol(parent, Decl(a.ts, 0, 6)) + +import here from "."; +>here : Symbol(here, Decl(a.ts, 1, 6)) + +parent + here; +>parent : Symbol(parent, Decl(a.ts, 0, 6)) +>here : Symbol(here, Decl(a.ts, 1, 6)) + diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.types b/tests/baselines/reference/relativeModuleWithoutSlash.types new file mode 100644 index 00000000000..3093aa2e7c6 --- /dev/null +++ b/tests/baselines/reference/relativeModuleWithoutSlash.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/index.ts === +export default 0; +No type information for this code. +No type information for this code.=== tests/cases/compiler/a/index.ts === +export default 1; +No type information for this code. +No type information for this code.=== tests/cases/compiler/a/a.ts === +import parent from ".."; +>parent : number + +import here from "."; +>here : number + +parent + here; +>parent + here : number +>parent : number +>here : number + diff --git a/tests/cases/compiler/relativeModuleWithoutSlash.ts b/tests/cases/compiler/relativeModuleWithoutSlash.ts new file mode 100644 index 00000000000..35ad2ae9c6a --- /dev/null +++ b/tests/cases/compiler/relativeModuleWithoutSlash.ts @@ -0,0 +1,10 @@ +// @Filename: index.ts +export default 0; + +// @Filename: a/index.ts +export default 1; + +// @Filename: a/a.ts +import parent from ".."; +import here from "."; +parent + here;