feat(linter): support path wildcards in enforce-module-boundaries autofix (#18316)
This commit is contained in:
parent
dcefa4a6ad
commit
9c7ded0b1a
@ -332,7 +332,7 @@ export default createESLintRule<Options, MessageIds>({
|
||||
fix(fixer) {
|
||||
// imp has form of @myorg/someproject/some/path
|
||||
const indexTsPaths = getBarrelEntryPointByImportScope(imp);
|
||||
if (indexTsPaths && indexTsPaths.length > 0) {
|
||||
if (indexTsPaths.length > 0) {
|
||||
const specifiers = (node as any).specifiers;
|
||||
if (!specifiers || specifiers.length === 0) {
|
||||
return;
|
||||
@ -359,13 +359,22 @@ export default createESLintRule<Options, MessageIds>({
|
||||
dirname(importPath)
|
||||
);
|
||||
|
||||
// the string we receive from elsewhere might not have a leading './' here despite still being a relative path
|
||||
// we'd like to ensure it's a normalized relative form starting from ./ or ../
|
||||
const ensureRelativeForm = (path: string): string =>
|
||||
path.startsWith('./') || path.startsWith('../')
|
||||
? path
|
||||
: `./${path}`;
|
||||
|
||||
// if the string is empty, it's the current file
|
||||
const importPathResolved =
|
||||
relativePath === ''
|
||||
? `./${basename(importPath)}`
|
||||
: joinPathFragments(
|
||||
relativePath,
|
||||
basename(importPath)
|
||||
: ensureRelativeForm(
|
||||
joinPathFragments(
|
||||
relativePath,
|
||||
basename(importPath)
|
||||
)
|
||||
);
|
||||
|
||||
importsToRemap.push({
|
||||
|
||||
@ -27,9 +27,38 @@ function tryReadBaseJson() {
|
||||
*/
|
||||
export function getBarrelEntryPointByImportScope(
|
||||
importScope: string
|
||||
): string[] | null {
|
||||
): string[] {
|
||||
const tryPaths = (
|
||||
paths: Record<string, string[]>,
|
||||
importScope: string
|
||||
): string[] => {
|
||||
// TODO check and warn that the entries of paths[importScope] have no wildcards; that'd be user misconfiguration
|
||||
if (paths[importScope]) return paths[importScope];
|
||||
|
||||
// accommodate wildcards (it's not glob) https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
|
||||
const result = new Set<string>(); // set ensures there are no duplicates
|
||||
|
||||
for (const [alias, targets] of Object.entries(paths)) {
|
||||
if (!alias.endsWith('*')) {
|
||||
continue;
|
||||
}
|
||||
const strippedAlias = alias.slice(0, -1); // remove asterisk
|
||||
if (!importScope.startsWith(strippedAlias)) {
|
||||
continue;
|
||||
}
|
||||
const dynamicPart = importScope.slice(strippedAlias.length);
|
||||
targets.forEach((target) => {
|
||||
result.add(target.replace('*', dynamicPart)); // add interpolated value
|
||||
});
|
||||
// we found the entry for importScope; an import scope not supposed and has no sense having > 1 Aliases; TODO warn on duplicated entries
|
||||
break;
|
||||
}
|
||||
|
||||
return Array.from(result);
|
||||
};
|
||||
const tsConfigBase = tryReadBaseJson();
|
||||
return tsConfigBase?.compilerOptions?.paths[importScope] || null;
|
||||
if (!tsConfigBase?.compilerOptions?.paths) return [];
|
||||
return tryPaths(tsConfigBase.compilerOptions.paths, importScope);
|
||||
}
|
||||
|
||||
export function getBarrelEntryPointProjectNode(
|
||||
@ -90,7 +119,20 @@ export function getRelativeImportPath(exportedMember, filePath, basePath) {
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (
|
||||
!lstatSync(filePath, {
|
||||
throwIfNoEntry: false,
|
||||
}) /*not folder, but probably not full file with an extension either*/
|
||||
) {
|
||||
// try to find an extension that exists
|
||||
const ext = ['.ts', '.tsx', '.js', '.jsx'].find((ext) =>
|
||||
lstatSync(filePath + ext, { throwIfNoEntry: false })
|
||||
);
|
||||
if (ext) {
|
||||
filePath += ext;
|
||||
}
|
||||
}
|
||||
|
||||
const fileContent = readFileSync(filePath, 'utf8');
|
||||
|
||||
// use the TypeScript AST to find the path to the file where exportedMember is defined
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user