Skip to content

Commit 0afea27

Browse files
alexeagleGreg Magolan
authored and
Greg Magolan
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 0afea27

File tree

4 files changed

+76
-1
lines changed

4 files changed

+76
-1
lines changed

src/compiler/commandLineParser.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,14 @@ namespace ts {
838838
category: Diagnostics.Modules,
839839
description: Diagnostics.Specify_multiple_folders_that_act_like_Slashnode_modules_Slash_types
840840
},
841+
{
842+
name: "resolveFromOutDir",
843+
type: "boolean",
844+
affectsModuleResolution: true,
845+
category: Diagnostics.Modules,
846+
description: Diagnostics.Allow_resolving_files_relative_to_the_output_directory,
847+
defaultValueDescription: false
848+
},
841849
{
842850
name: "types",
843851
type: "list",

src/compiler/diagnosticMessages.json

Lines changed: 17 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
@@ -5721,6 +5733,11 @@
57215733
"category": "Message",
57225734
"code": 6718
57235735
},
5736+
"Allow resolving files relative to the output directory.": {
5737+
"category": "Message",
5738+
"code": 6719
5739+
},
5740+
57245741
"Default catch clause variables as 'unknown' instead of 'any'.": {
57255742
"category": "Message",
57265743
"code": 6803

src/compiler/moduleNameResolver.ts

Lines changed: 50 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,51 @@ namespace ts {
11141118
}
11151119
}
11161120

1121+
function tryLoadModuleUsingOutDirIfEligible(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined {
1122+
const { baseUrl, resolveFromOutDir, outDir, rootDir } = state.compilerOptions;
1123+
if (!resolveFromOutDir) {
1124+
return undefined;
1125+
}
1126+
if (!outDir) {
1127+
return undefined;
1128+
}
1129+
if (state.traceEnabled) {
1130+
trace(state.host, Diagnostics.resolveFromOutDir_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
1131+
}
1132+
1133+
// COMMENT FOR REVIEWER: Is there a more robust way to determine the base directory here?
1134+
var baseDirectory = baseUrl;
1135+
if (!baseDirectory && state.host.getCurrentDirectory) {
1136+
baseDirectory = state.host.getCurrentDirectory();
1137+
}
1138+
if (!baseDirectory) {
1139+
return undefined;
1140+
}
1141+
1142+
// COMMENT FOR REVIEWER: I've seen rootDir be relative path and and absolute path so
1143+
// handling both cases here to come up with an absolute normalizedPrefix path
1144+
var normalizedPrefix = rootDir && ts.startsWith(rootDir, ts.directorySeparator) ?
1145+
ts.normalizePath(rootDir) :
1146+
ts.normalizePath(ts.combinePaths(baseDirectory, rootDir));
1147+
var candidate = ts.normalizePath(ts.combinePaths(containingDirectory, moduleName));
1148+
1149+
// COMMENT FOR REVIEWER: No ts.relativePath() function that I could find. Is there one
1150+
// somewhere that I'm not aware of?
1151+
var suffix = require("path").relative(normalizedPrefix, candidate);
1152+
candidate = ts.normalizePath(ts.combinePaths(baseDirectory, outDir, suffix))
1153+
if (state.traceEnabled) {
1154+
trace(state.host, Diagnostics.Loading_0_from_the_out_dir_1_candidate_location_2, suffix, outDir, candidate);
1155+
}
1156+
const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(containingDirectory, state.host), state);
1157+
if (resolvedFileName) {
1158+
return resolvedFileName;
1159+
}
1160+
if (state.traceEnabled) {
1161+
trace(state.host, Diagnostics.Module_resolution_using_outDir_has_failed);
1162+
}
1163+
return undefined;
1164+
}
1165+
11171166
function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
11181167
state: ModuleResolutionState): Resolved | undefined {
11191168

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)