New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(linter): automatic fixes for noRelativeOrAbsoluteImportsAcrossLibraries and noSelfCircularDependencies #8901
Conversation
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/nrwl/nx-dev/7toHFRSqF41vx78BFjEHnN4yfpxR [Deployment for 63fc1b4 canceled] |
bc55bf1
to
5c031db
Compare
5c031db
to
87c387b
Compare
87c387b
to
22521f6
Compare
22521f6
to
043b333
Compare
af8732a
to
0690d25
Compare
0690d25
to
4d66187
Compare
4d66187
to
c6a44c4
Compare
c6a44c4
to
27543b3
Compare
27543b3
to
814732b
Compare
814732b
to
f0498a5
Compare
f0498a5
to
0ff5e39
Compare
0ff5e39
to
cd4fd89
Compare
@meeroslav @JamesHenry can some of you please have a look at this? Whether this fits into the current Nx linter setup and anything I might have missed. Thanks a lot :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this extensive addition. I left some notes.
@@ -228,6 +238,95 @@ export default createESLintRule<Options, MessageIds>({ | |||
data: { | |||
npmScope, | |||
}, | |||
fix(fixer) { | |||
const targetImportProject = getTargetProjectBasedOnRelativeImport( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are running the isRelative
twice - as part of isRelativeImportIntoAnotherProject
and again at getTargetProjectBasedOnRelativeImport
.
I think we should split the condition on line 225 to check for relative and absolute import separately. That way we don't have to check for targetProjectImport twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly changed the current implementation to avoid duplicate calls. I guess the only concern there would be mostly around findTargetProject
that gets called as part of getTargetProjectBasedOnRelativeImport
sourceProject: ProjectGraphProjectNode | ||
): boolean { | ||
sourceFilePath: string | ||
) { | ||
if (!isRelative(imp)) return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The boolean return doesn't seem right based on the function's name. It should be null
or undefined
. Please add also return type to the function.
(node.source.parent as any).specifiers || [] | ||
).map((specifier) => specifier.imported.name); | ||
|
||
// ignore non-named imports for now, like "import '../../some-other-lib/src'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have been picked up above where we check if it's an import into another package.
Additionally, the example shows relative import, while the condition on line 354 explicitly checks against those.
What is the purpose of this early bail? Can we document this in the E2E test or unit test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have been picked up above where we check if it's an import into another package.
Where exactly?
Additionally, the example shows relative import, while the condition on line 354 explicitly checks against those.
Yeah this is a copy & paste comment error 😉. I'll adjust it. Basically this just documents the fact that imports like import '@myorg/somelib'
are not touched by the fixer implementation for now.
What is the purpose of this early bail? Can we document this in the E2E test or unit test?
😅 it's mostly because I didn't implement this case. Might be scope of a future improvement of this fix. So in the case of a relative import across library boundaries, the fix would be pretty easy, like
import from '../../../someotherlib/src'
=>import from '@myorg/someotherlib'
In the case of a local import to the same lib, the resolution would probably not be what we want:
import from '@myorg/samelib'
=>import from '../'
or whatever the current file location is relative toindex.ts
of the same lib
I feel this would probably not be what the dev intended though, but rather you'd want to import directly from the relative file in the same lib. Which would be hard to find without a named import.
In general I think these are edge cases though, but we might totally add it (I'd prefer in a future PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please leave a TODO as a reminder
}); | ||
} else { | ||
// we cannot remap, so leave it as is | ||
importsToRemap.push({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the scenario when this happens? A missing path in tsconfig?
Can we have this scenario in tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well this is basically the exit path for all the scenarios which I've either not considered, or which result due to some weird path mapping or something. In those cases the fixer should behave as if it isn't there, therefore defaulting back to not doing anything and just having the error surface to the dev
} | ||
|
||
// group imports from the same relative path | ||
const importsToRemapGrouped = importsToRemap.reduce( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the two importToRemap
reducers be extracted to a runtime linter util function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ofc, fixing it
cd4fd89
to
ebb34b2
Compare
ebb34b2
to
f87e990
Compare
f87e990
to
485e1ef
Compare
485e1ef
to
df122e6
Compare
@@ -134,16 +134,18 @@ class EnforceModuleBoundariesWalker extends Lint.RuleWalker { | |||
this.projectPath | |||
); | |||
const sourceProject = findSourceProject(this.projectGraph, filePath); | |||
const targetImportProject = getTargetProjectBasedOnRelativeImport( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If targetImportProject
is defined, we can skip the findProjectUsingImport
on line 159.
It should be something like:
const targetProject = targetImportProject || findProjectUsingImport(
this.projectGraph,
this.targetProjectLocator,
filePath,
imp,
this.npmScope
);
|
||
function tryReadBaseJson() { | ||
try { | ||
return JSON.parse( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Devkit has readJson
that wraps this segment
df122e6
to
eec9fb9
Compare
…braries and noSelfCircularDependencies
d0d08b7
to
63fc1b4
Compare
This pull request has already been merged/closed. If you experience issues related to these changes, please open a new issue referencing this pull request. |
Current Behavior
Expected Behavior
adds a Lint rule fixer for automatically adjusting
noRelativeOrAbsoluteImportsAcrossLibraries
andnoSelfCircularDependencies
violations of the Nx enforce-module-boundaries rule.Related Issue(s)
Fixes #