-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
feat: v6 - Environment API #16471
base: main
Are you sure you want to change the base?
feat: v6 - Environment API #16471
Conversation
Run & review this pull request in StackBlitz Codeflow. |
Co-authored-by: Vladimir Sheremet <sleuths.slews0s@icloud.com> Co-authored-by: Hiroshi Ogawa <hi.ogawa.zz@gmail.com> Co-authored-by: 翠 / green <green@sapphi.red> Co-authored-by: Jun Shindo <46585162+jay-es@users.noreply.github.com> Co-authored-by: Greg T. Wallace <greg@gregtwallace.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Caven <cavenasdev@gmail.com> Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Co-authored-by: Matyáš Racek <panstromek@seznam.cz> Co-authored-by: bluwy <bjornlu.dev@gmail.com>
Co-authored-by: Igor Minar <i@igor.dev> Co-authored-by: Vladimir <sleuths.slews0s@icloud.com> Co-authored-by: 翠 / green <green@sapphi.red> Co-authored-by: Clément <clemvnt@gmail.com>
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
…in the PR and discussions now
Co-authored-by: patak-dev <matias.capeletto@gmail.com>
/ecosystem-ci run |
📝 Ran ecosystem CI on
✅ analogjs, ladle, laravel, nuxt, quasar, unocss, vite-plugin-pwa, vite-plugin-react, vite-plugin-react-pages, vite-plugin-react-swc, vite-plugin-svelte, vite-plugin-vue, vite-setup-catalogue |
|
||
const resolved = tryNodeResolve( | ||
url, | ||
importer, | ||
{ ...resolveOptions, tryEsmOnly: true }, | ||
false, | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these options correct?
this fetchModule
is what the DevEnvironment
class uses right?
these options seem to be specifically targeting node? (given the .cjs
extension and the main
field usage)
should this be make more generic, or actually could this be made configurable?
(so that DevEnvironment
instances could tweak these options as needed?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or actually, shouldn't this inherit from the environment's options, some fields are indeed inherited:
https://github.com/vitejs/vite/pull/16471/files/2b3329d4aa0b91a5920527a9712b82ae59becba3#diff-fde6fc19368b69a4726cb38039db6dee6abf54bc69e1484c4f6e6c9d952acd9dR40-R42
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the same options that were used for ssrLoadModule
before. It might be a good idea to provide a way to customize it, but you can also intercept the fetchModule
call at any time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by intercepting the call? 🙏
Right now what I am doing is creating a new DevEnvironment
instance (source):
const devEnv = new ViteDevEnvironment(name, config, {
hot,
}) as DevEnvironment;
and then using the instance's fetchModule
in order to let vite apply its module resolution (source):
const result: any = await devEnv.fetchModule(...(args as [any, any]));
So that I can feed this result back to the module runner (source):
transport: {
fetchModule: async (...args) => {
const response = await env.__viteFetchModule.fetch(
new Request('http://localhost', {
method: 'POST',
body: JSON.stringify(args),
}),
);
const result = response.json();
return result as any;
},
},
is there a different way which would allow me to fetch the modules with my own resolve options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sheremet-va me and Igor have been looking more into this and we agree that the default node environment should preserve the ssrLoadModule
behavior.
The fetchModule
implementation in this class is however generally useful for custom environments, and in fact it's already exposed as part of the Environment API, so custom environments get to benefit from it.
For the custom environments to however truly benefit from this implementation, the environment config needs to be honored as I've pointed out above.
In order to preserve the behavior for the default node environment, we just need to ensure that it's default node environment is configured in a way that preserves the original ssrLoadModule
implementation.
Would this work for you or are we missing anything else that complicates the matter? Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see what createIdResolver
does here:
vite/packages/vite/src/node/idResolver.ts
Line 33 in bbca0b4
async function resolve( |
resolve.conditions
, resolve.alias
still needs to be worked out but that will also work). It is the alias+resolve plugins, instead of the whole plugin pipeline for the environment so custom resolving added by users won't apply (optimized deps ids also don't play a role here). It is currently implemented using these two plugins to reuse the implementation, but I think it would be better at some point to have the resolve logic as free functions we can call instead of needing a plugins container for two plugins.Internally, we are using this resolver to implement custom resolution. See for example resolving of CSS preprocessor files, that have their own rules:
vite/packages/vite/src/node/plugins/css.ts
Line 1067 in bbca0b4
return (sassResolve ??= createIdResolver(config, { |
We also use it to make esbuild follow Vite alias and resolve rules during dependency optimization, so it matches what Rollup will do during build. See here
// default resolver which prefers ESM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! I'll need to thoroughly look into those! thanks so much 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PS: I am not sure why but I can't resolve this thread, if you want please feel free to resolve this thread since the original issue has been resolved and this might just add unnecessary noise to this PR at this point (and I can ping you on discord for any further discussion on the resolver situation 🙂)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyways he made a great point in the fact that we really need to have the module resolution applied here be the exact same as the one that will be used for the production build (so that this behaves as close as possible to what gets actually deployed), so:
But Vite leaves external modules as is (so, vue
will be left as vue
, not /node_modules/vue
) in production, there is no Vite resolution during build for SSR 🤔 (if noExternal
is used for build)
The workerd does the resolution. You said that it works in production somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workerd does the resolution. You said that it works in production somehow.
@sheremet-va not really 😅 I said that workerd doesn't do the resolution and that in production the modules are bundled in and not external. The issue is that they need to be external during development because they are cjs (which the Vite dev server can't handle).
So during build they are resolved by the Vite resolution and during development they unfortunately need to be external, but they should be resolved to, as close as possible, the same modules that would be used during the build time resolution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally posting my review of the api-vite-environment.md file. Many of the comments were written a few weeks ago, but I forgot to post them, I hope they didn't bit rot too much.
I'm super excited about this work! I think the docs will need one more thorough pass once the code has settled down more.
### Registering new environments using hooks | ||
Plugins can add new environments in the `config` hook: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing a bit to me.... the plugin here only "provides a configuration" for the environment and doesn't "add" or "create" an environment.
It's unclear from the text/API what runtime will actually power this newly configured environment. Will it be createNodeEnvironment
? Or something else? What do I need to do if I want to create an environment to power a web worker in by browser app, how do I create such environment in my plugin?
This coupling of transform/build configuration to the runtime and module runner is what I struggle with the most with the current design.
I think it's important to enable plugins to create new environments and for users to adjust the build/transform config for all (or specific) environments, but I think it is just as important for the plugin's environment not to be tied to a particular js runtime. I see signs of that desire in the current design, but I can't really see how it would work end to end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this comment may be outdated. Let me know if there is still doubts about how plugins are created by plugins or configured (for dev and build).
} | ||
``` | ||
- `this.environment` is the module execution environment where a file update is currently being processed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this
the only way you can expose the environment? Exposing APIs vai this
is hard to document/discover and it's especially confusing when this state is mutable as it is the case of environments. How about including it in the HotContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative to this.environment
is having an environment
param in every hook. We're using this
because this is how vanilla Rollup already exposes the plugin context to hooks, and what we provide at the environment
instance are Vite specific extensions to that context. See:
docs/guide/api-vite-environment.md
Outdated
|
||
## Using environments in the Vite server | ||
|
||
A Vite dev server exposes two environments by default: a Client environment and a SSR environment. The client environment is a browser environment by default, and the module runner is implemented by importing the virtual module `/@vite/client` to client apps. The SSR environment runs in the same Node runtime as the Vite server by default and allows application servers to be used to render requests during dev with full HMR support. We'll discuss later how frameworks and users can change the environment types for the default client and SSR environments, or register new environments (for example to have a separate module graph for RSC). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've thought about the "client" name quite a bit and I wonder why not just call it a "browser" environment? Client feels simply too generic, as any environment connecting to the vite dev server is its "client".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this in person with @patak-dev and he made an argument that browser might need multiple environments (e.g. for web worker) and there are also other non-browser clients, like electron.
My 2c is that we should name environments by the purpose they serve rather than technology that powers them or generic category like "client" that can be easily misunderstood. So following this logic, the default browser environment could be called browserMain or browserUI to represent the browser main thread or ui envionment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling browserMain
to tauri doesn't seems to me to convey the purpose. I think the purpose of the browser, tauri, electron, web extension, etc is to be the client
of the app. There is another reason for the client
and ssr
naming in that it has been the way we have always called these two environments (there are options prefixed by ssr
and by client
in the config currently)
|
||
const resolved = tryNodeResolve( | ||
url, | ||
importer, | ||
{ ...resolveOptions, tryEsmOnly: true }, | ||
false, | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what is confusing here is that we use fetchModule to get hold of external modules. I believe usually fetchModule is meant to be called from runInlineModule only, because previously Vite made the assumption that once an import enters an external module the rest of the module resolution and evaluation will happen outside of Vite.
This assumption is valid for node which has access the the fs, but not valid for other runtimes that might need to rely on Vite and fetchModule to get hold of the script content for a given resolved module id.
In the latter case, we do want to honor the resolution configuration of the current environment, so that's why in Dario's remix PoC we patched Vite to do that.
Now we could add externalMainFields, etc but that raises three questions which I can't answer right now:
-
Why do we need to have a different set of resolution config attributes for external modules? Doesn't mainFields option only apply to resolving external modules nowadays anyway?
-
What should be the value of these external* options? And who determines that? Are the values just a copy of the original mainFields and friends?
-
What would happen if we didn't introduce external* options and instead honored the env specific mainFields and friends? What would break?
Co-authored-by: Igor Minar <i@igor.dev>
Co-authored-by: Igor Minar <i@igor.dev>
Co-authored-by: Igor Minar <i@igor.dev>
Co-authored-by: Igor Minar <i@igor.dev>
Co-authored-by: Igor Minar <i@igor.dev>
Co-authored-by: Igor Minar <i@igor.dev>
Description
Note
Check out the Environment API Docs
We're starting a new PR for the Environment API work in progress proposal for v6 to have a clean slate for reviews and discussions now that the implementation and docs are more stable.
This PR merges the work done in:
Refer to the discussions in these PRs for context. If you have general feedback about the APIs, let's use this discussion so we can have proper threads:
If you have comments about the implementation or would like to collaborate a feature, fix, test, or docs, please comment in this PR as an usual review or send a PR against the
v6/environment-api
branch.Ecosystem Compatibility
These projects are failing in CI because of known reasons.
server._importGlobMap
, which is moved