From 1c64615e32466d0e9f71a112939df6b7a566c2bb Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 8 Nov 2016 06:56:24 -0800 Subject: [PATCH] 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"] + } + } +}