Skip to content

Commit e7e5c17

Browse files
committed
hack: resolve from outDir
Proof of concept for resolving #37378 Under the new proposed `compilerOptions.resolveFromOutDir` boolean, module resolution is attempted relative to the output folder. This is analogous to loading from the rootDirs, however it allows compilation where the output directory is configured on the command line rather than in the tsconfig.json. See the attached issue for context. TODO: - figure out what tests to add - reason about whether this interacts correctly with other related module resolution conditional logic - verify this works in some Bazel projects where the problem is observed
1 parent ea4791d commit e7e5c17

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4433,6 +4433,10 @@
44334433
"category": "Message",
44344434
"code": 6107
44354435
},
4436+
"'resolveFromOutDir' option is set, using it to resolve relative module name '{0}'.": {
4437+
"category": "Message",
4438+
"code": 16107
4439+
},
44364440
"Longest matching prefix for '{0}' is '{1}'.": {
44374441
"category": "Message",
44384442
"code": 6108
@@ -4441,6 +4445,10 @@
44414445
"category": "Message",
44424446
"code": 6109
44434447
},
4448+
"Loading '{0}' from the out dir '{1}', candidate location '{2}'.": {
4449+
"category": "Message",
4450+
"code": 16109
4451+
},
44444452
"Trying other entries in 'rootDirs'.": {
44454453
"category": "Message",
44464454
"code": 6110
@@ -4449,6 +4457,10 @@
44494457
"category": "Message",
44504458
"code": 6111
44514459
},
4460+
"Module resolution using 'outDir' has failed.": {
4461+
"category": "Message",
4462+
"code": 16111
4463+
},
44524464
"Do not emit 'use strict' directives in module output.": {
44534465
"category": "Message",
44544466
"code": 6112

src/compiler/moduleNameResolver.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,12 +1026,13 @@ namespace ts {
10261026
type ResolutionKindSpecificLoader = (extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState) => Resolved | undefined;
10271027

10281028
/**
1029-
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
1029+
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'resolveFromOutDir', 'paths' and 'rootDirs' - they are used to
10301030
* mitigate differences between design time structure of the project and its runtime counterpart so the same import name
10311031
* can be resolved successfully by TypeScript compiler and runtime module loader.
10321032
* If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
10331033
* fallback to standard resolution routine.
10341034
*
1035+
* 'resolveFromOutDir': TODO document the semantics
10351036
* - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
10361037
* names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
10371038
* be '/a/b/c/d'
@@ -1088,6 +1089,9 @@ namespace ts {
10881089
function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
10891090
state: ModuleResolutionState): Resolved | undefined {
10901091

1092+
const resolvedFromOutDir = tryLoadModuleUsingOutDirIfEligible(extensions, moduleName, containingDirectory, loader, state);
1093+
if (resolvedFromOutDir) return resolvedFromOutDir;
1094+
10911095
const resolved = tryLoadModuleUsingPathsIfEligible(extensions, moduleName, loader, state);
10921096
if (resolved) return resolved.value;
10931097

@@ -1114,6 +1118,39 @@ namespace ts {
11141118
}
11151119
}
11161120

1121+
function tryLoadModuleUsingOutDirIfEligible(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined {
1122+
const { resolveFromOutDir, outDir } = state.compilerOptions;
1123+
if (!resolveFromOutDir) {
1124+
// FIXME: wire the new option through
1125+
// return undefined
1126+
}
1127+
if (!outDir) {
1128+
return undefined;
1129+
}
1130+
if (state.traceEnabled) {
1131+
trace(state.host, Diagnostics.resolveFromOutDir_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
1132+
}
1133+
1134+
let normalizedPrefix = normalizePath(outDir);
1135+
1136+
let candidate = normalizePath(combinePaths(containingDirectory, moduleName));
1137+
const suffix = candidate.substr(normalizedPrefix.length);
1138+
candidate = combinePaths(normalizePath(outDir), suffix);
1139+
if (state.traceEnabled) {
1140+
trace(state.host, Diagnostics.Loading_0_from_the_out_dir_1_candidate_location_2, suffix, outDir, candidate);
1141+
}
1142+
1143+
const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(containingDirectory, state.host), state);
1144+
if (resolvedFileName) {
1145+
return resolvedFileName;
1146+
}
1147+
1148+
if (state.traceEnabled) {
1149+
trace(state.host, Diagnostics.Module_resolution_using_outDir_has_failed);
1150+
}
1151+
return undefined;
1152+
}
1153+
11171154
function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
11181155
state: ModuleResolutionState): Resolved | undefined {
11191156

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6156,6 +6156,7 @@ namespace ts {
61566156
project?: string;
61576157
/* @internal */ pretty?: boolean;
61586158
reactNamespace?: string;
6159+
resolveFromOutDir?: boolean;
61596160
jsxFactory?: string;
61606161
jsxFragmentFactory?: string;
61616162
jsxImportSource?: string;

0 commit comments

Comments
 (0)