find-unused-exports icon indicating copy to clipboard operation
find-unused-exports copied to clipboard

Support import maps

Open djmccormick opened this issue 4 years ago • 7 comments

Support for aliases would make this tool useful in my project.

djmccormick avatar Dec 09 '20 15:12 djmccormick

Interesting 🤔

Webpack opens a can of works because you can modify anything to do anything else via webpack config. Generally, I keep bundler config to a minimum and avoid clever aliasing and things so that tooling such as import path linting and this one doesn’t get confused.

What is your specific use case (a monorepo?), and what would be the ideal way this package could support it (as in, how would it work)?

There are probably quick hack ways, that would break down in a lot of situations.

We could sniff a webpack config file, and extract the resolve config and use enhanced-resolve when converting the import specifiers to absolute file paths in here:

https://github.com/jaydenseric/find-unused-exports/blob/v1.1.1/public/findUnusedExports.js

But, that is very fragile:

  1. Webpack can have an array of configs for multiple builds (e.g. server, browser, etc.), that could have totally different resolve config. We can only use one at a time, so do we do multiple passes, one for each config, and consider exports unused across all the builds to be unused?
  2. Webpack resolve config actually doesn't apply to every file in the project; it applies to files bundled by Webpack. You could have a script file for example in the project that doesn't get bundled at all, and it would be incorrect to resolve it's import specifiers using the webpack config. So how do you know what files are bundled by Webpack? Only webpack knows, and after building everything.

Probably the most efficient way to deal with the these problems is to use a webpack plugin to find unused exports; some already exist. Although, it is possible for a project file to be used by both bundled and non-bundled code, and an export could be unused according to the webpack plugin when in fact is is used by an seperate non-bundled script.

Moral of the story, stick to standards, don't be too clever with your build tooling, webpack config is the devil 😅

jaydenseric avatar Dec 10 '20 00:12 jaydenseric

Another thing to consider is that there are other bundlers than just webpack; an ideal solution would work for all users.

The "dumbest" approach would be to add an alias option, that accepts a hardcoded map. We could use cosmiconfig to load find-unused-exports config, since it's a map is a little unwieldy to pass in as CLI arguments. A user could potentially use the .js format for the config, and import their webpack config and manually extract the alias config from it and massage it to the find-unused-exports alias format (if we don't exactly match the webpack format), so that way the config is DRY and shared by both tools.

jaydenseric avatar Dec 10 '20 01:12 jaydenseric

This feature would be a huge help! I'm sure it's nightmarish to implement and support every edge case, but any sort of workaround would be great. We use aliases for some of our packages to facilitate importing based on the environment (Electron vs React Native for example.) Manually copying our aliases to something like a .unusedexportsrc file would be trivial compared with the benefits of this project.

Thank you for your hard work @jaydenseric!

Nantris avatar Sep 14 '21 19:09 Nantris

Thinking about this fresh, and especially after working with web standard import maps in browsers and Deno with JSPM, adding support for import maps is probably the best way forward.

I'm currently retooling from Node.js to Deno, so if import maps are relatively easy to add here there is a chance I will get to it in the course of doing client Node.js project work. Otherwise, I might leave it for a bigger Deno rewrite. A lot of things we're doing here are vastly easier in Deno (e.g. globs), but I'm not sure yet how much we want to stick with Babel as there might be better options in Deno land.

jaydenseric avatar Sep 15 '21 01:09 jaydenseric

Wow cool, I'd never even heard of import maps!

I won't pretend to understand the full difficulties of supporting aliases, but those sound like a really great approach.

As far as Babel support goes, my guess is that they'll end up supporting Deno since it's increasingly gaining traction. There's at least a hint that they might decide to support Deno in 8.x or above.

Nantris avatar Sep 17 '21 21:09 Nantris

+1

My project uses create react app. At first it didn't work cause it was not seeing my babel config on the root folder. I added babel with preset react and the tool started to work but it wasn't taking the path alias into account. With CRA I'm using path aliases through jsconfig however, since I noticed this tool wanted my babel config to work I tried setting up aliases through babel config but it didn't work there.

My project would also benefit from alias support. Even if it is a dumb alias of some kind that we set it ourselves the config.

juanmagalhaes avatar Jan 09 '23 20:01 juanmagalhaes

:+1: for a simple map,

I use aliases to anchor local imports to the module root, so you don't have to think about ../components vs ../../components.

(Webpack: resolve.alias = { src: resolve(__dirname, '../src') } , tsconfig: "paths": { "src/*": ["src/*"] }, imports ...from 'src/components')

Something like find-unused-exports --map='src -> ./src' would be enough for this use case.

(My workaround for now is

perl -i -pe 's/from '\''src\//from '\''..\//' src/*/*.*
perl -i -pe 's/from '\''src\//from '\''..\/..\//' src/*/*/*.*
perl -i -pe 's/from '\''src\//from '\''..\/..\/..\//' src/*/*/*/*.*

npx find-unused-exports --module-glob 'src/**/*.{js,ts,jsx,tsx}' --resolve-file-extensions 'tsx,ts,jsx,js' --resolve-index-files

perl -i -pe 's/from '\''\.\.\//from '\''src\//' src/*/*.*
perl -i -pe 's/from '\''\.\.\/\.\.\//from '\''src\//' src/*/*/*.*
perl -i -pe 's/from '\''\.\.\/\.\.\/\.\.\//from '\''src\//' src/*/*/*/*.*

)

himdel avatar Jan 13 '23 19:01 himdel