vite: Can't dynamically import a node module with a variable
Describe the bug
Hi! I’ve ran into an issue where I can’t import a dependency from node modules with a variable. But without a variable, it works. I also tried using a variable for a “local” import and that also works.
I’ve attached a minimal reproduction, that uses dayjs as an example.
Reproduction
https://stackblitz.com/edit/vitejs-vite-rb9v1k?file=src%2FApp.tsx
(check console)
Steps to reproduce
No response
System Info
System:
OS: macOS 13.3
CPU: (8) arm64 Apple M1
Memory: 119.72 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.3.1 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 9.8.0 - /usr/local/bin/npm
pnpm: 8.2.0 - /usr/local/bin/pnpm
Browsers:
Chrome: 115.0.5790.170
Firefox Nightly: 114.0a1
Safari: 16.4
Used Package Manager
yarn
Logs
TypeError: Failed to resolve module specifier 'dayjs/locale/en'
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn’t already an issue that reports the same bug to avoid creating a duplicate.
- Make sure this is a Vite issue and not a framework-specific issue. For example, if it’s a Vue SFC related bug, it should likely be reported to vuejs/core instead.
- Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- The provided reproduction is a minimal reproducible example of the bug.
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 14
- Comments: 23 (7 by maintainers)
We’re not intentionally not supporting it, it’s that this usecase wasn’t considered in the initial design/support for glob imports. It started out as a way to glob relative files so it worked for relative paths, but then it slowly extended out, e.g. supporting globbing with
resolve.alias, and now the request for node_modules.node_modulesin particular is harder because packages can declare specific ways to export files. In particular, figuring out the possible values oflangwithin@company/i18n-json/${lang}/index.jsonisn’t an easy task. There is the logic implemented today, but making it work for dynamic imports is another task.We’re definitely open to a PR if someone submits one. For accepted enhancements we have the “enhancement” label.
If you’d like to see this feature implemented, you can also leave a 👍 to the issue.
If you’d like to workaround at the meantime, it’s doable to also write a plugin that generates the list of dynamic imports and inject it into your code. Essentially doing the globbing manually if you know what the langs are.
@bluwy Excuse me, I’d like to know if the reason for not supporting this feature for now is because the Vite or Rollup team assesses the implementation cost may be too high? What confuses me is that dynamic import of a node package name literal is currently supported, so I think the bundler should be able to statically analyze the absolute path of the node package correctly. Why does adding support for glob matching functionality bring the restriction of having to import resources with relative paths?
I have a webpack project that can correctly split chunks as intention when both variables and packages appear in the path, but I encountered this feature unsupported during the migration to a Vite project.
May I ask if this feature is hard to implement due to differences in dynamic import parsing algorithms between webpack and Vite/Rollup under the hood? Or is there any other design consideration or trade off for Vite/Rollup chose not to support it, like hoping follow to the ESM specification strictly etc.?
I understand the complexity of the module search algorithm currently with the introduction of Node submodules, and I appreciate the work of the community team. Please forgive me if I said anything incorrect or offended.
The main starting point would be the
importMetaGlobVite plugin. This line in particular is what Vite has today to resolve glob paths:https://github.com/vitejs/vite/blob/a43167ca3f0feeac9e58c0bc809b4d1acf0e1ec6/packages/vite/src/node/plugins/importMetaGlob.ts#L544-L548
(search for
resolveIdin that file to follow how the path is resolved)Also note that dynamic imports with variables are also transpiled as
import.meta.globtoday, so you’ll need to make sure the transpiled code is correctly accessing from the result ofimport.meta.glob:https://github.com/vitejs/vite/blob/a43167ca3f0feeac9e58c0bc809b4d1acf0e1ec6/packages/vite/src/node/plugins/dynamicImportVars.ts#L241-L248
Making this work will be tricky but not impossible. When you do
import(`dayjs/locale/${'en'}`), what happens is that Vite needs to globdayjs/locale/*to identify the possible values of*and bundle it all up. We’re able to support the glob syntax today withoptimizeDeps.include, and this translates to supporting the glob syntax inimport.meta.globtoo as the dynamic import will be transpiled as animport.meta.glob.I am not quite sure if this is exact the same issue, but letting the comment anyway because I could not find it explicitly this way.
I was trying to make this work:
It would work on dev, but build would create just translation file to
en-us. So “production” version would work just in english.So after digging a bit on what @bluwy said on
I’ve came to this solution below. Worked pretty well for me, loading just the correct file at the correct time, for both dev and build. Let me know if this is an anti-pattern or something like that.
Thk for your detailed explanation! I will keep an eye on it.
For those who may face the same problem as me can try this temporarily as long as your project lists the dependencies correctly:
I know this may feel a bit dirty and definitely not science, but engineering 😃
Hi, how far are you on this matter?
I would also need the feature to “dynamically globally import node_modules” for localization purposes. I am working on an app with 9 languages/locales and have two dependencies, which have to be set correctly, with a proper locale. I do not want to load all the localization settings, for all the languages. This worked with Webpack perfectly and now switching to Vite, this is the only “open” thing, for me to fix. Thanks and kind regards!
No worries! I haven’t had much time over the weekend anyways. I’ll take a last look over this weekend to see if I can hack something together with the new info. I really appreciate your guidance ❤️
Sorry for the late reply. I’ve been focusing on getting Vite 5 out and missed this.
resolvedFileNamebeingundefinedis expected I believe because IIUC you’re trying to resolvesome-pkg/**/*.jswhich doesn’t really exist.You’d first have to extract the library name
some-pkgand resolve that, then glob the files within that library. There’s a utility that does that: https://github.com/vitejs/vite/blob/a0c51237ab1c79b73b5894c4af67d8c6351aa961/packages/vite/src/node/optimizer/resolve.ts#L42-L45Though we can’t really use it completely to fix this cleanly. We might need to carve out a part in the
resolve.tsplugin for import globs only that can support resolving the globs (using some code from that utility). The standard resolve spec in Vite doesn’t resolve globs by default.Thanks for taking a look at it. Understandably it’s quite a big task to finish up. I’ll also remove the “contribution welcome” label for now since it’s a bigger task than expected, and we usually reserve it for simpler stuff. But we’ll still accept contributions if you like to look deeper into it!
You need strings inside
import()to be static like the first example. Most bundlers require that so it’s statically analyzable for bundling. I think it’s not quite related to this issue.