Skip to content

Commit

Permalink
hack: resolve from outDir
Browse files Browse the repository at this point in the history
Proof of concept for resolving microsoft#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
  • Loading branch information
alexeagle committed Mar 9, 2022
1 parent ea4791d commit e7e5c17
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
39 changes: 38 additions & 1 deletion src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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;

Expand All @@ -1114,6 +1118,39 @@ namespace ts {
}
}

function tryLoadModuleUsingOutDirIfEligible(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined {
const { resolveFromOutDir, outDir } = state.compilerOptions;
if (!resolveFromOutDir) {
// FIXME: wire the new option through
// 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);
}

let normalizedPrefix = normalizePath(outDir);

let candidate = normalizePath(combinePaths(containingDirectory, moduleName));
const suffix = candidate.substr(normalizedPrefix.length);
candidate = combinePaths(normalizePath(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 {

Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6156,6 +6156,7 @@ namespace ts {
project?: string;
/* @internal */ pretty?: boolean;
reactNamespace?: string;
resolveFromOutDir?: boolean;
jsxFactory?: string;
jsxFragmentFactory?: string;
jsxImportSource?: string;
Expand Down

0 comments on commit e7e5c17

Please sign in to comment.