Eslint-plugin-import: ESLint rule to prefer import from index.js when available?

Created on 12 Jul 2019  Â·  11Comments  Â·  Source: benmosher/eslint-plugin-import

If I export a module through index.js, I now have two ways I can import that module. Is there a way to have ESLint enforce that the import through index.js should be used instead of directly importing the module?

Most helpful comment

I wouldn't have much sympathy for code that breaks because it started depending on undocumented features. Plus, not all of us are building libraries... if you're working on a REST API, then this conversation is moot, because nothing can be "discovered in a repl".

I think it would be great to have a lint rule that prefers index.js imports, and users can decide for themselves if this pattern provides enough encapsulation for their needs.

_Edit: This project already has this rule! I guess that must've been added after this thread took place.._

All 11 comments

You mean if you re-export it from a manifest file?

A rule that forced that would be imo encouraging a terrible practice - one that is the entire reason why treeshaking is needed in the first place. You should be directly importing what you need, deeply, whenever possible.

A rule that forces that, is something I'd be interested in.

I see. I have this directory that has a couple of "public" and a few "private" modules. I was considering using an index file to make it more clear what's intended to be used. I guess I can make an internal subdirectory. Or is there some convention?

If it’s reachable, it’s public, and you can’t use a lint rule from within a package to prevent use of “private” parts of it.

You can name the folder or files however you like - and you can put in your docs that they shouldn’t be used directly - but I’d suggest putting the “private” things in a different package entirely rather than trying to pretend users can’t depend on them.

Now I wish we did have the rule that you'd be interested in lol. I might not have gone down this track. Thanks for the guidance!

@ljharb

If it’s reachable, it’s public

I wouldn't say that. Public are the parts that are documented as parts of the API, and only those fall under semantic versioning. Putting them in a "manifest file", as you call it, seems like a reasonable way to do that - especially if your package doesn't consist of a bunch of unrelated stuff that could be loaded independently, but forms one cohesive intradependent entity.
How else would you suggest to implement internal modules, where the structure of the package is private and only the index.js API is exposed?

I think the definition of semver here is fuzzy - if you make a change to something you pretend is internal that breaks a user, it’s still a breaking change.

The only privacy JavaScript has is closures, and soon private class fields. Pretending anything reachable is private is just a bug farm.

@ljharb Given that rarely privacy is enforced through closures, it's all about conventions - like leading underscores in property names. Those are reachable, but still internal. And I would've thought that modules in package subfolders are considered internal implementation details as well, unless they are explicitly documented to be require-able?

That’s certainly one perception, but to me documentation is irrelevant. Your public api is whatever i can discover in a repl.

I wouldn't have much sympathy for code that breaks because it started depending on undocumented features. Plus, not all of us are building libraries... if you're working on a REST API, then this conversation is moot, because nothing can be "discovered in a repl".

I think it would be great to have a lint rule that prefers index.js imports, and users can decide for themselves if this pattern provides enough encapsulation for their needs.

_Edit: This project already has this rule! I guess that must've been added after this thread took place.._

Your sympathy isn’t relevant; it’s part of the API, and semver governs it.

And technically, a REST api is fully discoverable - perhaps you’re thinking of a RESTful api, where any url you use in your frontend is discoverable :-)

That rule is for your own project, not a generic one for any package.

I completely understand the need for such a rule - as others mentioned, you are not always building reusable libraries...

As a possible solution (or rather workaround to be specific) you can use existing rules no-restricted-imports and import/no-internal-modules with a simple code to generate your ESLint config dynamically: scan your directory tree and restrict access to children of any directory where you find index.js.

In a larger project I wouldn't recommend putting such code directly into config file, since it may take a while. Create separate script to update this list of restricted imports and run it manually. You can also configure your IDE to run that script for you when it finds any index updated (using regexp matching), or any other kind of file watchers.

Was this page helpful?
0 / 5 - 0 ratings