From c90897ccdddfd35b119de9472980a65efec40071 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 12 Jul 2016 14:54:06 -0700 Subject: [PATCH 1/5] Treat "." and ".." as relative module names --- src/compiler/core.ts | 15 ++++++++--- src/compiler/program.ts | 8 +----- src/compiler/utilities.ts | 18 ++++++++++++- .../reference/relativeModuleWithoutSlash.js | 27 +++++++++++++++++++ .../relativeModuleWithoutSlash.symbols | 17 ++++++++++++ .../relativeModuleWithoutSlash.types | 18 +++++++++++++ .../compiler/relativeModuleWithoutSlash.ts | 10 +++++++ 7 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/relativeModuleWithoutSlash.js create mode 100644 tests/baselines/reference/relativeModuleWithoutSlash.symbols create mode 100644 tests/baselines/reference/relativeModuleWithoutSlash.types create mode 100644 tests/cases/compiler/relativeModuleWithoutSlash.ts 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; From e6e6a8b1102d1ee5aca429350d5c22e6c0ddd1c9 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 13 Jul 2016 09:55:57 -0700 Subject: [PATCH 2/5] Use regex --- src/compiler/utilities.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8dbc052ddcf..4a8391338c3 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1218,23 +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 "..". - 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; + return /^\.\.?($|[\\/])/.test(moduleName); } export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { From 8e679b7021ba0b1b078826647dc86fe5b676b322 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 13 Jul 2016 10:00:34 -0700 Subject: [PATCH 3/5] Remove duplicate startsWith and endsWith functions --- src/compiler/core.ts | 14 +------------- src/harness/harness.ts | 16 ++++++---------- src/services/patternMatcher.ts | 10 ---------- 3 files changed, 7 insertions(+), 33 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3a7765094a1..7c125d72e99 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -900,7 +900,7 @@ namespace ts { } export function fileExtensionIs(path: string, extension: string): boolean { - return stringEndsWith(path, extension); + return path.length > extension.length && endsWith(path, extension); } export function fileExtensionIsAny(path: string, extensions: string[]): boolean { @@ -913,18 +913,6 @@ 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 // proof. 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++) { From 919e31a264a1607b5fb978f72c81fc030047bcc4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 13 Jul 2016 11:26:24 -0700 Subject: [PATCH 4/5] Include resolution trace --- .../reference/relativeModuleWithoutSlash.js | 53 +++++++++++------ .../relativeModuleWithoutSlash.symbols | 54 ++++++++++++----- .../relativeModuleWithoutSlash.trace.json | 26 ++++++++ .../relativeModuleWithoutSlash.types | 59 ++++++++++++++----- .../compiler/relativeModuleWithoutSlash.ts | 24 +++++--- 5 files changed, 161 insertions(+), 55 deletions(-) create mode 100644 tests/baselines/reference/relativeModuleWithoutSlash.trace.json diff --git a/tests/baselines/reference/relativeModuleWithoutSlash.js b/tests/baselines/reference/relativeModuleWithoutSlash.js index de0a03c7923..f83406499d4 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.js +++ b/tests/baselines/reference/relativeModuleWithoutSlash.js @@ -1,27 +1,42 @@ //// [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; + +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; -//// [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(".."); +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("."); -__1["default"] + _1["default"]; +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 index 280a844f2f7..434c3aa6bd7 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.symbols +++ b/tests/baselines/reference/relativeModuleWithoutSlash.symbols @@ -1,17 +1,43 @@ -=== 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)) +=== tests/cases/compiler/a.ts === -import here from "."; ->here : Symbol(here, Decl(a.ts, 1, 6)) +export default { a: 0 }; +>a : Symbol(a, Decl(a.ts, 1, 16)) -parent + here; ->parent : Symbol(parent, Decl(a.ts, 0, 6)) ->here : Symbol(here, Decl(a.ts, 1, 6)) +=== 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 index 3093aa2e7c6..3b58f097405 100644 --- a/tests/baselines/reference/relativeModuleWithoutSlash.types +++ b/tests/baselines/reference/relativeModuleWithoutSlash.types @@ -1,18 +1,47 @@ -=== 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 +=== tests/cases/compiler/a.ts === -import here from "."; ->here : number +export default { a: 0 }; +>{ a: 0 } : { a: number; } +>a : number +>0 : number -parent + here; ->parent + here : number ->parent : number ->here : 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 index 35ad2ae9c6a..3b4f50da306 100644 --- a/tests/cases/compiler/relativeModuleWithoutSlash.ts +++ b/tests/cases/compiler/relativeModuleWithoutSlash.ts @@ -1,10 +1,20 @@ -// @Filename: index.ts -export default 0; +// @traceResolution: true +// @moduleResolution: node + +// @Filename: a.ts +export default { a: 0 }; // @Filename: a/index.ts -export default 1; +export default { aIndex: 0 }; -// @Filename: a/a.ts -import parent from ".."; -import here from "."; -parent + here; +// @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; From df590588125e5a8e362ed35f1c4acf46f8499977 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 13 Jul 2016 11:47:53 -0700 Subject: [PATCH 5/5] Fix endsWith bug --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4a8391338c3..f970a94373a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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; } }