eslint-plugin-import: `import/order` fails to recognize internal import groups
I’m using babel-plugin-module-resolver with eslint-import-resolver-babel-module to map imports from ~/<path>
to src/<path>
:
In my ESLint config, the import/order
rule is turned on using the following configuration:
module.exports = {
rules: {
'import/order': [
2,
{
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling', 'index'],
],
'newlines-between': 'always',
},
],
},
};
Imports from ~/<path>
, I believe, should be classified as an internal import group separated from other groups by a newline. I’m not sure what the behaviour is here, but given the following code:
import someNpmModule from 'some-npm-module';
import SomeComponent from '~/components/SomeComponent';
import someUtil from '~/utils/someUtil';
I’m receiving the following error:
3:1 error There should be at least one empty line between import groups import/order
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 37
- Comments: 44 (13 by maintainers)
Commits related to this issue
- Add tests for internal modules Including one broken test related to the #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
- Add broken tests for internal modules Including one broken test related to the #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
- Add setting for specifying internal module directories Closes #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
- Add setting for specifying internal module directories Closes #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
- Add broken tests for internal modules Including one broken test related to the #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
- Add broken tests for internal modules Including one broken test related to the #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
- Add broken tests for internal modules Including one broken test related to the #807 — committed to Whoaa512/eslint-plugin-import by Whoaa512 7 years ago
Maybe the actual fix is just to allow a setting which gives flexibility to what is considered an internal module.
Maybe something like this:
FYI, my use-case is a yarn workspace with namespaced packages. So, I have several packages in
packages/
, all of them are prefixed with a namespace like@x-client/
or@x-server/
. Using yarn workspace, I can just import like that, e.g.import { getBaseUrl } from '@x-client/api';
without relative paths.When using import/order, all my actually internal packages are treated as external, and it would be great to have some control over that treatment.
Hi there, I resolved this issue the following way. I hope this can help someone.
For JavaScript:
And overrides for TypeScript files:
I want to clarify the approach I highlighted above also worked within a monorepo, with a root ESLint config.
However, for us, internal means inside a package, and that may be a distinction. We don’t count other packages in the monorepo as internal.
I used the
ts
resolver over thetypescript
resolver, but it solved all problems and correctly identified internal modules. However, I haven’t updated this config since that time and the situation may have changed.On a new project I’m working on, which is not a monorepo, I just used the below and internal groups are working as expected:
I think what is missing is an option for custom resolver to define to which group a resolved file belongs to.
I was able to add the alias resolver, and it does seem to improve the behavior.
In my case, I don’t have specific aliases set up, but I do have “absolute imports” set up for a Create-React-App + TS project. My
tsconfig.json
has:For my app specifically, the source folder structure is:
and so typical imports might look like:
Those are imports I would want classified as “internal”.
I added these settings to my ESLint config:
That appears to have them mostly sorting how I want - after 3rd-party libs, and before local relative imports.
@ljharb Thanks. I guess that wasn’t clear to me, that statement didn’t seem to imply that
internal
is nothing by default. Also, it’s true that any file within the project is technically a sibling or parent, but I thought that was defined by the use of the relative path, and I’m using an absolute path.import/internal-regex
doesn’t work for me because I’d have to list every subfolder undersrc/
for the regex to pick it up. For example if my paths aresrc/routes/user
andsrc/models/user
, my imports areroutes/user
andmodels/user
and there’s no common prefix to use as the regex.The solution I posted above solves my use case and treats any import within the monorepo as
internal
, I just don’t understand how it’s working. @mrmckeb’s last response also works if you want monorepo imports outside of the local project to be treated asexternal
instead ofinternal
.@stramel There’s a PR (#914) to address this, but it needs changes which I’m unable to commit to doing at this time.
If you like I could add you as a collaborator on my fork.
In that case, I’ll repeat:
At this point, I’m going to close the thread, but will immediately reopen it once it becomes clear what the remaining issue is 😃
Unfortunately, I drove the discussion off-topic… Workspaces are a different use case entirely. Totally worth supporting, but I don’t see how that relates to the OP. Apologies.
@ljharb I believe that missing distinction is what is actionable here: most of us are asking for a new feature and concept of local workspace packages vs registry-installed packages. It doesn’t help that no such concept exists and is documented or not.
In the end, from a project’s codebase perspective this is a major distinction between “dependencies” and actual “source code”.
In modern projects with workspaces, you just can’t tell the difference by looking at an imported package name, e.g.
import utils from 'utils'
could be either autils
package from npm, or a local one.By the way, workspaces are not just a yarn feature anymore - npm 7 embraces and introduces the exact same concept of workspaces and local packages, see https://github.com/npm/rfcs/blob/latest/accepted/0026-workspaces.md
So what’s needed is a conceptual decision for the plugin first: support local packages and allow some control over the handling, or not.
The implementation could be anything from a simple yet good solution where users configure the plugin and tell it “what is local”, to more complex solutions that use the actual npm/yarn APIs and inherently know which packages are from local workspaces and which aren’t.
I initially tried the typescript resolver, but it kept counting modules that were included via a
@types/_whatever
package as an internal modules, which messed up the grouping. I setup the alias map just like you did.If you’re using
.tsx
files be sure to include that in the extensions.Heres my config
tsconfig.json
.eslintrc.js
@Whoaa512 I could try to take a look on it. Could give some initial guidance/context? Thank you
Maybe a bit off topic but… Can’t we just configure certain top-level paths to be explicitly treated as internal, like
shared/
? I guess given all possibilities of aliasing and module resolving, it would be much easier that way than trying to determine everything heuristically… Also, using yarn workspaces and lerna, I started to use use namespaces like “@client/” or “@server/”, but I might also introduce some namespace that actually exists on the public registry, but is a working local package nonetheless.