Skip to content

Allow a path mapping to provide a file extension #12103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
5 commits merged into from
Nov 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,13 @@ namespace ts {
* Path must have a valid extension.
*/
export function extensionFromPath(path: string): Extension {
const ext = tryGetExtensionFromPath(path);
if (ext !== undefined) {
return ext;
}
Debug.fail(`File ${path} has unknown extension.`);
}
export function tryGetExtensionFromPath(path: string): Extension | undefined {
if (fileExtensionIs(path, ".d.ts")) {
return Extension.Dts;
}
Expand All @@ -2225,7 +2232,5 @@ namespace ts {
if (fileExtensionIs(path, ".jsx")) {
return Extension.Jsx;
}
Debug.fail(`File ${path} has unknown extension.`);
return Extension.Js;
}
}
15 changes: 9 additions & 6 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,18 +515,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 = tryGetExtensionFromPath(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));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//// [tests/cases/compiler/pathMappingBasedModuleResolution_withExtension.ts] ////

//// [foo.ts]

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";
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=== /a.ts ===
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))

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
"======== 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.",
"======== 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.",
"======== Module name 'bar' was successfully resolved to '/bar/bar.js'. ========"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=== /a.ts ===
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

Original file line number Diff line number Diff line change
@@ -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'.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//// [a.ts]

import { foo } from "foo";


//// [a.js]
"use strict";
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[
"======== 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.d.ts' does not exist.",
"File '/node_modules/@types/foo/package.json' 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.",
"======== Module name 'foo' was not resolved. ========"
]
Original file line number Diff line number Diff line change
@@ -0,0 +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"],
"bar": ["bar/bar.js"]
},
"allowJs": true,
"outDir": "bin"
}
}
Original file line number Diff line number Diff line change
@@ -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"]
}
}
}