Skip to content

Commit 8fa70cd

Browse files
patak-devsapphi-redbluwy
authoredNov 13, 2024··
feat: extended applyToEnvironment and perEnvironmentPlugin (#18544)
Co-authored-by: 翠 / green <green@sapphi.red> Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
1 parent 87c5502 commit 8fa70cd

14 files changed

+116
-54
lines changed
 

‎docs/changes/shared-plugins-during-build.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ function PerEnvironmentCountTransformedModulesPlugin() {
5959
}
6060
```
6161

62-
To simplify this pattern, internally in Vite, we use a `usePerEnvironmentState` helper:
62+
To simplify this pattern, Vite exports a `perEnvironmentState` helper:
6363

6464
```js
6565
function PerEnvironmentCountTransformedModulesPlugin() {
66-
const state = usePerEnvironmentState<{ count: number }>(() => ({ count: 0 }))
66+
const state = perEnvironmentState<{ count: number }>(() => ({ count: 0 }))
6767
return {
6868
name: 'count-transformed-modules',
6969
perEnvironmentStartEndDuringDev: true,

‎docs/guide/api-environment-plugins.md

+33-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ const UnoCssPlugin = () => {
142142
// use global hooks normally
143143
},
144144
applyToEnvironment(environment) {
145-
// return true if this plugin should be active in this environment
145+
// return true if this plugin should be active in this environment,
146+
// or return a new plugin to replace it.
146147
// if the hook is not used, the plugin is active in all environments
147148
},
148149
resolveId(id, importer) {
@@ -152,6 +153,37 @@ const UnoCssPlugin = () => {
152153
}
153154
```
154155
156+
If a plugin isn't environment aware and has state that isn't keyed on the current environment, the `applyToEnvironment` hook allows to easily make it per-environment.
157+
158+
```js
159+
import { nonShareablePlugin } from 'non-shareable-plugin'
160+
161+
export default defineConfig({
162+
plugins: [
163+
{
164+
name: 'per-environment-plugin',
165+
applyToEnvironment(environment) {
166+
return nonShareablePlugin({ outputName: environment.name })
167+
},
168+
},
169+
],
170+
})
171+
```
172+
173+
Vite exports a `perEnvironmentPlugin` helper to simplify these cases where no other hooks are required:
174+
175+
```js
176+
import { nonShareablePlugin } from 'non-shareable-plugin'
177+
178+
export default defineConfig({
179+
plugins: [
180+
perEnvironmentPlugin('per-environment-plugin', (environment) =>
181+
nonShareablePlugin({ outputName: environment.name }),
182+
),
183+
],
184+
})
185+
```
186+
155187
## Environment in build hooks
156188
157189
In the same way as during dev, plugin hooks also receive the environment instance during build, replacing the `ssr` boolean.

‎packages/vite/src/node/build.ts

+18-27
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import {
5050
normalizePath,
5151
partialEncodeURIPath,
5252
} from './utils'
53-
import { resolveEnvironmentPlugins } from './plugin'
53+
import { perEnvironmentPlugin, resolveEnvironmentPlugins } from './plugin'
5454
import { manifestPlugin } from './plugins/manifest'
5555
import type { Logger } from './logger'
5656
import { dataURIPlugin } from './plugins/dataUri'
@@ -473,34 +473,26 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{
473473
pre: Plugin[]
474474
post: Plugin[]
475475
}> {
476-
const { commonjsOptions } = config.build
477-
const usePluginCommonjs =
478-
!Array.isArray(commonjsOptions.include) ||
479-
commonjsOptions.include.length !== 0
480476
return {
481477
pre: [
482478
completeSystemWrapPlugin(),
483-
/**
484-
* environment.config.build.commonjsOptions isn't currently supported
485-
* when builder.sharedConfigBuild or builder.sharedPlugins enabled.
486-
* To do it, we could inject one commonjs plugin per environment with
487-
* an applyToEnvironment hook.
488-
*/
489-
...(usePluginCommonjs ? [commonjsPlugin(commonjsOptions)] : []),
479+
perEnvironmentPlugin('commonjs', (environment) => {
480+
const { commonjsOptions } = environment.config.build
481+
const usePluginCommonjs =
482+
!Array.isArray(commonjsOptions.include) ||
483+
commonjsOptions.include.length !== 0
484+
return usePluginCommonjs ? commonjsPlugin(commonjsOptions) : false
485+
}),
490486
dataURIPlugin(),
491-
/**
492-
* environment.config.build.rollupOptions.plugins isn't supported
493-
* when builder.sharedConfigBuild or builder.sharedPlugins is enabled.
494-
* To do it, we should add all these plugins to the global pipeline, each with
495-
* an applyToEnvironment hook. It is similar to letting the user add per
496-
* environment plugins giving them a environment.config.plugins option that
497-
* we decided against.
498-
* For backward compatibility, we are still injecting the rollup plugins
499-
* defined in the default root build options.
500-
*/
501-
...((
502-
await asyncFlatten(arraify(config.build.rollupOptions.plugins))
503-
).filter(Boolean) as Plugin[]),
487+
perEnvironmentPlugin(
488+
'vite:rollup-options-plugins',
489+
async (environment) =>
490+
(
491+
await asyncFlatten(
492+
arraify(environment.config.build.rollupOptions.plugins),
493+
)
494+
).filter(Boolean) as Plugin[],
495+
),
504496
...(config.isWorker ? [webWorkerPostPlugin()] : []),
505497
],
506498
post: [
@@ -1464,13 +1456,12 @@ export class BuildEnvironment extends BaseEnvironment {
14641456
super(name, config, options)
14651457
}
14661458

1467-
// TODO: This could be sync, discuss if applyToEnvironment should support async
14681459
async init(): Promise<void> {
14691460
if (this._initiated) {
14701461
return
14711462
}
14721463
this._initiated = true
1473-
this._plugins = resolveEnvironmentPlugins(this)
1464+
this._plugins = await resolveEnvironmentPlugins(this)
14741465
}
14751466
}
14761467

‎packages/vite/src/node/environment.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ export type Environment =
1414
* Creates a function that hides the complexities of a WeakMap with an initial value
1515
* to implement object metadata. Used by plugins to implement cross hooks per
1616
* environment metadata
17+
*
18+
* @experimental
1719
*/
18-
export function usePerEnvironmentState<State>(
20+
export function perEnvironmentState<State>(
1921
initial: (environment: Environment) => State,
2022
): (context: PluginContext) => State {
2123
const stateMap = new WeakMap<Environment, State>()

‎packages/vite/src/node/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export {
88
resolveConfig,
99
sortUserPlugins,
1010
} from './config'
11+
export { perEnvironmentPlugin } from './plugin'
12+
export { perEnvironmentState } from './environment'
1113
export { createServer } from './server'
1214
export { preview } from './preview'
1315
export { build, createBuilder } from './build'

‎packages/vite/src/node/optimizer/scan.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class ScanEnvironment extends BaseEnvironment {
6363
return
6464
}
6565
this._initiated = true
66-
this._plugins = resolveEnvironmentPlugins(this)
66+
this._plugins = await resolveEnvironmentPlugins(this)
6767
this._pluginContainer = await createEnvironmentPluginContainer(
6868
this,
6969
this.plugins,

‎packages/vite/src/node/plugin.ts

+43-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import type { ModuleNode } from './server/mixedModuleGraph'
2222
import type { HmrContext, HotUpdateOptions } from './server/hmr'
2323
import type { DevEnvironment } from './server/environment'
2424
import type { Environment } from './environment'
25+
import type { PartialEnvironment } from './baseEnvironment'
2526
import type { PreviewServerHook } from './preview'
27+
import { arraify, asyncFlatten } from './utils'
2628

2729
/**
2830
* Vite plugins extends the Rollup plugin interface with a few extra
@@ -203,8 +205,11 @@ export interface Plugin<A = any> extends RollupPlugin<A> {
203205
/**
204206
* Define environments where this plugin should be active
205207
* By default, the plugin is active in all environments
208+
* @experimental
206209
*/
207-
applyToEnvironment?: (environment: Environment) => boolean
210+
applyToEnvironment?: (
211+
environment: PartialEnvironment,
212+
) => boolean | Promise<boolean> | PluginOption
208213
/**
209214
* Modify vite config before it's resolved. The hook can either mutate the
210215
* passed-in config directly, or return a partial config object that will be
@@ -324,11 +329,41 @@ type FalsyPlugin = false | null | undefined
324329

325330
export type PluginOption = Thenable<Plugin | FalsyPlugin | PluginOption[]>
326331

327-
export function resolveEnvironmentPlugins(environment: Environment): Plugin[] {
328-
return environment
329-
.getTopLevelConfig()
330-
.plugins.filter(
331-
(plugin) =>
332-
!plugin.applyToEnvironment || plugin.applyToEnvironment(environment),
333-
)
332+
export async function resolveEnvironmentPlugins(
333+
environment: PartialEnvironment,
334+
): Promise<Plugin[]> {
335+
const environmentPlugins: Plugin[] = []
336+
for (const plugin of environment.getTopLevelConfig().plugins) {
337+
if (plugin.applyToEnvironment) {
338+
const applied = await plugin.applyToEnvironment(environment)
339+
if (!applied) {
340+
continue
341+
}
342+
if (applied !== true) {
343+
environmentPlugins.push(
344+
...((await asyncFlatten(arraify(applied))).filter(
345+
Boolean,
346+
) as Plugin[]),
347+
)
348+
continue
349+
}
350+
}
351+
environmentPlugins.push(plugin)
352+
}
353+
return environmentPlugins
354+
}
355+
356+
/**
357+
* @experimental
358+
*/
359+
export function perEnvironmentPlugin(
360+
name: string,
361+
applyToEnvironment: (
362+
environment: PartialEnvironment,
363+
) => boolean | Promise<boolean> | PluginOption,
364+
): Plugin {
365+
return {
366+
name,
367+
applyToEnvironment,
368+
}
334369
}

‎packages/vite/src/node/plugins/clientInjections.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Plugin } from '../plugin'
33
import type { ResolvedConfig } from '../config'
44
import { CLIENT_ENTRY, ENV_ENTRY } from '../constants'
55
import { isObject, normalizePath, resolveHostname } from '../utils'
6-
import { usePerEnvironmentState } from '../environment'
6+
import { perEnvironmentState } from '../environment'
77
import { replaceDefine, serializeDefine } from './define'
88

99
// ids in transform are normalized to unix style
@@ -17,7 +17,7 @@ const normalizedEnvEntry = normalizePath(ENV_ENTRY)
1717
export function clientInjectionsPlugin(config: ResolvedConfig): Plugin {
1818
let injectConfigValues: (code: string) => string
1919

20-
const getDefineReplacer = usePerEnvironmentState((environment) => {
20+
const getDefineReplacer = perEnvironmentState((environment) => {
2121
const userDefine: Record<string, any> = {}
2222
for (const key in environment.config.define) {
2323
// import.meta.env.* is handled in `importAnalysis` plugin

‎packages/vite/src/node/plugins/dynamicImportVars.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
urlRE,
1919
} from '../utils'
2020
import type { Environment } from '../environment'
21-
import { usePerEnvironmentState } from '../environment'
21+
import { perEnvironmentState } from '../environment'
2222
import { hasViteIgnoreRE } from './importAnalysis'
2323
import { workerOrSharedWorkerRE } from './worker'
2424

@@ -171,7 +171,7 @@ export function dynamicImportVarsPlugin(config: ResolvedConfig): Plugin {
171171
extensions: [],
172172
})
173173

174-
const getFilter = usePerEnvironmentState((environment: Environment) => {
174+
const getFilter = perEnvironmentState((environment: Environment) => {
175175
const { include, exclude } =
176176
environment.config.build.dynamicImportVarsOptions
177177
return createFilter(include, exclude)

‎packages/vite/src/node/plugins/html.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { toOutputFilePathInHtml } from '../build'
3232
import { resolveEnvPrefix } from '../env'
3333
import type { Logger } from '../logger'
3434
import { cleanUrl } from '../../shared/utils'
35-
import { usePerEnvironmentState } from '../environment'
35+
import { perEnvironmentState } from '../environment'
3636
import { getNodeAssetAttributes } from '../assetSource'
3737
import {
3838
assetUrlRE,
@@ -330,7 +330,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
330330
preHooks.push(htmlEnvHook(config))
331331
postHooks.push(injectNonceAttributeTagHook(config))
332332
postHooks.push(postImportMapHook())
333-
const processedHtml = usePerEnvironmentState(() => new Map<string, string>())
333+
const processedHtml = perEnvironmentState(() => new Map<string, string>())
334334

335335
const isExcludedUrl = (url: string) =>
336336
url[0] === '#' || isExternalUrl(url) || isDataUrl(url)

‎packages/vite/src/node/plugins/manifest.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
} from 'rollup'
88
import type { Plugin } from '../plugin'
99
import { normalizePath, sortObjectKeys } from '../utils'
10-
import { usePerEnvironmentState } from '../environment'
10+
import { perEnvironmentState } from '../environment'
1111
import { cssEntriesMap } from './asset'
1212

1313
const endsWithJSRE = /\.[cm]?js$/
@@ -27,7 +27,7 @@ export interface ManifestChunk {
2727
}
2828

2929
export function manifestPlugin(): Plugin {
30-
const getState = usePerEnvironmentState(() => {
30+
const getState = perEnvironmentState(() => {
3131
return {
3232
manifest: {} as Manifest,
3333
outputCount: 0,

‎packages/vite/src/node/plugins/reporter.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { OutputBundle } from 'rollup'
66
import type { Plugin } from '../plugin'
77
import type { ResolvedConfig } from '../config'
88
import type { Environment } from '../environment'
9-
import { usePerEnvironmentState } from '../environment'
9+
import { perEnvironmentState } from '../environment'
1010
import { isDefined, isInNodeModules, normalizePath } from '../utils'
1111
import { LogLevels } from '../logger'
1212
import { withTrailingSlash } from '../../shared/utils'
@@ -40,7 +40,7 @@ export function buildReporterPlugin(config: ResolvedConfig): Plugin {
4040
const tty = process.stdout.isTTY && !process.env.CI
4141
const shouldLogInfo = LogLevels[config.logLevel || 'info'] >= LogLevels.info
4242

43-
const modulesReporter = usePerEnvironmentState((environment: Environment) => {
43+
const modulesReporter = perEnvironmentState((environment: Environment) => {
4444
let hasTransformed = false
4545
let transformedCount = 0
4646

@@ -83,7 +83,7 @@ export function buildReporterPlugin(config: ResolvedConfig): Plugin {
8383
}
8484
})
8585

86-
const chunksReporter = usePerEnvironmentState((environment: Environment) => {
86+
const chunksReporter = perEnvironmentState((environment: Environment) => {
8787
let hasRenderedChunk = false
8888
let hasCompressChunk = false
8989
let chunkCount = 0

‎packages/vite/src/node/server/environment.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export class DevEnvironment extends BaseEnvironment {
167167
return
168168
}
169169
this._initiated = true
170-
this._plugins = resolveEnvironmentPlugins(this)
170+
this._plugins = await resolveEnvironmentPlugins(this)
171171
this._pluginContainer = await createEnvironmentPluginContainer(
172172
this,
173173
this._plugins,

‎packages/vite/src/node/ssr/ssrManifestPlugin.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import {
1414
numberToPos,
1515
sortObjectKeys,
1616
} from '../utils'
17-
import { usePerEnvironmentState } from '../environment'
17+
import { perEnvironmentState } from '../environment'
1818

1919
export function ssrManifestPlugin(): Plugin {
2020
// module id => preload assets mapping
21-
const getSsrManifest = usePerEnvironmentState(() => {
21+
const getSsrManifest = perEnvironmentState(() => {
2222
return {} as Record<string, string[]>
2323
})
2424

0 commit comments

Comments
 (0)
Please sign in to comment.