Skip to content
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

Allow a path mapping to provide a file extension #12103

Merged
5 commits merged into from
Nov 11, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 13 additions & 2 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
}
15 changes: 9 additions & 6 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that restricting extensions to only .ts is a right thing here i.e. if user wants to remap some import to .js file. (cc @mhegazy for the opinion)

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,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";
Original file line number Diff line number Diff line change
@@ -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))

Original file line number Diff line number Diff line change
@@ -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'. ========"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== /a.ts ===
import { foo } from "foo";
>foo : () => void

=== /foo/foo.ts ===

export function foo() {}
>foo : () => 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,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. ========"
]
Original file line number Diff line number Diff line change
@@ -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"]
}
}
}
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"]
}
}
}