Skip to content

Commit 757a92f

Browse files
authoredAug 22, 2022
feat: support object style hooks (#9634)
1 parent aca6ac2 commit 757a92f

File tree

20 files changed

+398
-223
lines changed

20 files changed

+398
-223
lines changed
 

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
"prettier": "2.7.1",
7878
"prompts": "^2.4.2",
7979
"rimraf": "^3.0.2",
80-
"rollup": ">=2.75.6 <2.77.0 || ~2.77.0",
80+
"rollup": "~2.78.0",
8181
"semver": "^7.3.7",
8282
"simple-git-hooks": "^2.8.0",
8383
"tslib": "^2.4.0",

‎packages/plugin-vue/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"@jridgewell/gen-mapping": "^0.3.2",
4343
"@jridgewell/trace-mapping": "^0.3.15",
4444
"debug": "^4.3.4",
45-
"rollup": ">=2.75.6 <2.77.0 || ~2.77.0",
45+
"rollup": "~2.78.0",
4646
"slash": "^4.0.0",
4747
"source-map": "^0.6.1",
4848
"vite": "workspace:*",

‎packages/vite/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"esbuild": "^0.14.47",
6262
"postcss": "^8.4.16",
6363
"resolve": "^1.22.1",
64-
"rollup": ">=2.75.6 <2.77.0 || ~2.77.0"
64+
"rollup": "~2.78.0"
6565
},
6666
"optionalDependencies": {
6767
"fsevents": "~2.3.2"

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

+39-14
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import type {
1616
WarningHandler,
1717
WatcherOptions
1818
} from 'rollup'
19-
import type Rollup from 'rollup'
2019
import type { Terser } from 'types/terser'
2120
import commonjsPlugin from '@rollup/plugin-commonjs'
2221
import type { RollupCommonJSOptions } from 'types/commonjs'
@@ -788,34 +787,60 @@ function injectSsrFlagToHooks(plugin: Plugin): Plugin {
788787
}
789788
}
790789

791-
function wrapSsrResolveId(
792-
fn?: Rollup.ResolveIdHook
793-
): Rollup.ResolveIdHook | undefined {
794-
if (!fn) return
790+
function wrapSsrResolveId(hook?: Plugin['resolveId']): Plugin['resolveId'] {
791+
if (!hook) return
795792

796-
return function (id, importer, options) {
793+
const fn = 'handler' in hook ? hook.handler : hook
794+
const handler: Plugin['resolveId'] = function (id, importer, options) {
797795
return fn.call(this, id, importer, injectSsrFlag(options))
798796
}
797+
798+
if ('handler' in hook) {
799+
return {
800+
...hook,
801+
handler
802+
} as Plugin['resolveId']
803+
} else {
804+
return handler
805+
}
799806
}
800807

801-
function wrapSsrLoad(fn?: Rollup.LoadHook): Rollup.LoadHook | undefined {
802-
if (!fn) return
808+
function wrapSsrLoad(hook?: Plugin['load']): Plugin['load'] {
809+
if (!hook) return
803810

804-
return function (id, ...args) {
811+
const fn = 'handler' in hook ? hook.handler : hook
812+
const handler: Plugin['load'] = function (id, ...args) {
805813
// @ts-expect-error: Receiving options param to be future-proof if Rollup adds it
806814
return fn.call(this, id, injectSsrFlag(args[0]))
807815
}
816+
817+
if ('handler' in hook) {
818+
return {
819+
...hook,
820+
handler
821+
} as Plugin['load']
822+
} else {
823+
return handler
824+
}
808825
}
809826

810-
function wrapSsrTransform(
811-
fn?: Rollup.TransformHook
812-
): Rollup.TransformHook | undefined {
813-
if (!fn) return
827+
function wrapSsrTransform(hook?: Plugin['transform']): Plugin['transform'] {
828+
if (!hook) return
814829

815-
return function (code, importer, ...args) {
830+
const fn = 'handler' in hook ? hook.handler : hook
831+
const handler: Plugin['transform'] = function (code, importer, ...args) {
816832
// @ts-expect-error: Receiving options param to be future-proof if Rollup adds it
817833
return fn.call(this, code, importer, injectSsrFlag(args[0]))
818834
}
835+
836+
if ('handler' in hook) {
837+
return {
838+
...hook,
839+
handler
840+
} as Plugin['transform']
841+
} else {
842+
return handler
843+
}
819844
}
820845

821846
function injectSsrFlag<T extends Record<string, any>>(

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

+44-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { Alias, AliasOptions } from 'types/alias'
88
import aliasPlugin from '@rollup/plugin-alias'
99
import { build } from 'esbuild'
1010
import type { RollupOptions } from 'rollup'
11-
import type { Plugin } from './plugin'
11+
import type { HookHandler, Plugin } from './plugin'
1212
import type {
1313
BuildOptions,
1414
RenderBuiltAssetUrl,
@@ -33,7 +33,11 @@ import {
3333
normalizeAlias,
3434
normalizePath
3535
} from './utils'
36-
import { resolvePlugins } from './plugins'
36+
import {
37+
createPluginHookUtils,
38+
getSortedPluginsByHook,
39+
resolvePlugins
40+
} from './plugins'
3741
import type { ESBuildOptions } from './plugins/esbuild'
3842
import {
3943
CLIENT_ENTRY,
@@ -289,7 +293,7 @@ export interface LegacyOptions {
289293
buildSsrCjsExternalHeuristics?: boolean
290294
}
291295

292-
export interface ResolveWorkerOptions {
296+
export interface ResolveWorkerOptions extends PluginHookUtils {
293297
format: 'es' | 'iife'
294298
plugins: Plugin[]
295299
rollupOptions: RollupOptions
@@ -334,9 +338,16 @@ export type ResolvedConfig = Readonly<
334338
worker: ResolveWorkerOptions
335339
appType: AppType
336340
experimental: ExperimentalOptions
337-
}
341+
} & PluginHookUtils
338342
>
339343

344+
export interface PluginHookUtils {
345+
getSortedPlugins: (hookName: keyof Plugin) => Plugin[]
346+
getSortedPluginHooks: <K extends keyof Plugin>(
347+
hookName: K
348+
) => NonNullable<HookHandler<Plugin[K]>>[]
349+
}
350+
340351
export type ResolveFn = (
341352
id: string,
342353
importer?: string,
@@ -431,9 +442,11 @@ export async function resolveConfig(
431442

432443
// run config hooks
433444
const userPlugins = [...prePlugins, ...normalPlugins, ...postPlugins]
434-
for (const p of userPlugins) {
435-
if (p.config) {
436-
const res = await p.config(config, configEnv)
445+
for (const p of getSortedPluginsByHook('config', userPlugins)) {
446+
const hook = p.config
447+
const handler = hook && 'handler' in hook ? hook.handler : hook
448+
if (handler) {
449+
const res = await handler(config, configEnv)
437450
if (res) {
438451
config = mergeConfig(config, res)
439452
}
@@ -598,9 +611,11 @@ export async function resolveConfig(
598611
...workerNormalPlugins,
599612
...workerPostPlugins
600613
]
601-
for (const p of workerUserPlugins) {
602-
if (p.config) {
603-
const res = await p.config(workerConfig, configEnv)
614+
for (const p of getSortedPluginsByHook('config', workerUserPlugins)) {
615+
const hook = p.config
616+
const handler = hook && 'handler' in hook ? hook.handler : hook
617+
if (handler) {
618+
const res = await handler(workerConfig, configEnv)
604619
if (res) {
605620
workerConfig = mergeConfig(workerConfig, res)
606621
}
@@ -609,7 +624,9 @@ export async function resolveConfig(
609624
const resolvedWorkerOptions: ResolveWorkerOptions = {
610625
format: workerConfig.worker?.format || 'iife',
611626
plugins: [],
612-
rollupOptions: workerConfig.worker?.rollupOptions || {}
627+
rollupOptions: workerConfig.worker?.rollupOptions || {},
628+
getSortedPlugins: undefined!,
629+
getSortedPluginHooks: undefined!
613630
}
614631

615632
const resolvedConfig: ResolvedConfig = {
@@ -660,7 +677,9 @@ export async function resolveConfig(
660677
importGlobRestoreExtension: false,
661678
hmrPartialAccept: false,
662679
...config.experimental
663-
}
680+
},
681+
getSortedPlugins: undefined!,
682+
getSortedPluginHooks: undefined!
664683
}
665684
const resolved: ResolvedConfig = {
666685
...config,
@@ -673,31 +692,34 @@ export async function resolveConfig(
673692
normalPlugins,
674693
postPlugins
675694
)
695+
Object.assign(resolved, createPluginHookUtils(resolved.plugins))
676696

677697
const workerResolved: ResolvedConfig = {
678698
...workerConfig,
679699
...resolvedConfig,
680700
isWorker: true,
681701
mainConfig: resolved
682702
}
683-
684703
resolvedConfig.worker.plugins = await resolvePlugins(
685704
workerResolved,
686705
workerPrePlugins,
687706
workerNormalPlugins,
688707
workerPostPlugins
689708
)
709+
Object.assign(
710+
resolvedConfig.worker,
711+
createPluginHookUtils(resolvedConfig.worker.plugins)
712+
)
690713

691714
// call configResolved hooks
692-
await Promise.all(
693-
userPlugins
694-
.map((p) => p.configResolved?.(resolved))
695-
.concat(
696-
resolvedConfig.worker.plugins.map((p) =>
697-
p.configResolved?.(workerResolved)
698-
)
699-
)
700-
)
715+
await Promise.all([
716+
...resolved
717+
.getSortedPluginHooks('configResolved')
718+
.map((hook) => hook(resolved)),
719+
...resolvedConfig.worker
720+
.getSortedPluginHooks('configResolved')
721+
.map((hook) => hook(workerResolved))
722+
])
701723

702724
// validate config
703725

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export type {
5050
SSRFormat,
5151
SSRTarget
5252
} from './ssr'
53-
export type { Plugin } from './plugin'
53+
export type { Plugin, HookHandler } from './plugin'
5454
export type { PackageCache, PackageData } from './packages'
5555
export type {
5656
Logger,

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

+48-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
CustomPluginOptions,
33
LoadResult,
4+
ObjectHook,
45
PluginContext,
56
ResolveIdResult,
67
Plugin as RollupPlugin,
@@ -63,14 +64,16 @@ export interface Plugin extends RollupPlugin {
6364
* Note: User plugins are resolved before running this hook so injecting other
6465
* plugins inside the `config` hook will have no effect.
6566
*/
66-
config?: (
67-
config: UserConfig,
68-
env: ConfigEnv
69-
) => UserConfig | null | void | Promise<UserConfig | null | void>
67+
config?: ObjectHook<
68+
(
69+
config: UserConfig,
70+
env: ConfigEnv
71+
) => UserConfig | null | void | Promise<UserConfig | null | void>
72+
>
7073
/**
7174
* Use this hook to read and store the final resolved vite config.
7275
*/
73-
configResolved?: (config: ResolvedConfig) => void | Promise<void>
76+
configResolved?: ObjectHook<(config: ResolvedConfig) => void | Promise<void>>
7477
/**
7578
* Configure the vite server. The hook receives the {@link ViteDevServer}
7679
* instance. This can also be used to store a reference to the server
@@ -80,7 +83,7 @@ export interface Plugin extends RollupPlugin {
8083
* can return a post hook that will be called after internal middlewares
8184
* are applied. Hook can be async functions and will be called in series.
8285
*/
83-
configureServer?: ServerHook
86+
configureServer?: ObjectHook<ServerHook>
8487
/**
8588
* Configure the preview server. The hook receives the connect server and
8689
* its underlying http server.
@@ -89,7 +92,7 @@ export interface Plugin extends RollupPlugin {
8992
* return a post hook that will be called after other middlewares are
9093
* applied. Hooks can be async functions and will be called in series.
9194
*/
92-
configurePreviewServer?: PreviewServerHook
95+
configurePreviewServer?: ObjectHook<PreviewServerHook>
9396
/**
9497
* Transform index.html.
9598
* The hook receives the following arguments:
@@ -121,36 +124,46 @@ export interface Plugin extends RollupPlugin {
121124
* - If the hook doesn't return a value, the hmr update will be performed as
122125
* normal.
123126
*/
124-
handleHotUpdate?(
125-
ctx: HmrContext
126-
): Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>
127+
handleHotUpdate?: ObjectHook<
128+
(
129+
ctx: HmrContext
130+
) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>
131+
>
127132

128133
/**
129134
* extend hooks with ssr flag
130135
*/
131-
resolveId?: (
132-
this: PluginContext,
133-
source: string,
134-
importer: string | undefined,
135-
options: {
136-
custom?: CustomPluginOptions
137-
ssr?: boolean
138-
/**
139-
* @internal
140-
*/
141-
scan?: boolean
142-
isEntry: boolean
143-
}
144-
) => Promise<ResolveIdResult> | ResolveIdResult
145-
load?: (
146-
this: PluginContext,
147-
id: string,
148-
options?: { ssr?: boolean }
149-
) => Promise<LoadResult> | LoadResult
150-
transform?: (
151-
this: TransformPluginContext,
152-
code: string,
153-
id: string,
154-
options?: { ssr?: boolean }
155-
) => Promise<TransformResult> | TransformResult
136+
resolveId?: ObjectHook<
137+
(
138+
this: PluginContext,
139+
source: string,
140+
importer: string | undefined,
141+
options: {
142+
custom?: CustomPluginOptions
143+
ssr?: boolean
144+
/**
145+
* @internal
146+
*/
147+
scan?: boolean
148+
isEntry: boolean
149+
}
150+
) => Promise<ResolveIdResult> | ResolveIdResult
151+
>
152+
load?: ObjectHook<
153+
(
154+
this: PluginContext,
155+
id: string,
156+
options?: { ssr?: boolean }
157+
) => Promise<LoadResult> | LoadResult
158+
>
159+
transform?: ObjectHook<
160+
(
161+
this: TransformPluginContext,
162+
code: string,
163+
id: string,
164+
options?: { ssr?: boolean }
165+
) => Promise<TransformResult> | TransformResult
166+
>
156167
}
168+
169+
export type HookHandler<T> = T extends ObjectHook<infer H> ? H : T

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

+59-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import aliasPlugin from '@rollup/plugin-alias'
2-
import type { ResolvedConfig } from '../config'
2+
import type { PluginHookUtils, ResolvedConfig } from '../config'
33
import { isDepsOptimizerEnabled } from '../config'
4-
import type { Plugin } from '../plugin'
4+
import type { HookHandler, Plugin } from '../plugin'
55
import { getDepsOptimizer } from '../optimizer'
66
import { shouldExternalizeForSSR } from '../ssr/ssrExternal'
77
import { jsonPlugin } from './json'
@@ -99,3 +99,60 @@ export async function resolvePlugins(
9999
: [clientInjectionsPlugin(config), importAnalysisPlugin(config)])
100100
].filter(Boolean) as Plugin[]
101101
}
102+
103+
export function createPluginHookUtils(
104+
plugins: readonly Plugin[]
105+
): PluginHookUtils {
106+
// sort plugins per hook
107+
const sortedPluginsCache = new Map<keyof Plugin, Plugin[]>()
108+
function getSortedPlugins(hookName: keyof Plugin): Plugin[] {
109+
if (sortedPluginsCache.has(hookName))
110+
return sortedPluginsCache.get(hookName)!
111+
const sorted = getSortedPluginsByHook(hookName, plugins)
112+
sortedPluginsCache.set(hookName, sorted)
113+
return sorted
114+
}
115+
function getSortedPluginHooks<K extends keyof Plugin>(
116+
hookName: K
117+
): NonNullable<HookHandler<Plugin[K]>>[] {
118+
const plugins = getSortedPlugins(hookName)
119+
return plugins
120+
.map((p) => {
121+
const hook = p[hookName]!
122+
// @ts-expect-error cast
123+
return 'handler' in hook ? hook.handler : hook
124+
})
125+
.filter(Boolean)
126+
}
127+
128+
return {
129+
getSortedPlugins,
130+
getSortedPluginHooks
131+
}
132+
}
133+
134+
export function getSortedPluginsByHook(
135+
hookName: keyof Plugin,
136+
plugins: readonly Plugin[]
137+
): Plugin[] {
138+
const pre: Plugin[] = []
139+
const normal: Plugin[] = []
140+
const post: Plugin[] = []
141+
for (const plugin of plugins) {
142+
const hook = plugin[hookName]
143+
if (hook) {
144+
if (typeof hook === 'object') {
145+
if (hook.order === 'pre') {
146+
pre.push(plugin)
147+
continue
148+
}
149+
if (hook.order === 'post') {
150+
post.push(plugin)
151+
continue
152+
}
153+
}
154+
normal.push(plugin)
155+
}
156+
}
157+
return [...pre, ...normal, ...post]
158+
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,6 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
237237
injectEnv = module?.transformResult?.code || ''
238238
}
239239
}
240-
241240
return {
242241
code: injectEnv + raw
243242
}

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

+2-6
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,8 @@ export async function preview(
8787

8888
// apply server hooks from plugins
8989
const postHooks: ((() => void) | void)[] = []
90-
for (const plugin of config.plugins) {
91-
if (plugin.configurePreviewServer) {
92-
postHooks.push(
93-
await plugin.configurePreviewServer({ middlewares: app, httpServer })
94-
)
95-
}
90+
for (const hook of config.getSortedPluginHooks('configurePreviewServer')) {
91+
postHooks.push(await hook({ middlewares: app, httpServer }))
9692
}
9793

9894
// cors

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

+4-6
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,10 @@ export async function handleHMRUpdate(
9393
server
9494
}
9595

96-
for (const plugin of config.plugins) {
97-
if (plugin.handleHotUpdate) {
98-
const filteredModules = await plugin.handleHotUpdate(hmrContext)
99-
if (filteredModules) {
100-
hmrContext.modules = filteredModules
101-
}
96+
for (const hook of config.getSortedPluginHooks('handleHotUpdate')) {
97+
const filteredModules = await hook(hmrContext)
98+
if (filteredModules) {
99+
hmrContext.modules = filteredModules
102100
}
103101
}
104102

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -493,10 +493,8 @@ export async function createServer(
493493

494494
// apply server configuration hooks from plugins
495495
const postHooks: ((() => void) | void)[] = []
496-
for (const plugin of config.plugins) {
497-
if (plugin.configureServer) {
498-
postHooks.push(await plugin.configureServer(server))
499-
}
496+
for (const hook of config.getSortedPluginHooks('configureServer')) {
497+
postHooks.push(await hook(server))
500498
}
501499

502500
// Internal middlewares ------------------------------------------------------

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

+70-34
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,17 @@ import { join, resolve } from 'node:path'
3434
import { performance } from 'node:perf_hooks'
3535
import { createRequire } from 'node:module'
3636
import type {
37+
AsyncPluginHooks,
3738
CustomPluginOptions,
3839
EmittedFile,
40+
FunctionPluginHooks,
3941
InputOptions,
4042
LoadResult,
4143
MinimalPluginContext,
4244
ModuleInfo,
4345
NormalizedInputOptions,
4446
OutputOptions,
47+
ParallelPluginHooks,
4548
PartialResolvedId,
4649
ResolvedId,
4750
RollupError,
@@ -74,6 +77,7 @@ import {
7477
} from '../utils'
7578
import { FS_PREFIX } from '../constants'
7679
import type { ResolvedConfig } from '../config'
80+
import { createPluginHookUtils } from '../plugins'
7781
import { buildErrorMessage } from './middlewares/error'
7882
import type { ModuleGraph } from './moduleGraph'
7983

@@ -137,11 +141,19 @@ type PluginContext = Omit<
137141
export let parser = acorn.Parser
138142

139143
export async function createPluginContainer(
140-
{ plugins, logger, root, build: { rollupOptions } }: ResolvedConfig,
144+
config: ResolvedConfig,
141145
moduleGraph?: ModuleGraph,
142146
watcher?: FSWatcher
143147
): Promise<PluginContainer> {
144148
const isDebug = process.env.DEBUG
149+
const {
150+
plugins,
151+
logger,
152+
root,
153+
build: { rollupOptions }
154+
} = config
155+
const { getSortedPluginHooks, getSortedPlugins } =
156+
createPluginHookUtils(plugins)
145157

146158
const seenResolves: Record<string, true | undefined> = {}
147159
const debugResolve = createDebugger('vite:resolve')
@@ -192,6 +204,28 @@ export async function createPluginContainer(
192204
)
193205
}
194206

207+
// parallel, ignores returns
208+
async function hookParallel<H extends AsyncPluginHooks & ParallelPluginHooks>(
209+
hookName: H,
210+
context: (plugin: Plugin) => ThisType<FunctionPluginHooks[H]>,
211+
args: (plugin: Plugin) => Parameters<FunctionPluginHooks[H]>
212+
): Promise<void> {
213+
const parallelPromises: Promise<unknown>[] = []
214+
for (const plugin of getSortedPlugins(hookName)) {
215+
const hook = plugin[hookName]
216+
if (!hook) continue
217+
const handler: Function = 'handler' in hook ? hook.handler : hook
218+
if ((hook as { sequential?: boolean }).sequential) {
219+
await Promise.all(parallelPromises)
220+
parallelPromises.length = 0
221+
await handler.apply(context(plugin), args(plugin))
222+
} else {
223+
parallelPromises.push(handler.apply(context(plugin), args(plugin)))
224+
}
225+
}
226+
await Promise.all(parallelPromises)
227+
}
228+
195229
// throw when an unsupported ModuleInfo property is accessed,
196230
// so that incompatible plugins fail in a non-cryptic way.
197231
const ModuleInfoProxy: ProxyHandler<ModuleInfo> = {
@@ -500,10 +534,8 @@ export async function createPluginContainer(
500534
const container: PluginContainer = {
501535
options: await (async () => {
502536
let options = rollupOptions
503-
for (const plugin of plugins) {
504-
if (!plugin.options) continue
505-
options =
506-
(await plugin.options.call(minimalContext, options)) || options
537+
for (const optionsHook of getSortedPluginHooks('options')) {
538+
options = (await optionsHook.call(minimalContext, options)) || options
507539
}
508540
if (options.acornInjectPlugins) {
509541
parser = acorn.Parser.extend(
@@ -520,15 +552,10 @@ export async function createPluginContainer(
520552
getModuleInfo,
521553

522554
async buildStart() {
523-
await Promise.all(
524-
plugins.map((plugin) => {
525-
if (plugin.buildStart) {
526-
return plugin.buildStart.call(
527-
new Context(plugin) as any,
528-
container.options as NormalizedInputOptions
529-
)
530-
}
531-
})
555+
await hookParallel(
556+
'buildStart',
557+
(plugin) => new Context(plugin),
558+
() => [container.options as NormalizedInputOptions]
532559
)
533560
},
534561

@@ -544,24 +571,23 @@ export async function createPluginContainer(
544571

545572
let id: string | null = null
546573
const partial: Partial<PartialResolvedId> = {}
547-
for (const plugin of plugins) {
574+
for (const plugin of getSortedPlugins('resolveId')) {
548575
if (!plugin.resolveId) continue
549576
if (skip?.has(plugin)) continue
550577

551578
ctx._activePlugin = plugin
552579

553580
const pluginResolveStart = isDebug ? performance.now() : 0
554-
const result = await plugin.resolveId.call(
555-
ctx as any,
556-
rawId,
557-
importer,
558-
{
559-
custom: options?.custom,
560-
isEntry: !!options?.isEntry,
561-
ssr,
562-
scan
563-
}
564-
)
581+
const handler =
582+
'handler' in plugin.resolveId
583+
? plugin.resolveId.handler
584+
: plugin.resolveId
585+
const result = await handler.call(ctx as any, rawId, importer, {
586+
custom: options?.custom,
587+
isEntry: !!options?.isEntry,
588+
ssr,
589+
scan
590+
})
565591
if (!result) continue
566592

567593
if (typeof result === 'string') {
@@ -607,10 +633,12 @@ export async function createPluginContainer(
607633
const ssr = options?.ssr
608634
const ctx = new Context()
609635
ctx.ssr = !!ssr
610-
for (const plugin of plugins) {
636+
for (const plugin of getSortedPlugins('load')) {
611637
if (!plugin.load) continue
612638
ctx._activePlugin = plugin
613-
const result = await plugin.load.call(ctx as any, id, { ssr })
639+
const handler =
640+
'handler' in plugin.load ? plugin.load.handler : plugin.load
641+
const result = await handler.call(ctx as any, id, { ssr })
614642
if (result != null) {
615643
if (isObject(result)) {
616644
updateModuleInfo(id, result)
@@ -626,15 +654,19 @@ export async function createPluginContainer(
626654
const ssr = options?.ssr
627655
const ctx = new TransformContext(id, code, inMap as SourceMap)
628656
ctx.ssr = !!ssr
629-
for (const plugin of plugins) {
657+
for (const plugin of getSortedPlugins('transform')) {
630658
if (!plugin.transform) continue
631659
ctx._activePlugin = plugin
632660
ctx._activeId = id
633661
ctx._activeCode = code
634662
const start = isDebug ? performance.now() : 0
635663
let result: TransformResult | string | undefined
664+
const handler =
665+
'handler' in plugin.transform
666+
? plugin.transform.handler
667+
: plugin.transform
636668
try {
637-
result = await plugin.transform.call(ctx as any, code, id, { ssr })
669+
result = await handler.call(ctx as any, code, id, { ssr })
638670
} catch (e) {
639671
ctx.error(e)
640672
}
@@ -670,11 +702,15 @@ export async function createPluginContainer(
670702
async close() {
671703
if (closed) return
672704
const ctx = new Context()
673-
await Promise.all(
674-
plugins.map((p) => p.buildEnd && p.buildEnd.call(ctx as any))
705+
await hookParallel(
706+
'buildEnd',
707+
() => ctx,
708+
() => []
675709
)
676-
await Promise.all(
677-
plugins.map((p) => p.closeBundle && p.closeBundle.call(ctx as any))
710+
await hookParallel(
711+
'closeBundle',
712+
() => ctx,
713+
() => []
678714
)
679715
closed = true
680716
}

‎playground/lib/__tests__/lib.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe.runIf(isBuild)('build', () => {
2424
expect(await page.textContent('.iife')).toBe('It works')
2525
const code = readFile('dist/my-lib-custom-filename.iife.js')
2626
// esbuild helpers are injected inside of the IIFE wrapper
27-
expect(code).toMatch(/^const MyLib=function\(\){"use strict";/)
27+
expect(code).toMatch(/^var MyLib=function\(\){"use strict";/)
2828
})
2929

3030
test('Library mode does not include `preload`', async () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { expect, test } from 'vitest'
2+
import { page } from '~utils'
3+
4+
test('object hooks', async () => {
5+
expect(await page.textContent('#transform')).toMatch('ok')
6+
})

‎playground/object-hooks/index.html

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<h3>Transform Hook order</h3>
2+
<div id="transform"></div>
3+
4+
<script type="module" src="./main.ts"></script>

‎playground/object-hooks/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const app = document.getElementById('transform')
2+
app.innerText = '__TRANSFORM__'

‎playground/object-hooks/package.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "test-extensions",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"vue": "^3.2.37"
13+
}
14+
}
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* eslint-disable import/no-nodejs-modules */
2+
import assert from 'assert'
3+
import { defineConfig } from 'vite'
4+
5+
let count = 0
6+
export default defineConfig({
7+
plugins: [
8+
{
9+
name: 'plugin1',
10+
buildStart: {
11+
async handler() {
12+
await new Promise((r) => setTimeout(r, 100))
13+
count += 1
14+
}
15+
},
16+
transform: {
17+
order: 'post',
18+
handler(code) {
19+
return code.replace('__TRANSFORM3__', 'ok')
20+
}
21+
}
22+
},
23+
{
24+
name: 'plugin2',
25+
buildStart: {
26+
sequential: true,
27+
async handler() {
28+
assert(count === 1)
29+
await new Promise((r) => setTimeout(r, 100))
30+
count += 1
31+
}
32+
},
33+
transform: {
34+
handler(code) {
35+
return code.replace('__TRANSFORM1__', '__TRANSFORM2__')
36+
}
37+
}
38+
},
39+
{
40+
name: 'plugin3',
41+
buildStart: {
42+
async handler() {
43+
assert(count === 2)
44+
await new Promise((r) => setTimeout(r, 100))
45+
count += 1
46+
}
47+
},
48+
transform: {
49+
order: 'pre',
50+
handler(code) {
51+
return code.replace('__TRANSFORM__', '__TRANSFORM1__')
52+
}
53+
}
54+
},
55+
{
56+
name: 'plugin4',
57+
buildStart: {
58+
async handler() {
59+
assert(count === 2)
60+
}
61+
},
62+
transform: {
63+
handler(code) {
64+
return code.replace('__TRANSFORM2__', '__TRANSFORM3__')
65+
}
66+
}
67+
}
68+
]
69+
})

‎pnpm-lock.yaml

+30-94
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.