mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 03:20:56 -06:00
Fix auto import file extensions with package.json imports wildcards (#59564)
This commit is contained in:
parent
f3b118ed21
commit
4b12d82b6b
@ -4,6 +4,7 @@ import {
|
||||
AmbientModuleDeclaration,
|
||||
append,
|
||||
arrayFrom,
|
||||
changeFullExtension,
|
||||
CharacterCodes,
|
||||
combinePaths,
|
||||
compareBooleans,
|
||||
@ -59,6 +60,7 @@ import {
|
||||
getSupportedExtensions,
|
||||
getTemporaryModuleResolutionState,
|
||||
getTextOfIdentifierOrLiteral,
|
||||
hasImplementationTSFileExtension,
|
||||
hasJSFileExtension,
|
||||
hasTSFileExtension,
|
||||
hostGetCanonicalFileName,
|
||||
@ -599,7 +601,16 @@ function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOpt
|
||||
return pathsOnly ? undefined : relativePath;
|
||||
}
|
||||
|
||||
const fromPackageJsonImports = pathsOnly ? undefined : tryGetModuleNameFromPackageJsonImports(moduleFileName, sourceDirectory, compilerOptions, host, importMode);
|
||||
const fromPackageJsonImports = pathsOnly
|
||||
? undefined
|
||||
: tryGetModuleNameFromPackageJsonImports(
|
||||
moduleFileName,
|
||||
sourceDirectory,
|
||||
compilerOptions,
|
||||
host,
|
||||
importMode,
|
||||
prefersTsExtension(allowedEndings),
|
||||
);
|
||||
|
||||
const fromPaths = pathsOnly || fromPackageJsonImports === undefined ? paths && tryGetModuleNameFromPaths(relativeToBaseUrl, paths, allowedEndings, host, compilerOptions) : undefined;
|
||||
if (pathsOnly) {
|
||||
@ -997,7 +1008,18 @@ const enum MatchingMode {
|
||||
Pattern,
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromExportsOrImports(options: CompilerOptions, host: ModuleSpecifierResolutionHost, targetFilePath: string, packageDirectory: string, packageName: string, exports: unknown, conditions: string[], mode: MatchingMode, isImports: boolean): { moduleFileToTry: string; } | undefined {
|
||||
function tryGetModuleNameFromExportsOrImports(
|
||||
options: CompilerOptions,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
targetFilePath: string,
|
||||
packageDirectory: string,
|
||||
packageName: string,
|
||||
exports: unknown,
|
||||
conditions: string[],
|
||||
mode: MatchingMode,
|
||||
isImports: boolean,
|
||||
preferTsExtension: boolean,
|
||||
): { moduleFileToTry: string; } | undefined {
|
||||
if (typeof exports === "string") {
|
||||
const ignoreCase = !hostUsesCaseSensitiveFileNames(host);
|
||||
const getCommonSourceDirectory = () => host.getCommonSourceDirectory();
|
||||
@ -1006,6 +1028,7 @@ function tryGetModuleNameFromExportsOrImports(options: CompilerOptions, host: Mo
|
||||
|
||||
const pathOrPattern = getNormalizedAbsolutePath(combinePaths(packageDirectory, exports), /*currentDirectory*/ undefined);
|
||||
const extensionSwappedTarget = hasTSFileExtension(targetFilePath) ? removeFileExtension(targetFilePath) + tryGetJSExtensionForFile(targetFilePath, options) : undefined;
|
||||
const canTryTsExtension = preferTsExtension && hasImplementationTSFileExtension(targetFilePath);
|
||||
|
||||
switch (mode) {
|
||||
case MatchingMode.Exact:
|
||||
@ -1019,11 +1042,15 @@ function tryGetModuleNameFromExportsOrImports(options: CompilerOptions, host: Mo
|
||||
}
|
||||
break;
|
||||
case MatchingMode.Directory:
|
||||
if (canTryTsExtension && containsPath(targetFilePath, pathOrPattern, ignoreCase)) {
|
||||
const fragment = getRelativePathFromDirectory(pathOrPattern, targetFilePath, /*ignoreCase*/ false);
|
||||
return { moduleFileToTry: getNormalizedAbsolutePath(combinePaths(combinePaths(packageName, exports), fragment), /*currentDirectory*/ undefined) };
|
||||
}
|
||||
if (extensionSwappedTarget && containsPath(pathOrPattern, extensionSwappedTarget, ignoreCase)) {
|
||||
const fragment = getRelativePathFromDirectory(pathOrPattern, extensionSwappedTarget, /*ignoreCase*/ false);
|
||||
return { moduleFileToTry: getNormalizedAbsolutePath(combinePaths(combinePaths(packageName, exports), fragment), /*currentDirectory*/ undefined) };
|
||||
}
|
||||
if (containsPath(pathOrPattern, targetFilePath, ignoreCase)) {
|
||||
if (!canTryTsExtension && containsPath(pathOrPattern, targetFilePath, ignoreCase)) {
|
||||
const fragment = getRelativePathFromDirectory(pathOrPattern, targetFilePath, /*ignoreCase*/ false);
|
||||
return { moduleFileToTry: getNormalizedAbsolutePath(combinePaths(combinePaths(packageName, exports), fragment), /*currentDirectory*/ undefined) };
|
||||
}
|
||||
@ -1032,7 +1059,7 @@ function tryGetModuleNameFromExportsOrImports(options: CompilerOptions, host: Mo
|
||||
return { moduleFileToTry: combinePaths(packageName, fragment) };
|
||||
}
|
||||
if (declarationFile && containsPath(pathOrPattern, declarationFile, ignoreCase)) {
|
||||
const fragment = getRelativePathFromDirectory(pathOrPattern, declarationFile, /*ignoreCase*/ false);
|
||||
const fragment = changeFullExtension(getRelativePathFromDirectory(pathOrPattern, declarationFile, /*ignoreCase*/ false), getJSExtensionForFile(declarationFile, options));
|
||||
return { moduleFileToTry: combinePaths(packageName, fragment) };
|
||||
}
|
||||
break;
|
||||
@ -1040,11 +1067,15 @@ function tryGetModuleNameFromExportsOrImports(options: CompilerOptions, host: Mo
|
||||
const starPos = pathOrPattern.indexOf("*");
|
||||
const leadingSlice = pathOrPattern.slice(0, starPos);
|
||||
const trailingSlice = pathOrPattern.slice(starPos + 1);
|
||||
if (canTryTsExtension && startsWith(targetFilePath, leadingSlice, ignoreCase) && endsWith(targetFilePath, trailingSlice, ignoreCase)) {
|
||||
const starReplacement = targetFilePath.slice(leadingSlice.length, targetFilePath.length - trailingSlice.length);
|
||||
return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) };
|
||||
}
|
||||
if (extensionSwappedTarget && startsWith(extensionSwappedTarget, leadingSlice, ignoreCase) && endsWith(extensionSwappedTarget, trailingSlice, ignoreCase)) {
|
||||
const starReplacement = extensionSwappedTarget.slice(leadingSlice.length, extensionSwappedTarget.length - trailingSlice.length);
|
||||
return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) };
|
||||
}
|
||||
if (startsWith(targetFilePath, leadingSlice, ignoreCase) && endsWith(targetFilePath, trailingSlice, ignoreCase)) {
|
||||
if (!canTryTsExtension && startsWith(targetFilePath, leadingSlice, ignoreCase) && endsWith(targetFilePath, trailingSlice, ignoreCase)) {
|
||||
const starReplacement = targetFilePath.slice(leadingSlice.length, targetFilePath.length - trailingSlice.length);
|
||||
return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) };
|
||||
}
|
||||
@ -1054,20 +1085,22 @@ function tryGetModuleNameFromExportsOrImports(options: CompilerOptions, host: Mo
|
||||
}
|
||||
if (declarationFile && startsWith(declarationFile, leadingSlice, ignoreCase) && endsWith(declarationFile, trailingSlice, ignoreCase)) {
|
||||
const starReplacement = declarationFile.slice(leadingSlice.length, declarationFile.length - trailingSlice.length);
|
||||
return { moduleFileToTry: replaceFirstStar(packageName, starReplacement) };
|
||||
const substituted = replaceFirstStar(packageName, starReplacement);
|
||||
const jsExtension = tryGetJSExtensionForFile(declarationFile, options);
|
||||
return jsExtension ? { moduleFileToTry: changeFullExtension(substituted, jsExtension) } : undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(exports)) {
|
||||
return forEach(exports, e => tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, packageName, e, conditions, mode, isImports));
|
||||
return forEach(exports, e => tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, packageName, e, conditions, mode, isImports, preferTsExtension));
|
||||
}
|
||||
else if (typeof exports === "object" && exports !== null) { // eslint-disable-line no-restricted-syntax
|
||||
// conditional mapping
|
||||
for (const key of getOwnKeys(exports as MapLike<unknown>)) {
|
||||
if (key === "default" || conditions.indexOf(key) >= 0 || isApplicableVersionedTypesKey(conditions, key)) {
|
||||
const subTarget = (exports as MapLike<unknown>)[key];
|
||||
const result = tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, packageName, subTarget, conditions, mode, isImports);
|
||||
const result = tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, packageName, subTarget, conditions, mode, isImports, preferTsExtension);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
@ -1089,13 +1122,13 @@ function tryGetModuleNameFromExports(options: CompilerOptions, host: ModuleSpeci
|
||||
const mode = endsWith(k, "/") ? MatchingMode.Directory
|
||||
: k.includes("*") ? MatchingMode.Pattern
|
||||
: MatchingMode.Exact;
|
||||
return tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, subPackageName, (exports as MapLike<unknown>)[k], conditions, mode, /*isImports*/ false);
|
||||
return tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, subPackageName, (exports as MapLike<unknown>)[k], conditions, mode, /*isImports*/ false, /*preferTsExtension*/ false);
|
||||
});
|
||||
}
|
||||
return tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, packageName, exports, conditions, MatchingMode.Exact, /*isImports*/ false);
|
||||
return tryGetModuleNameFromExportsOrImports(options, host, targetFilePath, packageDirectory, packageName, exports, conditions, MatchingMode.Exact, /*isImports*/ false, /*preferTsExtension*/ false);
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromPackageJsonImports(moduleFileName: string, sourceDirectory: string, options: CompilerOptions, host: ModuleSpecifierResolutionHost, importMode: ResolutionMode) {
|
||||
function tryGetModuleNameFromPackageJsonImports(moduleFileName: string, sourceDirectory: string, options: CompilerOptions, host: ModuleSpecifierResolutionHost, importMode: ResolutionMode, preferTsExtension: boolean) {
|
||||
if (!host.readFile || !getResolvePackageJsonImports(options)) {
|
||||
return undefined;
|
||||
}
|
||||
@ -1120,7 +1153,7 @@ function tryGetModuleNameFromPackageJsonImports(moduleFileName: string, sourceDi
|
||||
const mode = endsWith(k, "/") ? MatchingMode.Directory
|
||||
: k.includes("*") ? MatchingMode.Pattern
|
||||
: MatchingMode.Exact;
|
||||
return tryGetModuleNameFromExportsOrImports(options, host, moduleFileName, ancestorDirectoryWithPackageJson, k, (imports as MapLike<unknown>)[k], conditions, mode, /*isImports*/ true);
|
||||
return tryGetModuleNameFromExportsOrImports(options, host, moduleFileName, ancestorDirectoryWithPackageJson, k, (imports as MapLike<unknown>)[k], conditions, mode, /*isImports*/ true, preferTsExtension);
|
||||
})?.moduleFileToTry;
|
||||
}
|
||||
|
||||
@ -1221,7 +1254,15 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan
|
||||
const packageName = getPackageNameFromTypesPackageName(nodeModulesDirectoryName);
|
||||
const conditions = getConditions(options, importMode);
|
||||
const fromExports = packageJsonContent?.exports
|
||||
? tryGetModuleNameFromExports(options, host, path, packageRootPath, packageName, packageJsonContent.exports, conditions)
|
||||
? tryGetModuleNameFromExports(
|
||||
options,
|
||||
host,
|
||||
path,
|
||||
packageRootPath,
|
||||
packageName,
|
||||
packageJsonContent.exports,
|
||||
conditions,
|
||||
)
|
||||
: undefined;
|
||||
if (fromExports) {
|
||||
return { ...fromExports, verbatimFromExports: true };
|
||||
@ -1411,3 +1452,8 @@ function isPathRelativeToParent(path: string): boolean {
|
||||
function getDefaultResolutionModeForFile(file: Pick<SourceFile, "fileName" | "impliedNodeFormat" | "packageJsonScope">, host: Pick<ModuleSpecifierResolutionHost, "getDefaultResolutionModeForFile">, compilerOptions: CompilerOptions) {
|
||||
return isFullSourceFile(file) ? host.getDefaultResolutionModeForFile(file) : getDefaultResolutionModeForFileWorker(file, compilerOptions);
|
||||
}
|
||||
|
||||
function prefersTsExtension(allowedEndings: readonly ModuleSpecifierEnding[]) {
|
||||
const tsPriority = allowedEndings.indexOf(ModuleSpecifierEnding.TsExtension);
|
||||
return tsPriority > -1 && tsPriority < allowedEndings.indexOf(ModuleSpecifierEnding.JsExtension);
|
||||
}
|
||||
|
||||
@ -265,6 +265,7 @@ import {
|
||||
isConstructorDeclaration,
|
||||
isConstTypeReference,
|
||||
isDeclaration,
|
||||
isDeclarationFileName,
|
||||
isDecorator,
|
||||
isElementAccessExpression,
|
||||
isEnumDeclaration,
|
||||
@ -9785,6 +9786,12 @@ export function hasTSFileExtension(fileName: string): boolean {
|
||||
return some(supportedTSExtensionsFlat, extension => fileExtensionIs(fileName, extension));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function hasImplementationTSFileExtension(fileName: string): boolean {
|
||||
return some(supportedTSImplementationExtensions, extension => fileExtensionIs(fileName, extension))
|
||||
&& !isDeclarationFileName(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Corresponds to UserPreferences#importPathEnding
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @module: nodenext
|
||||
// @allowImportingTsExtensions: true
|
||||
|
||||
// @Filename: /node_modules/pkg/package.json
|
||||
//// {
|
||||
//// "name": "pkg",
|
||||
//// "type": "module",
|
||||
//// "exports": {
|
||||
//// "./*": {
|
||||
//// "types": "./types/*",
|
||||
//// "default": "./dist/*"
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
|
||||
// @Filename: /node_modules/pkg/types/external.d.ts
|
||||
//// export declare function external(name: string): any;
|
||||
|
||||
// @Filename: /package.json
|
||||
//// {
|
||||
//// "name": "self",
|
||||
//// "type": "module",
|
||||
//// "imports": {
|
||||
//// "#*": "./src/*"
|
||||
//// },
|
||||
//// "dependencies": {
|
||||
//// "pkg": "*"
|
||||
//// }
|
||||
//// }
|
||||
|
||||
// @Filename: /src/add.ts
|
||||
//// export function add(a: number, b: number) {}
|
||||
|
||||
// @Filename: /src/index.ts
|
||||
//// add/*imports*/;
|
||||
//// external/*exports*/;
|
||||
|
||||
verify.importFixModuleSpecifiers("imports", ["#add.ts"]);
|
||||
verify.importFixModuleSpecifiers("exports", ["pkg/external.js"]);
|
||||
@ -0,0 +1,34 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @Filename: /tsconfig.json
|
||||
//// {
|
||||
//// "compilerOptions": {
|
||||
//// "module": "nodenext",
|
||||
//// "allowImportingTsExtensions": true,
|
||||
//// "rootDir": "src",
|
||||
//// "outDir": "dist",
|
||||
//// "declarationDir": "types",
|
||||
//// "declaration": true
|
||||
//// }
|
||||
//// }
|
||||
|
||||
// @Filename: /package.json
|
||||
//// {
|
||||
//// "name": "self",
|
||||
//// "type": "module",
|
||||
//// "imports": {
|
||||
//// "#*": {
|
||||
//// "types": "./types/*",
|
||||
//// "default": "./dist/*"
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
|
||||
// @Filename: /src/add.ts
|
||||
//// export function add(a: number, b: number) {}
|
||||
|
||||
// @Filename: /src/index.ts
|
||||
//// add/*imports*/;
|
||||
//// external/*exports*/;
|
||||
|
||||
verify.importFixModuleSpecifiers("imports", ["#add.js"]);
|
||||
@ -0,0 +1,20 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @module: nodenext
|
||||
// @allowImportingTsExtensions: true
|
||||
|
||||
// @Filename: /package.json
|
||||
//// {
|
||||
//// "type": "module",
|
||||
//// "imports": {
|
||||
//// "#src/*": "./SRC/*"
|
||||
//// }
|
||||
//// }
|
||||
|
||||
// @Filename: /src/add.ts
|
||||
//// export function add(a: number, b: number) {}
|
||||
|
||||
// @Filename: /src/index.ts
|
||||
//// add/*imports*/;
|
||||
|
||||
verify.importFixModuleSpecifiers("imports", ["#src/add.ts"], { importModuleSpecifierPreference: "non-relative" });
|
||||
Loading…
x
Reference in New Issue
Block a user