Replace virtual modules with generated modules
#15212
Development PR
closes #15212 and helps with #1485
This PR writes the virtual env modules to disk so that they can be used in files that run outside of the Vite pipeline.
Some thoughts while implementing this:
It's not very nice that secrets can now exist in both the .env file and the generated module. But maybe it's fine because it's gitignored and bound to be overwritten when updated and sync runs?the dynamic module no longer serialises the .env filesvelte-kit syncused to only generate types but now it generates modules too so that we can use $env without starting the dev server.I noticed our test apps had tests in a "test" folder instead of "tests" (plural). Is that on purpose? Or should we include both variations in the generated tsconfig.json in case of mistakes like this? created#15254
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- This message body should clearly illustrate what problems it solves.
- Ideally, include a test that fails without this PR but passes with it.
Tests
- Run the tests with
pnpm testand lint the project withpnpm lintandpnpm check
Changesets
- If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running
pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.
Edits
- Please ensure that 'Allow edits from maintainers' is checked. PRs without this option may be closed.
Issue
Describe the problem
You can't use code in lib/db to setup your e2e tests if it uses $env because Vite doesn't run in the Playwright context
Describe the proposed solution
Replace virtual modules with generated modules
Alternatives considered
No response
Importance
would make my life easier
Additional Information
No response
Info
What does 'generated modules' mean?
The idea I had in my head was to write js/dts files to disk instead of loading them directly in Vite's load hook https://github.com/sveltejs/kit/blob/d25a8537aa22a49db9be72cbc7ff39dc5f85ed89/packages/kit/src/exports/vite/index.js#L476-L497 Then, we'd just need to add aliases to our generated tsconfig.json's paths like we're doing now with $lib. This should mean that anything using typescript will be able to pick up these modules.
When we eventually move to the Vite environment API, we'd need to do this or more virtual modules since we can't just store these things in the Node process anymore.
But how would Node's module resolver find these modules? Would they be written to node_modules/$env/... and so on? That seems unorthodox and a bit brittle
They can probably go in the .svelte-kit folder and we'd rely on tsconfig.json's aliasing. We can still use the load hook to help load them based when the module ID is requested. But with the tsconfig path setting, this should at least expose them to non-Vite tooling
EDIT: doing these for a lot of the virtual modules (where possible) probably helps close #1485 which is the original issue
tsconfig paths should not be used as source of truth for resolution, they're meant to inform typescript of foreign resolutions.
generating vite resolve aliases (or import maps in kit@3, changing to # prefix)? and then using those to add tsconfig paths should help. basically the inverse of vite-tsconfig-paths.
sidenote: rolldown has a feature that does resolve tsconfig paths, i don't like it, esp. given the complexities around tsconfig inheritance and merging.
I'll just drop my current setup here as I think it relates a lot to this.
src/common(aliased as$common) contains code that can be used from anywhere. Used by scripts ran directly with Node, project code through Vite, Playwright etc. To validate it, it's included in the default/projecttsconfig.json(which includes the entire src dir), but I also have a customtsconfig.jsonfor it (the one ), and I run bothsv checkand the customtsccheck.scripts- self-explanatory, scripts that are run directly usingnode scripts/<script>.ts, can import from./src/commonsrc/scripts- these are scripts that need access to more of the project code (and thus the Vite pipeline). They are run using
After making it work I've never had any issues with in. I do agree that TS paths should maybe be phased out. I like them, but I don't like the complexity it adds (like Node type-stripping ignores tsconfig.json). I think the ideal future is one where TS goes full erasable syntax only mode. TypeScript used to add many useful features, but I think we're at a point where plain JS and package.json-based config (supported by pretty much everyone) covers all use-cases (subpath imports, export conditions etc.).
Pro tip: You can prefix GitHub URLs of issues, PRs or discussions with svcl.dev/ to view them on this page! Also try it on a GitHub release ;)