From 1c64615e32466d0e9f71a112939df6b7a566c2bb Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 8 Nov 2016 06:56:24 -0800 Subject: [PATCH 1/3] Allow a path mapping to provide a file extension --- src/compiler/core.ts | 15 ++++++- src/compiler/moduleNameResolver.ts | 15 ++++--- ...pingBasedModuleResolution_withExtension.js | 16 ++++++++ ...asedModuleResolution_withExtension.symbols | 9 ++++ ...dModuleResolution_withExtension.trace.json | 11 +++++ ...gBasedModuleResolution_withExtension.types | 9 ++++ ...tion_withExtension_failedLookup.errors.txt | 9 ++++ ...leResolution_withExtension_failedLookup.js | 7 ++++ ...tion_withExtension_failedLookup.trace.json | 41 +++++++++++++++++++ ...pingBasedModuleResolution_withExtension.ts | 18 ++++++++ ...leResolution_withExtension_failedLookup.ts | 15 +++++++ 11 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.errors.txt create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.js create mode 100644 tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json create mode 100644 tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts create mode 100644 tests/cases/compiler/pathMappingBasedModuleResolution_withExtension_failedLookup.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 8175730159b..ded186691de 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2210,6 +2210,17 @@ namespace ts { * Path must have a valid extension. */ export function extensionFromPath(path: string): Extension { + const ts = tryGetTypeScriptExtensionFromPath(path); + if (ts !== undefined) { + return ts; + } + const js = tryGetJavaScriptExtensionFromPath(path); + if (js !== undefined) { + return js; + } + Debug.fail(`File ${path} has unknown extension.`); + } + export function tryGetTypeScriptExtensionFromPath(path: string): Extension | undefined { if (fileExtensionIs(path, ".d.ts")) { return Extension.Dts; } @@ -2219,13 +2230,13 @@ namespace ts { if (fileExtensionIs(path, ".tsx")) { return Extension.Tsx; } + } + function tryGetJavaScriptExtensionFromPath(path: string): Extension | undefined { if (fileExtensionIs(path, ".js")) { return Extension.Js; } if (fileExtensionIs(path, ".jsx")) { return Extension.Jsx; } - Debug.fail(`File ${path} has unknown extension.`); - return Extension.Js; } } diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 86e0157d847..831a19c4000 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -514,18 +514,21 @@ namespace ts { if (state.traceEnabled) { trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } - for (const subst of state.compilerOptions.paths[matchedPatternText]) { + return forEach(state.compilerOptions.paths[matchedPatternText], subst => { const path = matchedStar ? subst.replace("*", matchedStar) : subst; const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path)); if (state.traceEnabled) { trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); } - const resolved = loader(extensions, candidate, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state); - if (resolved) { - return resolved; + // A path mapping may have a ".ts" extension; in contrast to an import, which should omit it. + const tsExtension = tryGetTypeScriptExtensionFromPath(candidate); + if (tsExtension !== undefined) { + const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/false, state); + return path && { path, extension: tsExtension }; } - } - return undefined; + + return loader(extensions, candidate, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state); + }); } else { const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName)); diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js new file mode 100644 index 00000000000..7ed0026a050 --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts] //// + +//// [foo.ts] + +export function foo() {} + +//// [a.ts] +import { foo } from "foo"; + + +//// [foo.js] +"use strict"; +function foo() { } +exports.foo = foo; +//// [a.js] +"use strict"; diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols new file mode 100644 index 00000000000..0adeab146cd --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols @@ -0,0 +1,9 @@ +=== /a.ts === +import { foo } from "foo"; +>foo : Symbol(foo, Decl(a.ts, 0, 8)) + +=== /foo/foo.ts === + +export function foo() {} +>foo : Symbol(foo, Decl(foo.ts, 0, 0)) + diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json new file mode 100644 index 00000000000..56c03de869e --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json @@ -0,0 +1,11 @@ +[ + "======== Resolving module 'foo' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "'baseUrl' option is set to '/', using this value to resolve non-relative module name 'foo'", + "'paths' option is specified, looking for a pattern to match module name 'foo'.", + "Module name 'foo', matched pattern 'foo'.", + "Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.", + "File '/foo/foo.ts' exist - use it as a name resolution result.", + "Resolving real path for '/foo/foo.ts', result '/foo/foo.ts'", + "======== Module name 'foo' was successfully resolved to '/foo/foo.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types new file mode 100644 index 00000000000..0bc686d64e5 --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types @@ -0,0 +1,9 @@ +=== /a.ts === +import { foo } from "foo"; +>foo : () => void + +=== /foo/foo.ts === + +export function foo() {} +>foo : () => void + diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.errors.txt b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.errors.txt new file mode 100644 index 00000000000..7578f730c33 --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.errors.txt @@ -0,0 +1,9 @@ +/a.ts(2,21): 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/pathMappingBasedModuleResolution_withExtension_failedLookup.js b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.js new file mode 100644 index 00000000000..1774876911f --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.js @@ -0,0 +1,7 @@ +//// [a.ts] + +import { foo } from "foo"; + + +//// [a.js] +"use strict"; diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json new file mode 100644 index 00000000000..17e78176f4c --- /dev/null +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json @@ -0,0 +1,41 @@ +[ + "======== Resolving module 'foo' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "'baseUrl' option is set to '/', using this value to resolve non-relative module name 'foo'", + "'paths' option is specified, looking for a pattern to match module name 'foo'.", + "Module name 'foo', matched pattern 'foo'.", + "Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.", + "File '/foo/foo.ts' does not exist.", + "Loading module 'foo' from 'node_modules' folder.", + "File '/node_modules/foo.ts' does not exist.", + "File '/node_modules/foo.tsx' does not exist.", + "File '/node_modules/foo.d.ts' does not exist.", + "File '/node_modules/foo/package.json' does not exist.", + "File '/node_modules/foo/index.ts' does not exist.", + "File '/node_modules/foo/index.tsx' does not exist.", + "File '/node_modules/foo/index.d.ts' does not exist.", + "File '/node_modules/@types/foo.ts' does not exist.", + "File '/node_modules/@types/foo.tsx' does not exist.", + "File '/node_modules/@types/foo.d.ts' does not exist.", + "File '/node_modules/@types/foo/package.json' does not exist.", + "File '/node_modules/@types/foo/index.ts' does not exist.", + "File '/node_modules/@types/foo/index.tsx' does not exist.", + "File '/node_modules/@types/foo/index.d.ts' does not exist.", + "'baseUrl' option is set to '/', using this value to resolve non-relative module name 'foo'", + "'paths' option is specified, looking for a pattern to match module name 'foo'.", + "Module name 'foo', matched pattern 'foo'.", + "Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.", + "File '/foo/foo.ts' does not exist.", + "Loading module 'foo' from 'node_modules' folder.", + "File '/node_modules/foo.js' does not exist.", + "File '/node_modules/foo.jsx' does not exist.", + "File '/node_modules/foo/package.json' does not exist.", + "File '/node_modules/foo/index.js' does not exist.", + "File '/node_modules/foo/index.jsx' does not exist.", + "File '/node_modules/@types/foo.js' does not exist.", + "File '/node_modules/@types/foo.jsx' does not exist.", + "File '/node_modules/@types/foo/package.json' does not exist.", + "File '/node_modules/@types/foo/index.js' does not exist.", + "File '/node_modules/@types/foo/index.jsx' does not exist.", + "======== Module name 'foo' was not resolved. ========" +] \ No newline at end of file diff --git a/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts b/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts new file mode 100644 index 00000000000..0bfba0c0280 --- /dev/null +++ b/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts @@ -0,0 +1,18 @@ +// @noImplicitReferences: true +// @traceResolution: true + +// @Filename: /foo/foo.ts +export function foo() {} + +// @Filename: /a.ts +import { foo } from "foo"; + +// @Filename: /tsconfig.json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "foo": ["foo/foo.ts"] + } + } +} diff --git a/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension_failedLookup.ts b/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension_failedLookup.ts new file mode 100644 index 00000000000..a983b6c4825 --- /dev/null +++ b/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension_failedLookup.ts @@ -0,0 +1,15 @@ +// @noImplicitReferences: true +// @traceResolution: true + +// @Filename: /a.ts +import { foo } from "foo"; + +// @Filename: /tsconfig.json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "foo": ["foo/foo.ts"] + } + } +} From a0e4ab94f189b40ce4c7dd7b082c4b794f744ba1 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 11 Nov 2016 13:59:13 -0800 Subject: [PATCH 2/3] Allow JS files --- src/compiler/core.ts | 14 ++++---------- src/compiler/moduleNameResolver.ts | 2 +- ...thMappingBasedModuleResolution_withExtension.js | 8 ++++++++ ...pingBasedModuleResolution_withExtension.symbols | 7 +++++++ ...gBasedModuleResolution_withExtension.trace.json | 11 ++++++++++- ...appingBasedModuleResolution_withExtension.types | 7 +++++++ ...esolution_withExtension_failedLookup.trace.json | 9 --------- ...thMappingBasedModuleResolution_withExtension.ts | 12 ++++++++++-- 8 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ded186691de..f647480979b 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2210,17 +2210,13 @@ namespace ts { * Path must have a valid extension. */ export function extensionFromPath(path: string): Extension { - const ts = tryGetTypeScriptExtensionFromPath(path); - if (ts !== undefined) { - return ts; - } - const js = tryGetJavaScriptExtensionFromPath(path); - if (js !== undefined) { - return js; + const ext = tryGetExtensionFromPath(path); + if (ext !== undefined) { + return ext; } Debug.fail(`File ${path} has unknown extension.`); } - export function tryGetTypeScriptExtensionFromPath(path: string): Extension | undefined { + export function tryGetExtensionFromPath(path: string): Extension | undefined { if (fileExtensionIs(path, ".d.ts")) { return Extension.Dts; } @@ -2230,8 +2226,6 @@ namespace ts { if (fileExtensionIs(path, ".tsx")) { return Extension.Tsx; } - } - function tryGetJavaScriptExtensionFromPath(path: string): Extension | undefined { if (fileExtensionIs(path, ".js")) { return Extension.Js; } diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 045e90a6add..c6ffab4908f 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -519,7 +519,7 @@ namespace ts { trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); } // A path mapping may have a ".ts" extension; in contrast to an import, which should omit it. - const tsExtension = tryGetTypeScriptExtensionFromPath(candidate); + const tsExtension = tryGetExtensionFromPath(candidate); if (tsExtension !== undefined) { const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/false, state); return path && { path, extension: tsExtension }; diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js index 7ed0026a050..06f6b50e4a0 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.js @@ -4,13 +4,21 @@ export function foo() {} +//// [bar.js] +export function bar() {} + //// [a.ts] import { foo } from "foo"; +import { bar } from "bar"; //// [foo.js] "use strict"; function foo() { } exports.foo = foo; +//// [bar.js] +"use strict"; +function bar() { } +exports.bar = bar; //// [a.js] "use strict"; diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols index 0adeab146cd..717660067ec 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.symbols @@ -2,8 +2,15 @@ import { foo } from "foo"; >foo : Symbol(foo, Decl(a.ts, 0, 8)) +import { bar } from "bar"; +>bar : Symbol(bar, Decl(a.ts, 1, 8)) + === /foo/foo.ts === export function foo() {} >foo : Symbol(foo, Decl(foo.ts, 0, 0)) +=== /bar/bar.js === +export function bar() {} +>bar : Symbol(bar, Decl(bar.js, 0, 0)) + diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json index 56c03de869e..c4064ab8b8b 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json @@ -7,5 +7,14 @@ "Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.", "File '/foo/foo.ts' exist - use it as a name resolution result.", "Resolving real path for '/foo/foo.ts', result '/foo/foo.ts'", - "======== Module name 'foo' was successfully resolved to '/foo/foo.ts'. ========" + "======== Module name 'foo' was successfully resolved to '/foo/foo.ts'. ========", + "======== Resolving module 'bar' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "'baseUrl' option is set to '/', using this value to resolve non-relative module name 'bar'", + "'paths' option is specified, looking for a pattern to match module name 'bar'.", + "Module name 'bar', matched pattern 'bar'.", + "Trying substitution 'bar/bar.js', candidate module location: 'bar/bar.js'.", + "File '/bar/bar.js' exist - use it as a name resolution result.", + "Resolving real path for '/bar/bar.js', result '/bar/bar.js'", + "======== Module name 'bar' was successfully resolved to '/bar/bar.js'. ========" ] \ No newline at end of file diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types index 0bc686d64e5..8d7c57b1d27 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.types @@ -2,8 +2,15 @@ import { foo } from "foo"; >foo : () => void +import { bar } from "bar"; +>bar : () => void + === /foo/foo.ts === export function foo() {} >foo : () => void +=== /bar/bar.js === +export function bar() {} +>bar : () => void + diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json index 17e78176f4c..face88d225e 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension_failedLookup.trace.json @@ -14,12 +14,8 @@ "File '/node_modules/foo/index.ts' does not exist.", "File '/node_modules/foo/index.tsx' does not exist.", "File '/node_modules/foo/index.d.ts' does not exist.", - "File '/node_modules/@types/foo.ts' does not exist.", - "File '/node_modules/@types/foo.tsx' does not exist.", "File '/node_modules/@types/foo.d.ts' does not exist.", "File '/node_modules/@types/foo/package.json' does not exist.", - "File '/node_modules/@types/foo/index.ts' does not exist.", - "File '/node_modules/@types/foo/index.tsx' does not exist.", "File '/node_modules/@types/foo/index.d.ts' does not exist.", "'baseUrl' option is set to '/', using this value to resolve non-relative module name 'foo'", "'paths' option is specified, looking for a pattern to match module name 'foo'.", @@ -32,10 +28,5 @@ "File '/node_modules/foo/package.json' does not exist.", "File '/node_modules/foo/index.js' does not exist.", "File '/node_modules/foo/index.jsx' does not exist.", - "File '/node_modules/@types/foo.js' does not exist.", - "File '/node_modules/@types/foo.jsx' does not exist.", - "File '/node_modules/@types/foo/package.json' does not exist.", - "File '/node_modules/@types/foo/index.js' does not exist.", - "File '/node_modules/@types/foo/index.jsx' does not exist.", "======== Module name 'foo' was not resolved. ========" ] \ No newline at end of file diff --git a/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts b/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts index 0bfba0c0280..e40c67e4ea3 100644 --- a/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts +++ b/tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts @@ -1,18 +1,26 @@ // @noImplicitReferences: true // @traceResolution: true +// @allowJs: true // @Filename: /foo/foo.ts export function foo() {} +// @Filename: /bar/bar.js +export function bar() {} + // @Filename: /a.ts import { foo } from "foo"; +import { bar } from "bar"; // @Filename: /tsconfig.json { "compilerOptions": { "baseUrl": ".", "paths": { - "foo": ["foo/foo.ts"] - } + "foo": ["foo/foo.ts"], + "bar": ["bar/bar.js"] + }, + "allowJs": true, + "outDir": "bin" } } From 43a4b22eba1e3626b7f31deee6b6ec434588555f Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 11 Nov 2016 14:13:09 -0800 Subject: [PATCH 3/3] Update baseline --- .../pathMappingBasedModuleResolution_withExtension.trace.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json index c4064ab8b8b..a761414eb42 100644 --- a/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json +++ b/tests/baselines/reference/pathMappingBasedModuleResolution_withExtension.trace.json @@ -6,7 +6,6 @@ "Module name 'foo', matched pattern 'foo'.", "Trying substitution 'foo/foo.ts', candidate module location: 'foo/foo.ts'.", "File '/foo/foo.ts' exist - use it as a name resolution result.", - "Resolving real path for '/foo/foo.ts', result '/foo/foo.ts'", "======== Module name 'foo' was successfully resolved to '/foo/foo.ts'. ========", "======== Resolving module 'bar' from '/a.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", @@ -15,6 +14,5 @@ "Module name 'bar', matched pattern 'bar'.", "Trying substitution 'bar/bar.js', candidate module location: 'bar/bar.js'.", "File '/bar/bar.js' exist - use it as a name resolution result.", - "Resolving real path for '/bar/bar.js', result '/bar/bar.js'", "======== Module name 'bar' was successfully resolved to '/bar/bar.js'. ========" ] \ No newline at end of file