Skip to content

Commit d7c759c

Browse files
JesseZomerFrozenPandaz
authored andcommitted
fix(linter): allow for wildcards paths in enforce-module-boundaries rule (#34066)
closed #32190 ## Current Behavior eslint crashes when tsconfig.base.json path includes a * and you have an import going to that project ## Expected Behavior The plugin shouldn't crash and it should auto fix to a working import ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> #32190 Fixes #32190 (cherry picked from commit ac2ef1a)
1 parent e941892 commit d7c759c

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

packages/eslint-plugin/src/rules/enforce-module-boundaries.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const tsconfig = {
6363
'@mycompany/domain2': ['libs/domain2/src/index.ts'],
6464
'@mycompany/buildableLib': ['libs/buildableLib/src/main.ts'],
6565
'@mycompany/buildableLib2': ['libs/buildableLib2/src/main.ts'],
66+
'@mycompany/wildcard/*': ['libs/wildcard/*'],
6667
'@nonBuildableScope/nonBuildableLib': [
6768
'libs/nonBuildableLib/src/main.ts',
6869
],
@@ -115,6 +116,8 @@ const fileSys = {
115116
'./libs/dependsOnPrivate/src/index.ts': '',
116117
'./libs/dependsOnPrivate2/src/index.ts': '',
117118
'./libs/private/src/index.ts': '',
119+
'./libs/wildcard/user.ts': 'export class User {}',
120+
'./libs/wildcard/nested/user.ts': 'export class User {}',
118121
'./tsconfig.base.json': JSON.stringify(tsconfig),
119122
'./package.json': JSON.stringify(packageJson),
120123
'./nx.json': JSON.stringify({ npmScope: 'happyorg' }),
@@ -1239,6 +1242,53 @@ Violation detected in:
12391242
'External resources cannot be imported using a relative or absolute path'
12401243
);
12411244
});
1245+
1246+
it('should handle relative imports containing a wildcard', () => {
1247+
const failures = runRule(
1248+
{},
1249+
`${process.cwd()}/proj/libs/mylib/src/main.ts`,
1250+
`
1251+
import { User } from '../../wildcard/user';
1252+
`,
1253+
{
1254+
nodes: {
1255+
mylibName: {
1256+
name: 'mylibName',
1257+
type: 'lib',
1258+
data: {
1259+
root: 'libs/mylib',
1260+
tags: [],
1261+
implicitDependencies: [],
1262+
targets: {},
1263+
},
1264+
},
1265+
wildcardName: {
1266+
name: 'wildcardName',
1267+
type: 'lib',
1268+
data: {
1269+
root: 'libs/wildcard',
1270+
tags: [],
1271+
implicitDependencies: [],
1272+
targets: {},
1273+
},
1274+
},
1275+
},
1276+
dependencies: {},
1277+
},
1278+
{
1279+
mylibName: [createFile(`libs/mylib/src/main.ts`)],
1280+
wildcardName: [createFile('libs/wildcard/user.ts')],
1281+
}
1282+
);
1283+
1284+
expect(failures.length).toEqual(1);
1285+
expect(failures[0].message).toEqual(
1286+
'Projects cannot be imported by a relative or absolute path, and must begin with a npm scope'
1287+
);
1288+
1289+
// Verify the fix is available
1290+
expect(failures[0].fix).toBeDefined();
1291+
});
12421292
});
12431293

12441294
it('should error on absolute imports into libraries without using the npm scope', () => {

packages/eslint-plugin/src/rules/enforce-module-boundaries.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '@typescript-eslint/utils';
1414
import { isBuiltinModuleImport } from '@nx/js/src/internal';
1515
import { isRelativePath } from 'nx/src/utils/fileutils';
16-
import { basename, dirname, join, relative } from 'path';
16+
import { basename, dirname, join, relative, resolve } from 'path';
1717
import {
1818
getBarrelEntryPointByImportScope,
1919
getBarrelEntryPointProjectNode,
@@ -335,16 +335,50 @@ export default ESLintUtils.RuleCreator(
335335
const importsToRemap = [];
336336

337337
for (const entryPointPath of indexTsPaths) {
338+
// Resolve wildcard paths before passing to getRelativeImportPath
339+
let resolvedPath = entryPointPath.path;
340+
let targetImportScope = entryPointPath.importScope;
341+
if (resolvedPath.includes('*')) {
342+
// For wildcard paths resolve using the actual file path from the relative import
343+
// Step 1: Resolve the relative import to an absolute path
344+
// Example: imp='../../models/user', fileName='/root/libs/mylib/src/main.ts'
345+
// => absoluteImportPath='/root/libs/models/user'
346+
const absoluteImportPath = resolve(dirname(fileName), imp);
347+
348+
// Step 2: Get the path relative to project path (which is the workspace root in practice)
349+
// Example: absoluteImportPath='/root/libs/models/user', projectPath='/root'
350+
// => workspaceRelativePath='libs/models/user'
351+
const workspaceRelativePath = normalizePath(
352+
relative(projectPath, absoluteImportPath)
353+
);
354+
355+
// Step 3: Extract the dynamic part after the base path
356+
// Example: resolvedPath='libs/models/*', workspaceRelativePath='libs/models/user'
357+
// => basePath='libs/models/', dynamicPart='user'
358+
// => resolvedPath='libs/models/user', targetImportScope='@myorg/models/user'
359+
const basePath = resolvedPath.replace('*', '');
360+
if (workspaceRelativePath.startsWith(basePath)) {
361+
const dynamicPart = workspaceRelativePath.substring(
362+
basePath.length
363+
);
364+
resolvedPath = resolvedPath.replace('*', dynamicPart);
365+
targetImportScope = targetImportScope.replace(
366+
'*',
367+
dynamicPart
368+
);
369+
}
370+
}
371+
338372
for (const importMember of imports) {
339373
const importPath = getRelativeImportPath(
340374
importMember,
341-
join(workspaceRoot, entryPointPath.path)
375+
join(workspaceRoot, resolvedPath)
342376
);
343377
// we cannot remap, so leave it as is
344378
if (importPath) {
345379
importsToRemap.push({
346380
member: importMember,
347-
importPath: entryPointPath.importScope,
381+
importPath: targetImportScope,
348382
});
349383
}
350384
}

0 commit comments

Comments
 (0)