diff --git a/docs/guide/api-vite-environment.md b/docs/guide/api-vite-environment.md index fac53cbc67b397..dcc9bf70b185f2 100644 --- a/docs/guide/api-vite-environment.md +++ b/docs/guide/api-vite-environment.md @@ -58,7 +58,7 @@ class DevEnvironment { * Resolved plugins for this environment, including the ones * created using the per-environment `create` hook */ - plugins: EnvironmentPlugin[] + plugins: Plugin[] /** * Allows to resolve, load, and transform code through the * environment plugins pipeline @@ -579,19 +579,25 @@ The hook can choose to: ### Per-environment Plugins -A plugin can now also be a constructor to lazily create per-environment plugins. +A plugin can define what are the environments it should apply to with the `applyToEnvironment` function. ```js -function perEnvironmentPlugin() { - return (environment: Environment) => { - // Return a plugin, an array, a Promise, or a falsy value for each environment - if (!passesCondition(environment)) { - return undefined - } - return [ - createEnvironmentPlugin(environment), - otherPlugin(environment) - ] +const UnoCssPlugin = () => { + // shared global state + return { + buildStart() { + // init per environment state with WeakMap, this.environment + }, + configureServer() { + // use global hooks normally + }, + applyToEnvironment(environment) { + // return true if this plugin should be active in this environment + // if the function isn't provided, the plugin is active in all environments + }, + resolveId(id, importer) { + // only called for environments this plugin apply to + }, } } ``` @@ -881,29 +887,6 @@ function myPlugin() { } ``` -And for per-environment plugins: - -```js -function myPlugin() { - // Share state among all environments in dev and build - const sharedState = ... - - return { - name: 'with-environment-plugins', - environmentPlugins(environment) { - // Isolated state for each environment during dev and build - const isolatedState = ... - return { - name: 'per-environment-plugin', - transform(code, id) { ... }, - } - }, - // Opt-in into a single instance for all environments - sharedDuringBuild: true - } -} -``` - ## Backward Compatibility The current Vite server API will be deprecated but keep working during the next major. diff --git a/packages/vite/src/node/__tests_dts__/plugin.ts b/packages/vite/src/node/__tests_dts__/plugin.ts index 6844672accfa15..5b4ebeb82895c8 100644 --- a/packages/vite/src/node/__tests_dts__/plugin.ts +++ b/packages/vite/src/node/__tests_dts__/plugin.ts @@ -1,9 +1,9 @@ /** - * This is a developement only file for testing types. + * This is a development only file for testing types. */ import type { Plugin as RollupPlugin } from 'rollup' import type { Equal, ExpectExtends, ExpectTrue } from '@type-challenges/utils' -import type { EnvironmentPlugin, PluginContextExtension } from '../plugin' +import type { Plugin, PluginContextExtension } from '../plugin' import type { ROLLUP_HOOKS } from '../constants' import type { GetHookContextMap, @@ -11,7 +11,7 @@ import type { RollupPluginHooks, } from '../typeUtils' -type EnvironmentPluginHooksContext = GetHookContextMap +type EnvironmentPluginHooksContext = GetHookContextMap type EnvironmentPluginHooksContextMatched = { [K in keyof EnvironmentPluginHooksContext]: EnvironmentPluginHooksContext[K] extends PluginContextExtension ? never @@ -19,20 +19,20 @@ type EnvironmentPluginHooksContextMatched = { } type HooksMissingExtension = NonNeverKeys -type HooksMissingInConstans = Exclude< +type HooksMissingInConstants = Exclude< RollupPluginHooks, (typeof ROLLUP_HOOKS)[number] > export type cases = [ // Ensure environment plugin hooks are superset of rollup plugin hooks - ExpectTrue>, + ExpectTrue>, // Ensure all Rollup hooks have Vite's plugin context extension ExpectTrue>, // Ensure the `ROLLUP_HOOKS` constant is up-to-date - ExpectTrue>, + ExpectTrue>, ] export {} diff --git a/packages/vite/src/node/baseEnvironment.ts b/packages/vite/src/node/baseEnvironment.ts index 8de290697cc8bb..b03d8164b08974 100644 --- a/packages/vite/src/node/baseEnvironment.ts +++ b/packages/vite/src/node/baseEnvironment.ts @@ -1,7 +1,7 @@ import colors from 'picocolors' import type { Logger } from './logger' import type { ResolvedConfig, ResolvedEnvironmentOptions } from './config' -import type { EnvironmentPlugin } from './plugin' +import type { Plugin } from './plugin' export class PartialEnvironment { name: string @@ -61,7 +61,7 @@ export class PartialEnvironment { } export class BaseEnvironment extends PartialEnvironment { - get plugins(): EnvironmentPlugin[] { + get plugins(): Plugin[] { if (!this._plugins) throw new Error( `${this.name} environment.plugins called before initialized`, @@ -72,7 +72,7 @@ export class BaseEnvironment extends PartialEnvironment { /** * @internal */ - _plugins: EnvironmentPlugin[] | undefined + _plugins: Plugin[] | undefined /** * @internal */ diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index a8b042c4569bb6..0742a90da7c6bf 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -1480,12 +1480,13 @@ export class BuildEnvironment extends BaseEnvironment { super(name, config, options) } + // TODO: This could be sync, discuss if applyToEnvironment should support async async init(): Promise { if (this._initiated) { return } this._initiated = true - this._plugins = await resolveEnvironmentPlugins(this) + this._plugins = resolveEnvironmentPlugins(this) } } diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 58e19934c253dc..ce5fec1b02e969 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -52,13 +52,7 @@ export type { DevEnvironmentOptions, ResolvedDevEnvironmentOptions, } from './config' -export type { - EnvironmentPlugin, - Plugin, - EnvironmentPluginOptionArray, - PluginOption, - HookHandler, -} from './plugin' +export type { Plugin, PluginOption, HookHandler } from './plugin' export type { FilterPattern } from './utils' export type { CorsOptions, CorsOrigin, CommonServerOptions } from './http' export type { diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index c4164f9cab8ab3..02f547790f339f 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -64,7 +64,7 @@ export class ScanEnvironment extends BaseEnvironment { return } this._initiated = true - this._plugins = await resolveEnvironmentPlugins(this) + this._plugins = resolveEnvironmentPlugins(this) this._pluginContainer = await createEnvironmentPluginContainer( this, this.plugins, diff --git a/packages/vite/src/node/plugin.ts b/packages/vite/src/node/plugin.ts index 0a931bd6114c53..9799dca74cbb95 100644 --- a/packages/vite/src/node/plugin.ts +++ b/packages/vite/src/node/plugin.ts @@ -83,7 +83,7 @@ declare module 'rollup' { * Environment Plugins are closer to regular rollup plugins. They can't define * app level hooks (like config, configResolved, configureServer, etc). */ -export interface EnvironmentPlugin extends RollupPlugin { +export interface Plugin extends RollupPlugin { /** * Perform custom handling of HMR updates. * The handler receives a context containing changed filename, timestamp, a @@ -161,9 +161,6 @@ export interface EnvironmentPlugin extends RollupPlugin { }, ) => Promise | TransformResult > -} - -export interface Plugin extends EnvironmentPlugin { /** * Opt-in this plugin into the shared plugins pipeline. * For backward-compatibility, plugins are re-recreated for each environment @@ -194,6 +191,11 @@ export interface Plugin extends EnvironmentPlugin { | 'serve' | 'build' | ((this: void, config: UserConfig, env: ConfigEnv) => boolean) + /** + * Define environments where this plugin should be active + * By default, the plugin is active in all environments + */ + applyToEnvironment?: (environment: Environment) => boolean /** * Modify vite config before it's resolved. The hook can either mutate the * passed-in config directly, or return a partial config object that will be @@ -277,12 +279,6 @@ export interface Plugin extends EnvironmentPlugin { * `{ order: 'pre', handler: hook }` */ transformIndexHtml?: IndexHtmlTransform - /** - * Inject per environment plugins after the shared plugin - */ - environmentPlugins?: ( - environment: Environment, - ) => EnvironmentPluginOptionArray /** * @deprecated @@ -304,43 +300,14 @@ export type PluginWithRequiredHook = Plugin & { } type Thenable = T | Promise -type FalsyPlugin = false | null | undefined - -export type EnvironmentPluginOption = Thenable< - EnvironmentPlugin | FalsyPlugin | EnvironmentPluginOption[] -> -export type EnvironmentPluginOptionArray = Thenable< - EnvironmentPluginOption[] | FalsyPlugin -> +type FalsyPlugin = false | null | undefined export type PluginOption = Thenable -export async function resolveEnvironmentPlugins( - environment: Environment, -): Promise { - const resolvedPlugins: EnvironmentPlugin[] = [] - for (const plugin of environment.config.plugins) { - resolvedPlugins.push(plugin) - if (plugin.environmentPlugins) { - const environmentPlugins = await plugin.environmentPlugins(environment) - if (environmentPlugins) { - const newPlugins = - await asyncFlattenEnvironmentPlugins(environmentPlugins) - resolvedPlugins.push(...newPlugins) - } - } - } - return resolvedPlugins -} - -async function asyncFlattenEnvironmentPlugins( - plugins: EnvironmentPluginOption[], -): Promise { - do { - plugins = ((await Promise.all(plugins)) as any[]) - .flat(Infinity) - .filter(Boolean) as EnvironmentPluginOption[] - } while (plugins.some((v: any) => v?.then)) - return plugins as EnvironmentPlugin[] +export function resolveEnvironmentPlugins(environment: Environment): Plugin[] { + return environment.config.plugins.filter( + (plugin) => + !plugin.applyToEnvironment || plugin.applyToEnvironment(environment), + ) } diff --git a/packages/vite/src/node/server/environment.ts b/packages/vite/src/node/server/environment.ts index a4ddd12f06fb6e..742186e9a3ae1b 100644 --- a/packages/vite/src/node/server/environment.ts +++ b/packages/vite/src/node/server/environment.ts @@ -164,7 +164,7 @@ export class DevEnvironment extends BaseEnvironment { return } this._initiated = true - this._plugins = await resolveEnvironmentPlugins(this) + this._plugins = resolveEnvironmentPlugins(this) this._pluginContainer = await createEnvironmentPluginContainer( this, this._plugins, diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index dbf25c65ecbba4..07557b9b102c6e 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -61,7 +61,7 @@ import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping' import MagicString from 'magic-string' import type { FSWatcher } from 'chokidar' import colors from 'picocolors' -import type { EnvironmentPlugin, Plugin } from '../plugin' +import type { Plugin } from '../plugin' import { combineSourcemaps, createDebugger, @@ -149,7 +149,7 @@ type PluginContext = Omit< */ export async function createEnvironmentPluginContainer( environment: Environment, - plugins: EnvironmentPlugin[], + plugins: Plugin[], watcher?: FSWatcher, ): Promise { const {