diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 1cf4b9724d7ae..7e8ad40577c10 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -838,6 +838,14 @@ namespace ts { category: Diagnostics.Modules, description: Diagnostics.Specify_multiple_folders_that_act_like_Slashnode_modules_Slash_types }, + { + name: "resolveFromOutDir", + type: "boolean", + affectsModuleResolution: true, + category: Diagnostics.Modules, + description: Diagnostics.Allow_resolving_files_relative_to_the_output_directory, + defaultValueDescription: false + }, { name: "types", type: "list", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f211d7aeb20fc..20abb0830f6a0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4433,6 +4433,10 @@ "category": "Message", "code": 6107 }, + "'resolveFromOutDir' option is set, using it to resolve relative module name '{0}'.": { + "category": "Message", + "code": 16107 + }, "Longest matching prefix for '{0}' is '{1}'.": { "category": "Message", "code": 6108 @@ -4441,6 +4445,10 @@ "category": "Message", "code": 6109 }, + "Loading '{0}' from the out dir '{1}', candidate location '{2}'.": { + "category": "Message", + "code": 16109 + }, "Trying other entries in 'rootDirs'.": { "category": "Message", "code": 6110 @@ -4449,6 +4457,10 @@ "category": "Message", "code": 6111 }, + "Module resolution using 'outDir' has failed.": { + "category": "Message", + "code": 16111 + }, "Do not emit 'use strict' directives in module output.": { "category": "Message", "code": 6112 @@ -5721,6 +5733,11 @@ "category": "Message", "code": 6718 }, + "Allow resolving files relative to the output directory.": { + "category": "Message", + "code": 6719 + }, + "Default catch clause variables as 'unknown' instead of 'any'.": { "category": "Message", "code": 6803 diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index da2324cc74ecb..aefea65b54e92 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -1026,12 +1026,13 @@ namespace ts { type ResolutionKindSpecificLoader = (extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState) => Resolved | undefined; /** - * Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to + * Any module resolution kind can be augmented with optional settings: 'baseUrl', 'resolveFromOutDir', 'paths' and 'rootDirs' - they are used to * mitigate differences between design time structure of the project and its runtime counterpart so the same import name * can be resolved successfully by TypeScript compiler and runtime module loader. * If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will * fallback to standard resolution routine. * + * 'resolveFromOutDir': TODO document the semantics * - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative * names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will * be '/a/b/c/d' @@ -1088,6 +1089,9 @@ namespace ts { function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined { + const resolvedFromOutDir = tryLoadModuleUsingOutDirIfEligible(extensions, moduleName, containingDirectory, loader, state); + if (resolvedFromOutDir) return resolvedFromOutDir; + const resolved = tryLoadModuleUsingPathsIfEligible(extensions, moduleName, loader, state); if (resolved) return resolved.value; @@ -1114,6 +1118,51 @@ namespace ts { } } + function tryLoadModuleUsingOutDirIfEligible(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined { + const { baseUrl, resolveFromOutDir, outDir, rootDir } = state.compilerOptions; + if (!resolveFromOutDir) { + return undefined; + } + if (!outDir) { + return undefined; + } + if (state.traceEnabled) { + trace(state.host, Diagnostics.resolveFromOutDir_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName); + } + + // COMMENT FOR REVIEWER: Is there a more robust way to determine the base directory here? + var baseDirectory = baseUrl; + if (!baseDirectory && state.host.getCurrentDirectory) { + baseDirectory = state.host.getCurrentDirectory(); + } + if (!baseDirectory) { + return undefined; + } + + // COMMENT FOR REVIEWER: I've seen rootDir be relative path and and absolute path so + // handling both cases here to come up with an absolute normalizedPrefix path + var normalizedPrefix = rootDir && ts.startsWith(rootDir, ts.directorySeparator) ? + ts.normalizePath(rootDir) : + ts.normalizePath(ts.combinePaths(baseDirectory, rootDir)); + var candidate = ts.normalizePath(ts.combinePaths(containingDirectory, moduleName)); + + // COMMENT FOR REVIEWER: No ts.relativePath() function that I could find. Is there one + // somewhere that I'm not aware of? + var suffix = require("path").relative(normalizedPrefix, candidate); + candidate = ts.normalizePath(ts.combinePaths(baseDirectory, outDir, suffix)) + if (state.traceEnabled) { + trace(state.host, Diagnostics.Loading_0_from_the_out_dir_1_candidate_location_2, suffix, outDir, candidate); + } + const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(containingDirectory, state.host), state); + if (resolvedFileName) { + return resolvedFileName; + } + if (state.traceEnabled) { + trace(state.host, Diagnostics.Module_resolution_using_outDir_has_failed); + } + return undefined; + } + function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5ce2842fa8995..88ea91dcb95a6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6156,6 +6156,7 @@ namespace ts { project?: string; /* @internal */ pretty?: boolean; reactNamespace?: string; + resolveFromOutDir?: boolean; jsxFactory?: string; jsxFragmentFactory?: string; jsxImportSource?: string;