diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ba81ee7d103..932acdc6324 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -861,6 +861,13 @@ namespace ts { return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; } + export function getFileExtension(path: string): string { + const dot = path.lastIndexOf("."); + if (dot !== -1) { + return path.substring(dot); + } + } + export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index af8dd8ceb69..373e5fc3388 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2768,6 +2768,10 @@ "category": "Error", "code": 6131 }, + "File name '{0}' has a '{1}' extension - stripping it": { + "category": "Message", + "code": 6132 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 720b99d8900..c7b2cd66caf 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -615,11 +615,26 @@ namespace ts { } /** - * @param extensions - Either supportedTypeScriptExtensions or if --allowJs, allSupportedExtensions * @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. */ function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { + // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" + const keepOrAddExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); + if (keepOrAddExtension) { + return keepOrAddExtension; + } + // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" + if (hasJavaScriptFileExtension(candidate)) { + const extensionless = removeFileExtension(candidate); + if (state.traceEnabled) { + const extension = candidate.substring(extensionless.length); + trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension); + } + return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state); + } + } + function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { if (!onlyRecordFailures) { // check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing const directory = getDirectoryPath(candidate); @@ -628,27 +643,12 @@ namespace ts { } } - // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" - const keepOrAddExtension = forEach(extensions, ext => { + return forEach(extensions, ext => { if (state.skipTsx && (ext === ".jsx" || ext === ".tsx")) { return; } return tryLoad(fileExtensionIs(candidate, ext) ? candidate : candidate + ext); }); - if (keepOrAddExtension) { - return keepOrAddExtension; - } - - // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" - return forEach(supportedJavascriptExtensions, jsExt => { - if (state.skipTsx && jsExt === ".jsx") { - return; - } - const extensionless = tryRemoveExtension(candidate, jsExt); - if (extensionless !== undefined) { - return forEach(supportedTypeScriptExtensions, tsExt => tryLoad(extensionless + tsExt)); - } - }); function tryLoad(fileName: string): string { if (!onlyRecordFailures && state.host.fileExists(fileName)) { diff --git a/tests/baselines/reference/moduleResolution.trace.json b/tests/baselines/reference/moduleResolution.trace.json new file mode 100644 index 00000000000..f51251810a7 --- /dev/null +++ b/tests/baselines/reference/moduleResolution.trace.json @@ -0,0 +1,32 @@ +[ + "======== Resolving module './a' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/b.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "======== Module name './a' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './a.ts' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/c.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.ts' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './a.js' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/d.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.ts' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.tsx' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.d.ts' does not exist.", + "File name 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js' has a '.js' extension - stripping it", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.js' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './jquery.js' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery_user_1.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.ts' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.tsx' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.d.ts' does not exist.", + "File name 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js' has a '.js' extension - stripping it", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.ts' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.tsx' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.d.ts' exist - use it as a name resolution result.", + "======== Module name './jquery.js' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/cases/compiler/moduleResolution.ts b/tests/cases/compiler/moduleResolution.ts index c21614577c0..0af824100a7 100644 --- a/tests/cases/compiler/moduleResolution.ts +++ b/tests/cases/compiler/moduleResolution.ts @@ -1,3 +1,4 @@ +// @traceResolution: true // @Filename: a.ts export default 0;