mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 01:49:41 -05:00
feat(48665): tsconfig "extends" field ignores "exports" field of source package (#50955)
* feat(48665): resolve configs from the exports field of the source package * add missed property * rename loadFileName to loadFileNameFromPackageJsonField * Apply suggestions from code review Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com> Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
@@ -85,7 +85,7 @@ import {
|
||||
NewLineKind,
|
||||
Node,
|
||||
NodeArray,
|
||||
nodeModuleNameResolver,
|
||||
nodeNextJsonConfigResolver,
|
||||
normalizePath,
|
||||
normalizeSlashes,
|
||||
NumericLiteral,
|
||||
@@ -3413,7 +3413,7 @@ function getExtendsConfigPath(
|
||||
return extendedConfigPath;
|
||||
}
|
||||
// If the path isn't a rooted or relative path, resolve like a module
|
||||
const resolved = nodeModuleNameResolver(extendedConfig, combinePaths(basePath, "tsconfig.json"), { moduleResolution: ModuleResolutionKind.Node10 }, host, /*cache*/ undefined, /*projectRefs*/ undefined, /*lookupConfig*/ true);
|
||||
const resolved = nodeNextJsonConfigResolver(extendedConfig, combinePaths(basePath, "tsconfig.json"), host);
|
||||
if (resolved.resolvedModule) {
|
||||
return resolved.resolvedModule.resolvedFileName;
|
||||
}
|
||||
|
||||
@@ -1629,6 +1629,11 @@ export function nodeModuleNameResolver(moduleName: string, containingFile: strin
|
||||
return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, extensions, !!isConfigLookup, redirectedReference);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function nodeNextJsonConfigResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
return nodeModuleNameResolverWorker(NodeResolutionFeatures.Exports, moduleName, getDirectoryPath(containingFile), { moduleResolution: ModuleResolutionKind.NodeNext }, host, /*cache*/ undefined, Extensions.Json, /*isConfigLookup*/ true, /*redirectedReference*/ undefined);
|
||||
}
|
||||
|
||||
function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions, isConfigLookup: boolean, redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations {
|
||||
const traceEnabled = isTraceEnabled(compilerOptions, host);
|
||||
|
||||
@@ -1868,7 +1873,13 @@ function loadModuleFromFileNoImplicitExtensions(extensions: Extensions, candidat
|
||||
}
|
||||
}
|
||||
|
||||
function loadJSOrExactTSFileName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
|
||||
/**
|
||||
* This function is only ever called with paths written in package.json files - never
|
||||
* module specifiers written in source files - and so it always allows the
|
||||
|
||||
* candidate to end with a TS extension (but will also try substituting a JS extension for a TS extension).
|
||||
*/
|
||||
function loadFileNameFromPackageJsonField(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
|
||||
if (extensions & Extensions.TypeScript && fileExtensionIsOneOf(candidate, supportedTSImplementationExtensions) ||
|
||||
extensions & Extensions.Declaration && fileExtensionIsOneOf(candidate, supportedDeclarationExtensions)
|
||||
) {
|
||||
@@ -1876,6 +1887,11 @@ function loadJSOrExactTSFileName(extensions: Extensions, candidate: string, only
|
||||
return result !== undefined ? { path: candidate, ext: tryExtractTSExtension(candidate) as Extension, resolvedUsingTsExtension: undefined } : undefined;
|
||||
}
|
||||
|
||||
if (state.isConfigLookup && extensions === Extensions.Json && fileExtensionIs(candidate, Extension.Json)) {
|
||||
const result = tryFile(candidate, onlyRecordFailures, state);
|
||||
return result !== undefined ? { path: candidate, ext: Extension.Json, resolvedUsingTsExtension: undefined } : undefined;
|
||||
}
|
||||
|
||||
return loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures, state);
|
||||
}
|
||||
|
||||
@@ -2058,7 +2074,7 @@ function loadEntrypointsFromExportMap(
|
||||
}
|
||||
const resolvedTarget = combinePaths(scope.packageDirectory, target);
|
||||
const finalPath = getNormalizedAbsolutePath(resolvedTarget, state.host.getCurrentDirectory?.());
|
||||
const result = loadJSOrExactTSFileName(extensions, finalPath, /*recordOnlyFailures*/ false, state);
|
||||
const result = loadFileNameFromPackageJsonField(extensions, finalPath, /*recordOnlyFailures*/ false, state);
|
||||
if (result) {
|
||||
entrypoints = appendIfUnique(entrypoints, result, (a, b) => a.path === b.path);
|
||||
return true;
|
||||
@@ -2488,7 +2504,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo
|
||||
const finalPath = toAbsolutePath(pattern ? resolvedTarget.replace(/\*/g, subpath) : resolvedTarget + subpath);
|
||||
const inputLink = tryLoadInputFileForPath(finalPath, subpath, combinePaths(scope.packageDirectory, "package.json"), isImports);
|
||||
if (inputLink) return inputLink;
|
||||
return toSearchResult(withPackageId(scope, loadJSOrExactTSFileName(extensions, finalPath, /*onlyRecordFailures*/ false, state)));
|
||||
return toSearchResult(withPackageId(scope, loadFileNameFromPackageJsonField(extensions, finalPath, /*onlyRecordFailures*/ false, state)));
|
||||
}
|
||||
else if (typeof target === "object" && target !== null) { // eslint-disable-line no-null/no-null
|
||||
if (!Array.isArray(target)) {
|
||||
@@ -2632,7 +2648,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo
|
||||
if (!extensionIsOk(extensions, possibleExt)) continue;
|
||||
const possibleInputWithInputExtension = changeAnyExtension(possibleInputBase, possibleExt, ext, !useCaseSensitiveFileNames());
|
||||
if (state.host.fileExists(possibleInputWithInputExtension)) {
|
||||
return toSearchResult(withPackageId(scope, loadJSOrExactTSFileName(extensions, possibleInputWithInputExtension, /*onlyRecordFailures*/ false, state)));
|
||||
return toSearchResult(withPackageId(scope, loadFileNameFromPackageJsonField(extensions, possibleInputWithInputExtension, /*onlyRecordFailures*/ false, state)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,24 @@ function createFileSystem(ignoreCase: boolean, cwd: string, root: string) {
|
||||
cwd,
|
||||
files: {
|
||||
[root]: {
|
||||
"dev/node_modules/@foo/tsconfig/package.json": JSON.stringify({
|
||||
name: "@foo/tsconfig",
|
||||
version: "1.0.0",
|
||||
exports: {
|
||||
".": "./src/tsconfig.json"
|
||||
}
|
||||
}),
|
||||
"dev/node_modules/@foo/tsconfig/src/tsconfig.json": JSON.stringify({
|
||||
compilerOptions: {
|
||||
strict: true,
|
||||
}
|
||||
}),
|
||||
"dev/tsconfig.extendsFoo.json": JSON.stringify({
|
||||
extends: "@foo/tsconfig",
|
||||
files: [
|
||||
"main.ts",
|
||||
]
|
||||
}),
|
||||
"dev/node_modules/config-box/package.json": JSON.stringify({
|
||||
name: "config-box",
|
||||
version: "1.0.0",
|
||||
@@ -376,6 +394,7 @@ describe("unittests:: config:: configurationExtension", () => {
|
||||
testSuccess("can lookup via an implicit tsconfig in a package-relative directory", "tsconfig.extendsBoxImpliedUnstrict.json", { strict: false }, [ts.combinePaths(basePath, "main.ts")]);
|
||||
testSuccess("can lookup via an implicit tsconfig in a package-relative directory with name", "tsconfig.extendsBoxImpliedUnstrictExtension.json", { strict: false }, [ts.combinePaths(basePath, "main.ts")]);
|
||||
testSuccess("can lookup via an implicit tsconfig in a package-relative directory with extension", "tsconfig.extendsBoxImpliedPath.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
|
||||
testSuccess("can lookup via an package.json exports", "tsconfig.extendsFoo.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
|
||||
});
|
||||
|
||||
it("adds extendedSourceFiles only once", () => {
|
||||
|
||||
Reference in New Issue
Block a user