module-alias: IMPORTANT: DO NOT USE! Use import mapping instead
A similar feature to this library is already available in node as a standard: import mapping.
Not only does it allow for directory mapping, but it also allows dependency aliasing, and works for both require
and import
(ESM) WITHOUT breaking resolution behaviour in production or other libraries like using this library does.
You have been warned.
I know this sounds hateful, however, that is not my intention. My intention is instead to spread awareness about this mostly undocumented feature (it is not shown in any package manifest documentation besides node’s, and it is relatively tucked away) so people can write better software without needing to use hacky libraries like this one and without adding unnecessary dependencies.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 60
- Comments: 54 (2 by maintainers)
@Papooch you will have to convert to using
#alias
instead of@alias
, but that’s it.I hope that helps.
@Nytelife26 , thanks for raising awareness. This was introduced “recently”, so on older versions of node, you might still want
module-alias
- therefore we probably won’t just “deprecate” the package.Would you mind opening a PR to update the README of this package, explaining that import mapping exists and should be used instead?
To see a working example of using subpath imports with
index.js
files in a commonjs environment - see the forwardemail.net repopackage.json
Many people are confused because using these import maps uses the same resolution logic as ESModules, which do not automatically resolve index.js files in the same way commonjs does. ESM requires the full path and extension to the file it is importing, and by extension so do the import maps here.
Using import maps like shown above will help you have a similar feel to commonjs. If you go deeper in the linked repo - you can see that this works.
Also note that the
*
in the imports does not have the same functionality as a glob star, which can be a bit confusing and may need some trial and error to set up your project correctly.This package didn’t work for me no matter how I hard I tried to configure it. Maybe because I use ES6+ including imports (no single require() in my project) Builtin functionality works.
jsconfig
usage
@nick-bull Well, that does seem to work, but I have to explicitly state the file, including the file extension.
require('#submodules/db-models/master/index')
does not work. I would have to change imports in the whole project and lose the flexibility along the way.I would very much prefer to use a native solution instead of a library, but not in a way that makes my experience worse.
the problem is not how to replace ‘@’ by ‘#’, the problem is in the project structure itself.
for example if you want to import a local package that is not published to npm and updated frequently without packing it everytime you can add it’s name to tsconfig path so
@scope/package-name
refers the location of the local package and allows you to import it in your project like thiswhich will be replaced with the actual path.
changing ‘@’ to ‘#’ will not help
@Nytelife26 Trying to switch to a node subpath imports, but for me it only works with the full path.
My imports:
Utils folder has two files:
index.js
andtest.js
, both hasfoo
andbar
functions.My entry:
Both calls only work if I add the full path:
#utils/index.js
or#utils/test.js
. Otherwise i have:I have no
"type": "module"
enabled, and I tried"type": "commonjs"
with no success. Alsoexperimental-specifier-resolution
does not help.Any idea what I’m doing wrong?
You’re right, I totally thought I’d written that example too and forgot to include it. I’m still not sure that answers my question though - is there a way to write a mapping that will allow both of the following:
Edit, scrap the above. It was because I had
"type": "module"
inpackage.json
in my test project, causing it to throw theError [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import ... is not supported resolving ES modules imported from ..
I’d mentioned before. You’ll either need transpilation ornode --experimental-specifier-resolution=node ...
. Thanks Nytelife!@jlenon7 Yeah I don’t think the path intellisense extensions support subpath imports, however, TS (and vscode ts linting) does. So while it won’t help you autocomplete, it will help validate their existence and types.
@Griffork That seems to be a relatively critical misunderstanding of how monorepositories work. They’re packages within a package structure.
Common is not part of either package. So, instead, you should make the other packages depend on common, assuming common is its own package.
As to why that needs to be a “very arbitrary condition”, how do you expect a package to work if it imports code that is neither bundled with the package nor depended on in the manifest?? If anyone installs that, it will break, guaranteed. Shared code and external libraries are common - that’s why you can make them dependencies.
You should look into workspaces for your relevant node package manager and research further into monorepositories.
No worries @initplatform. I give special credit to @nick-bull for the module resolution override because prior to that I was unaware it even existed honestly. I’m glad you found your solution, though!
Life on easy mode for typescript users: use tsx as a runtime instead of node. Everything just works, no need for additional config besides the aliases defined in
tsconfig.json
, no need to emit any js file either.@spence-s when you use the Node.js ESM import aliases in your code to import modules, does VSCode understand the type of the class or function that you are importing? In my case I can only get the type of the class or function if I use the real path to import modules:
this will force you make some changes to your codebase that introduce breaking change
Another solution is tsc-alias which generates relative paths. You can see my configuration for it.
However, this works fine for me. VS Code Intellisense will not resolve the imports. Is there any convenient workaround for that?