vite: `vite.config.ts` can't import untranspiled ts files from other packages in the same monorepo
Describe the bug
If we import something from symlink and the importee is ts file. We counter a such error:
failed to load config from /Users/zheeeng/Workspace/foo/bar/baz/vite.config.ts
error when starting dev server:
TypeError: defaultLoader is not a function
There are two workarounds: compile the ts file to the common js file, or specify the importee path to its real file path rather than symlink.
How could we use it without these two approaches?
Reproduction
https://github.com/zheeeng/test-symlink-vite-config
System Info
Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 1.60 GB / 16.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 14.17.0 - ~/.nvm/versions/node/v14.17.0/bin/node
Yarn: 1.22.11 - ~/.nvm/versions/node/v14.17.0/bin/yarn
npm: 6.14.13 - ~/.nvm/versions/node/v14.17.0/bin/npm
Browsers:
Chrome: 95.0.4638.54
Safari: 14.1.2
Used Package Manager
pnpm
Logs
failed to load config from /Users/zheeeng/Workspace/foo/bar/baz/vite.config.ts
error when starting dev server:
TypeError: defaultLoader is not a function
at Object.require.extensions.<computed> [as .ts] (/Users/zheeeng/Workspace/foo/node_modules/.pnpm/vite@2.6.5_less@4.1.2/node_modules/vite/dist/node/chunks/dep-55830a1a.js:68633:13)
at Module.load (internal/modules/cjs/loader.js:933:32)
at Function.Module._load (internal/modules/cjs/loader.js:774:14)
at Module.require (internal/modules/cjs/loader.js:957:19)
at require (internal/modules/cjs/helpers.js:88:18)
at Object.<anonymous> (/Users/zheeeng/Workspace/foo/web/studio/vite.config.ts:37:32)
at Module._compile (internal/modules/cjs/loader.js:1068:30)
at Object.require.extensions.<computed> [as .ts] (/Users/zheeeng/Workspace/foo/node_modules/.pnpm/vite@2.6.5_less@4.1.2/node_modules/vite/dist/node/chunks/dep-55830a1a.js:68630:20)
at Module.load (internal/modules/cjs/loader.js:933:32)
at Function.Module._load (internal/modules/cjs/loader.js:774:14)
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 https://github.com/vuejs/vue-next 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 3 years ago
- Reactions: 83
- Comments: 38 (8 by maintainers)
Commits related to this issue
- release:ts发布修改为js 1、ts在实际引用时会vite bug导致发生报错 见https://github.com/vitejs/vite/issues/5370 2、构建到js发布 — committed to yucccc/vitepress-plugin-props2table by yucccc 2 years ago
- Fixed imports of vite.config.extension.ts from projects Path that was being used for fs.existsSync was not correct, missing .ts on the end, so no extensions were being found or applied. Import of ex... — committed to EtherealEngine/etherealengine by barankyle 6 months ago
- Fixed imports of vite.config.extension.ts from projects Path that was being used for fs.existsSync was not correct, missing .ts on the end, so no extensions were being found or applied. Import of ex... — committed to EtherealEngine/etherealengine by barankyle 6 months ago
- Fixed imports of vite.config.extension.ts from projects (#9622) Path that was being used for fs.existsSync was not correct, missing .ts on the end, so no extensions were being found or applied. I... — committed to EtherealEngine/etherealengine by barankyle 5 months ago
Are we still looking at it? because it is very useful when it comes to monorepo and workspaces. Even more so when we can create and use plugins in simple and shared ways within the project.
May be true, but the expectation from a user’s point of view is a little different. If typescript support for configs is coming out of the box, it should be supported fully. In my point of view there’s only two ways of solving this:
🤷🏻 it’s not much tbh. compared to the drama llama of your current situation, i’d happily pick preconstruct.
What worked as a workaround for me was using a relative import inside the monorepo instead of absolute package paths. So assuming you have a monorepo with
app/
andplugin/
in rootpackages/
, inpackages/app/vite.config.ts
instead of importingjust used
It’s not ideal since you are importing from outside of your package, so still hoping this issue gets a proper fix in Vite.
I just realized that we could use a loader like
esbuild-register
for config loading.I’m not sure if we can use
esbuild-register
directly. For now, I can think of a few edge cases:tsconfig.json
isn’t correctly respected. We need to reuse https://github.com/dominikg/tsconfck for that.Anyway, this is a low-priority but doable feature. We can fix it when we are going to refactor the configuration loading logic in the future.
Just remembered of jiti, and it seems to have the mechanism we need to programmatically load the config, and addresses my previous comment’s concern.
jiti
however seems to have a rather large bundle size. We could probably implement something simlar, leveraging our own esbuild API too. So maybe there’s a way to solve this and #9202.use preconstruct on your packages… then you can just sidestep all this headache.
https://preconstruct.tools/
it sets up mjs and cjs stubs that behave differently in development than in production. 👍🏻
Now that vite 3 is using esm, I’m getting an issue with importing *.ts files in monorepo packages (
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"
). The “right” answer is to compile the *.ts files. I would love to avoid the extra build step, as it takes development maintenance, time, complexity, & makes the DX considerably worse. Bun will handle this but it’s is 6+ months away. At this point, what would be the best way to import *.ts files without having to compile those files? I get that it’s a low priority & the “right” answer is to compile the *.ts files, but it sure would be great if it were not necessary.TSX has been working great for running from the cli. Importing *.ts from monorepo packages worked in vite v2. Now how do we get the automatic transpilation of *.ts back in vite3 + nodejs?
There seems to be a confluence of underlying approaches which cause this problem. The need to automatically transpile ts to js. The need to use a monorepo with separate npm packages. The need to have a quick build…etc
Agreed. The problem with partial support for TypeScript is that the support becomes a moving target…such as the
ERR_UNKNOWN_FILE_EXTENSION
regression between v2 & v3…which makes for some nasty surprises when upgrading.This solution has worked for me:
https://github.com/privatenumber/tsx
"NODE_OPTIONS='--loader tsx/esm' vite ..." // node 18
"NODE_OPTIONS='--import tsx' vite ..." // node 20
I’ve recently run into same/similar problem when using a
pnpm
monorepo with some custom/user exports conditions inpackage.json
. I tried to work around this limitation with a DIY solution of my own. I first digged into vite’s code itself that allows TS support for config files to check if I can customize it, and then I decided to graduate the DIY workaround to its ownnpm
package called import-single-ts.At the moment that’s how I set up the vite config in the porjects of my monorepo:
vite.config.ts
tovite.config.original.ts
vite.config.js
with contents of:And that’s it. It is inspired by how
vite
does this internally + howesbuild
’s node-resolve plugin works .Pros of import-single-ts for the
vite.config.ts
usecase:Some benefits compared to solutions suggested earlier in this thread:
vite
’s dist files@babel/register
,esbuild-register
orjiti.register()
which latch on require.extensions, add overhead and seem to be deprecated in nodeesbuild
.Cons of import-single-ts for the
vite.config.ts
usecase:You have an additional almost empty proxy
viter.config.js
file but I can live with this for now 🤷 .Final thoughts
I wonder if
vite
’s team is open to use enhance-resolve since this worked for me pretty much out of the box. There could already be such discussion, I knowenhanced-resolve
is created by webpack and vite has its own resolution logic and of course I don’t know the differences, I just saw thatenhanced-resolve
worked for me so I’m mentioning it.In my specific use-case as I mentioned I use custom exports conditions which I define like this in the
vite.config.js
:@airtonix does that not need to setup babel and stuff to compile everything? The whole reason we use Vite is for the dx and no need to setup a lot of additional babel stuff.
Just wanted to put out another option that I see mentioned once but as something to look for in the future - I’ve simply swapped to using
bun
to run Vite, and there isn’t any issue anymore…This is where I found how to use bun with vite: https://github.com/bluwy/bun-vite-ts-test/blob/master/package.json
This is precisely the point of preconstruct.
Feels like everyone is trying to reinvent nodejs to avoid using preconstruct?
because there are several problems here you can keep using your project references with preconstruct.
paths
, or b. just use normal nodejs way where each package has it’s own package.json and then you use preconstruct to provide easy no-compile access to ts and non ts tooling.If you go for 2.a, then you cant dogfood your own typescript tooling packages with any tooling you use that doesn’t know how to typescript.
because of that, i choose to work with preconstruct
We’re facing the same issue at Nx. Related issue: https://github.com/nrwl/nx/issues/17019#issuecomment-1561452476
and repro: https://github.com/mandarini/vite-paths
For myself, I found a workaround for loading a
ts
file invite.config.ts
. I import thets
file throughjs
(js file like a proxy)@kdembler your workaround works for me too, I don’t like it but until Vite can fix this issue it’ll have to do, this is definitely the simplest workaround I’ve found so far, thanks!
@btakita
I couldn’t agree more, partial support for TS is a deal breaker in so many open source projects these days, either support it 100% or don’t, there is really no in between.
We also encounter this problem. Our use case is that we have a monorepo with 5 micro-frontends (vite apps). We want to have 1 base vite config file and extend from that. It would be really nice to have an option to import and/or extends other config files in
.ts
format without compiling first.Anyway, we ended up writing the base config file in plain js with
module.exports
.and then we could use like this:
Interesting. Tailwind had similar issues to this very ticket (https://github.com/vitejs/vite/issues/5370), they addressed it with jiti.
Thanks for sharing @maximan3000 ❤️ Solved my issue importing a typescript remark plugin into my
astro.config.ts
❌ Not Working:
absolute path
✔️ Working:
relative path
works for me, but requres node 18+, dependency tsx and permament setting NODE_OPTIONS env
As an alternative, relative path will work and requires 0 setup. In our team we’ll wait until out-of-box solution and then replace relative path to package name
PS: Using Bun instead of Node.js also good alternative
I also had this issue (a shared config that I want to import in several other workspaces), but just realized today that the simplest solution for us was to write the shared file as
*.mjs
and swap Typescript for JSDoc to keep type safety and intellisense.I realize this might be more work with shared plugins but for colocating a shared config, it was a breeze ✌️
If you want to use a monorepo/workspace with typescript, you should set it up correctly using project references with compilerOptions
composite: true
, since you can’t import an untranspiled.ts
file in a transpiled module. Every module (like a plugin) you import intovite.config.ts
is already transpiled tojs
. Even withoutvite
, the projects need to be referenced. While developing you should use ts-node or tsx, so you don’t need to rebuild the files all the time.There are many different ways to set up a working typescript workspace. I created an example vite-typescript-monorepo.