-
Notifications
You must be signed in to change notification settings - Fork 69
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
IMPORTANT: DO NOT USE! Use import mapping instead #113
Comments
@Nytelife26 , thanks for raising awareness. This was introduced "recently", so on older versions of node, you might still want Would you mind opening a PR to update the README of this package, explaining that import mapping exists and should be used instead? |
Yes, that is fine. I would be more than happy to open a pull request regarding
versioning and use of import mapping where possible. Thank you for the response.
|
@nick-bull Thanks for the extra examples :) greatly appreciated |
An interest afterthought to replacing this module is that import mapping does not resolve directory imports, e.g. You can get around this with The value is not really a regex, so something like
throws Anybody know a way around this? |
@nick-bull ..uh
Of course it doesn't? That'd resolve to
|
I have already suggested that in my comment; I've edited my response so it's a little clearer, as it was a bit densely packed, as it does not work, it throws an error |
@nick-bull No, you did not already suggest what I said specifically. You suggested something similar, which was I hope that helps. |
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 |
No worries @nick-bull. My answer if not for your discovery of needing to set the type to modules would've been to just import If you have any further questions or concerns let me know. |
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. "imports": {
"##/*": "./*"
}, jsconfig {
"compilerOptions": {
"baseUrl": ".",
"paths": {
"##/*": ["./*"]
}
},
"exclude": ["node_modules"]
}
usage import { Model } from "##/api/models.js"; |
@thefoxie thank you for the contribution :) however, this is not a place to report the package failing. I am glad the built-in functionality works, however, if you have any problems you should still create a separate issue so the ilearnio team can ensure the package works as intended for those who need it. |
After reading the documentation, I still don't understand how to use it... Could you please explain to me how can I convert this:
to use native import mapping instead? Preferably without changing any other code. Thanks. |
@Papooch you will have to convert to using "imports": {
"#root/*": "./*",
"#submodules/*": "./submodules/*",
"#db/*": "./src/db/*",
"#middleware/*": "./src/middleware/*"
} I hope that helps. |
Well that's what I tried also, but I am getting and error. in const { initModels } = require('#submodules/db-models/master'); But it throws an error, which I don't understand:
But the folder Does folder mapping work differently to file mapping using this technique? |
@Papooch You didn't happen to get burned in the same way as I did? Test by using
|
@nick-bull Well, that does seem to work, but I have to explicitly state the file, including the file extension. I would very much prefer to use a native solution instead of a library, but not in a way that makes my experience worse. |
@Papooch I suspect you're being burned for the same reason as the prior mentioned comment, this feature isn't going to affect extension resolution. Try |
I've gotten this to work as described above. In my package.json "imports": {
"#app/*": "./dist/app/*",
"#lib/*": "./dist/lib/*",
"#src/*": "./dist/*"
}, then using Where I am having issues, is when I try and use jest. Previously when using module-alias I could do the following in jest.config.ts moduleNameMapper: {
'^#app/(.*)$': '<rootDir>/src/app/$1',
'^#lib/(.*)$': '<rootDir>/src/lib/$1',
'^#src/(.*)$': '<rootDir>/src/$1'
}, but when I try and run tests, now, I am getting:
It looks like it is looking for If I do a quick hardcode hack, the test runs (I hardcode index.ts): moduleNameMapper: {
'^#app/(.*)$': '<rootDir>/src/app/index.ts',
'^#lib/(.*)$': '<rootDir>/src/lib/$1',
'^#src/(.*)$': '<rootDir>/src/$1'
}, ...any ideas? |
@Papooch if you wish to use extensions in the map to avoid having to specify @initplatform how are the imports specified in the actual files? |
Ha! For whatever reason, when I was debugging the transition over to import mapping I had done this: import { FastifyApp } from '#app/index.js'; instead of this: import { FastifyApp } from '#app/index'; Can't even remember what I was testing... but I forgot to remove the Sometimes it's the tiny things right :) I am excited to see this import mapping land in node. At first I was averse to the Thanks to all involved that created |
@initplatform I'm not sure you'll need the |
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! |
I believe that's an annoyance with typescript. For whatever reason, if only using the typescript path alias, I need to specify index or typescript won't compile. I don't need to use index for nested routes though. |
One other reason to keep maintaining this package is that import mapping only works for |
Is that so? Standard React and JSX relies on ESM, I cannot see why React Native wouldn't permit it. |
I've discovered that I might need to switch to es6 modules, and that module-alias seems to be not working. I stumbled across this thread and converted my project over to using the "imports" property in package.json, but I'm getting an error I can't solve: Package.json: "imports": {
"#Common/*": "../Common/*.js",
"#Typescript/*": "../Typescript/*.js"
} Error: Invalid "imports" target "../Common/*.js" defined for '#Common/*' in the package config [PathToRepo]\Server\package.json imported from [PathToRepo]\Server\server.js Does this mean that the inbuilt "imports" property doesn't work outside the directory that the package.json is located within? Is there a work around for this? If not is there a way to make module-alias work with ES6 modules? |
Does this mean that the inbuilt "imports" property doesn't work outside
the directory that the package.json is located within?
Do I get to ask why you're trying to use import paths to import from
directories not contained within your project? The paths you're aliasing
should *always* be contained within the project that the `package.json`
is for.
It appears that you're working on a project with multiple subpackages.
If that is the case, use the import paths in the `package.json` for the
top level directory of the project.
I hope this helps.
|
I can't figureout how to get node 16 native ESM support to work with Mocha 8 and module-alias... So this is my only option Error on node 16, mocha 8, type: "module"
mocha test wrapper
package.json
reset.js import { simple as logger } from '@src/common/Logger.js' |
this works fine for me
Something working does not mean it works well, nor that you should use
it.
VS Code Intellisense will not resolve the imports. Is there any convenient
workaround for that?
That seems to be an issue with current tooling and import resolution.
ESLint with the `node/no-unresolved-imports` rule fails in the same way.
Hopefully soon more people will implement checking for this - including
language servers - and then it will work better.
|
@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: My entry:
Both calls only work if I add the full path:
I have no Any idea what I'm doing wrong? |
To see a working example of using subpath imports with package.json "imports": {
"#config/*": "./config/*.js",
"#config": "./config/index.js",
"#helpers/*": "./helpers/*.js",
"#models/*": "./app/models/*.js",
"#models": "./app/models/index.js",
"#controllers/*": "./app/controllers/*.js",
"#controllers": "./app/controllers/index.js",
"#controllers/web": "./app/controllers/web/index.js",
"#controllers/api": "./app/controllers/api/index.js"
}, 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 |
Best solution to solve aliases/paths in js projects for node scripts is using tsconfig-paths. For example. |
@spence-s Yeah, I use vanilla nodejs, without bundlers, and I'm forced to use replace-in-file solution with regexp for solving extension & index addition & tsconfig-paths/register for node aliases. With ts-projects & ttsc we have more functionality with custom transformers which can do this work for us.
How about glob paths? Something like that |
Another solution is tsc-alias which generates relative paths. |
unfortunately, the native imports mapping forces you to rewrite all of your path aliases to be start with '#' which breaks your codebase |
this doesn't work if you set |
this will force you make some changes to your codebase that introduce breaking change
|
For those who say changing '@' to '#' will break their codebase, is it possible for you to do a find/replace in your codebase to fix the imports? We've done something like that before when needing to do other sweeping changes across the codebase, and it's worked out quite well as long as there was a pattern we could match on initially. |
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
which will be replaced with the actual path. changing '@' to '#' will not help |
One interesting thing I've come across recently when trying to run both tsc->node for the build/start script and ts-node for the dev script is that neither module-alias nor node.js' new import mapping support aliasing paths relative to the working directory. For example, my src folder contains Typescript files. Package.json contains a _moduleAliases alias from "@" to "dist", which is the folder typescript will build to. This works perfectly fine, the start script is essentially The issue occurs when running something like ts-node, the dev script is: In the end it was solved by using both module-alias and tsconfig-paths, the former using alias/paths config from package.json when running from dist/build folder. And the latter using alias/paths config from tsconfig.json when running from the src folder. All of this module/path aliasing stuff makes things so much tidier, but all implementations seem to rely on aliases to a static path. Imagine if for some reason I needed two separate copies of my code in different src or dist/build folders, which current path aliasing solutions it's impossible; imports in "src2" would reference files in the src folder. Edit: I've just spotted "tsc-alias" mentioned here, looking into it, it may be able to help with this issue. |
@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: // Does not work. VSCode does not show intellisense for Hello class.
import { Hello } from '#src/hello'
// Works
import { Hello } from './src/Hello.js' |
Would it be possible to resolve the path from subpath Imports? For example if i have this import for assets: "imports": {
"#config": "./src/config.json",
} How would i resolve the path to a variable? const path = getAliasPath("#config");
console.log(path); // "./src/config.json" or absolute path Edit: require.resolve('#config'); // absolute path to config.json |
@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. |
And what if Common is not it's own package? We should not be forced into anyone's idea of what a project should look like. Our project is NOT a distributed package, has complex needs, and one of those is to include files from libs outside the project. These libs are not in a package, and they need to be shared many places. One solution is to put a symlink to outside the project, in the project root. However this is not ideal. If anyone knows another way to create an alias outside the project, please post it. |
@TheDirigible why not make Common into its own package so it can be added as a dependency? You of course can setup a project however you like. No one is trying to force anything, but I think the idea here is that NPM has features and functionality to include files and libs from outside the project, so that installing it is a one command operation. If it's possible to make those changes in the project to set it up to work with the existing tooling, then configuration and managing it could potentially be a lot easier. |
@eng-dibo Local dependencies that aren't published to NPM are registered like this. "dependencies": {
"@some/package": "file:./path/to/package",
} I'm not sure what makes you think you have to update your code for this, nor why you're using import aliases for local packages when the file dependency specifier has existed for quite some time now. |
Common was very clearly a package of some description in their example, seemingly as a shared library. Aside from making everything you want to use an actual dependency, there is no solution that makes sense. |
@Nytelife26 I'm struggling with the same, and this is the first time I do this, so I might be mistaken, but I spent the better part of two days with this. It looks like the local dependency you mention has the significant drawback that the building process will not build the dependent project even if needed, It looks like the only way to make TSC build it automatically is to use project references. This is exactly cut out for directly this purpose (compartmentalize your own project, without the need of external dependencies, into subprojects that can reference each other and the build system will take care of the rest), and has been available since TS 3.0. However, I have a problem with that approach you suggested. While IDE build and intellisensing works all right, running does not. As I can see now, The only workaround I can see now is to cheat:
where |
I kept going on and I think I have the solution, without symlinks. It's rather fragile, in the sense that it needs a very carefully balanced folder structure to work. I'll keep the client-server-common paradigm for the discussion, both the client and the server depending on the common project. Let's start with the topmost, "solution" folder. Apart from other settings, it needs a
And a
Both the client and the server packages need a
The common project needs a
I found that if the External dependencies are left for the specific case, each subproject needs its own dependencies and the topmost |
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 |
@jjangga0214 I think first solution subpath importing is enough in most cases. Is there any technical reason not to use subpath importing? |
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
andimport
(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.
The text was updated successfully, but these errors were encountered: