diff --git a/packages/compat/webpack/src/webpackConfig.ts b/packages/compat/webpack/src/webpackConfig.ts index aa7c61c264..116c01f3fa 100644 --- a/packages/compat/webpack/src/webpackConfig.ts +++ b/packages/compat/webpack/src/webpackConfig.ts @@ -26,9 +26,11 @@ async function modifyWebpackChain( ): Promise { logger.debug('modify webpack chain'); - const [modifiedChain] = await context.hooks.modifyWebpackChain.call( - utils.environment.name, - )(chain, utils); + const [modifiedChain] = + await context.hooks.modifyWebpackChain.callInEnvironment({ + environment: utils.environment.name, + args: [chain, utils], + }); if (utils.environment.config.tools?.webpackChain) { for (const item of castArray(utils.environment.config.tools.webpackChain)) { @@ -47,9 +49,11 @@ async function modifyWebpackConfig( utils: ModifyWebpackConfigUtils, ): Promise { logger.debug('modify webpack config'); - let [modifiedConfig] = await context.hooks.modifyWebpackConfig.call( - utils.environment.name, - )(webpackConfig, utils); + let [modifiedConfig] = + await context.hooks.modifyWebpackConfig.callInEnvironment({ + environment: utils.environment.name, + args: [webpackConfig, utils], + }); if (utils.environment.config.tools?.webpack) { modifiedConfig = reduceConfigsWithContext({ diff --git a/packages/core/src/configChain.ts b/packages/core/src/configChain.ts index fb2815611f..d2c54fde95 100644 --- a/packages/core/src/configChain.ts +++ b/packages/core/src/configChain.ts @@ -23,9 +23,11 @@ export async function modifyBundlerChain( const bundlerChain = getBundlerChain(); - const [modifiedBundlerChain] = await context.hooks.modifyBundlerChain.call( - utils.environment.name, - )(bundlerChain, utils); + const [modifiedBundlerChain] = + await context.hooks.modifyBundlerChain.callInEnvironment({ + environment: utils.environment.name, + args: [bundlerChain, utils], + }); if (utils.environment.config.tools?.bundlerChain) { for (const item of castArray(utils.environment.config.tools.bundlerChain)) { diff --git a/packages/core/src/createRsbuild.ts b/packages/core/src/createRsbuild.ts index 0dad8c1f37..dd948f2e3c 100644 --- a/packages/core/src/createRsbuild.ts +++ b/packages/core/src/createRsbuild.ts @@ -38,9 +38,10 @@ async function applyDefaultPlugins( import('./plugins/asset').then(({ pluginAsset }) => pluginAsset()), import('./plugins/html').then(({ pluginHtml }) => pluginHtml((environment: string) => async (...args) => { - const result = await context.hooks.modifyHTMLTags.call(environment)( - ...args, - ); + const result = await context.hooks.modifyHTMLTags.callInEnvironment({ + environment, + args, + }); return result[0]; }), ), diff --git a/packages/core/src/initHooks.ts b/packages/core/src/initHooks.ts index ca1326c948..5776b66013 100644 --- a/packages/core/src/initHooks.ts +++ b/packages/core/src/initHooks.ts @@ -3,7 +3,6 @@ import { isPluginMatchEnvironment } from './pluginManager'; import type { AsyncHook, EnvironmentAsyncHook, - EnvironmentMeta, HookDescriptor, ModifyBundlerChainFn, ModifyEnvironmentConfigFn, @@ -29,68 +28,77 @@ export function createEnvironmentAsyncHook< Callback extends (...args: any[]) => any, >(): EnvironmentAsyncHook { type Hook = { - environmentMeta?: EnvironmentMeta; + environment?: string; handler: Callback; }; const preGroup: Hook[] = []; const postGroup: Hook[] = []; const defaultGroup: Hook[] = []; - const tap = - (environmentMeta?: EnvironmentMeta) => - (cb: Callback | HookDescriptor) => { - if (isFunction(cb)) { - defaultGroup.push({ - environmentMeta, - handler: cb, - }); - } else if (cb.order === 'pre') { - preGroup.push({ - environmentMeta, - handler: cb.handler, - }); - } else if (cb.order === 'post') { - postGroup.push({ - environmentMeta, - handler: cb.handler, - }); - } else { - defaultGroup.push({ - environmentMeta, - handler: cb.handler, - }); + const tapEnvironment = ({ + environment, + handler: cb, + }: { + environment?: string; + handler: Callback | HookDescriptor; + }) => { + if (isFunction(cb)) { + defaultGroup.push({ + environment, + handler: cb, + }); + } else if (cb.order === 'pre') { + preGroup.push({ + environment, + handler: cb.handler, + }); + } else if (cb.order === 'post') { + postGroup.push({ + environment, + handler: cb.handler, + }); + } else { + defaultGroup.push({ + environment, + handler: cb.handler, + }); + } + }; + + const callInEnvironment = async ({ + environment, + args: params, + }: { + environment?: string; + args: Parameters; + }) => { + const callbacks = [...preGroup, ...defaultGroup, ...postGroup]; + + for (const callback of callbacks) { + // If this callback is not a global callback, the environment info should match + if ( + callback.environment && + environment && + !isPluginMatchEnvironment(callback.environment, environment) + ) { + continue; } - }; - - const call = - (environment?: string) => - async (...args: Parameters) => { - const params = args.slice(0) as Parameters; - const callbacks = [...preGroup, ...defaultGroup, ...postGroup]; - - for (const callback of callbacks) { - // If this callback is not a global callback, the environment info should match - if ( - callback.environmentMeta && - environment && - !isPluginMatchEnvironment(callback.environmentMeta, environment) - ) { - continue; - } - - const result = await callback.handler(...params); - - if (result !== undefined) { - params[0] = result; - } + + const result = await callback.handler(...params); + + if (result !== undefined) { + params[0] = result; } + } - return params; - }; + return params; + }; return { - tap, - call, + tapEnvironment, + tap: (handler: Callback | HookDescriptor) => + tapEnvironment({ handler }), + callInEnvironment, }; } @@ -113,8 +121,7 @@ export function createAsyncHook< } }; - const call = async (...args: Parameters) => { - const params = args.slice(0) as Parameters; + const call = async (...params: Parameters) => { const callbacks = [...preGroup, ...defaultGroup, ...postGroup]; for (const callback of callbacks) { diff --git a/packages/core/src/initPlugins.ts b/packages/core/src/initPlugins.ts index aec368ee33..87d118b9f6 100644 --- a/packages/core/src/initPlugins.ts +++ b/packages/core/src/initPlugins.ts @@ -6,7 +6,6 @@ import { removeLeadingSlash } from './helpers'; import type { TransformLoaderOptions } from './loader/transformLoader'; import { isPluginMatchEnvironment } from './pluginManager'; import type { - EnvironmentMeta, GetRsbuildConfig, InternalContext, NormalizedConfig, @@ -82,7 +81,7 @@ export function initPluginAPI({ }: { context: InternalContext; pluginManager: PluginManager; -}): (environmentMeta?: { environment: string }) => RsbuildPluginAPI { +}): (environment?: string) => RsbuildPluginAPI { const { hooks } = context; const publicContext = createPublicContext(context); @@ -137,12 +136,12 @@ export function initPluginAPI({ let transformId = 0; const transformer: Record = {}; const processAssetsFns: Array<{ - environmentMeta?: EnvironmentMeta; + environment?: string; descriptor: ProcessAssetsDescriptor; handler: ProcessAssetsHandler; }> = []; - hooks.modifyBundlerChain.tap()((chain, { target, environment }) => { + hooks.modifyBundlerChain.tap((chain, { target, environment }) => { const pluginName = 'RsbuildCorePlugin'; /** @@ -164,7 +163,7 @@ export function initPluginAPI({ for (const { descriptor, handler, - environmentMeta, + environment: metaEnvironment, } of processAssetsFns) { // filter by targets if (descriptor.targets && !descriptor.targets.includes(target)) { @@ -173,8 +172,8 @@ export function initPluginAPI({ // filter by environment if ( - environmentMeta && - !isPluginMatchEnvironment(environmentMeta, environment.name) + metaEnvironment && + !isPluginMatchEnvironment(metaEnvironment, environment.name) ) { return; } @@ -201,70 +200,69 @@ export function initPluginAPI({ chain.plugin(pluginName).use(RsbuildCorePlugin); }); - const getTransformFn: (environmentMeta?: { - environment: string; - }) => TransformFn = (environmentMeta) => (descriptor, handler) => { - const id = `rsbuild-transform-${transformId++}`; + const getTransformFn: (environment?: string) => TransformFn = + (environment) => (descriptor, handler) => { + const id = `rsbuild-transform-${transformId++}`; - transformer[id] = handler; + transformer[id] = handler; - hooks.modifyBundlerChain.tap(environmentMeta)( - (chain, { target, environment }) => { - // filter by targets - if (descriptor.targets && !descriptor.targets.includes(target)) { - return; - } + hooks.modifyBundlerChain.tapEnvironment({ + environment, + handler: (chain, { target, environment }) => { + // filter by targets + if (descriptor.targets && !descriptor.targets.includes(target)) { + return; + } - // filter by environments - if ( - descriptor.environments && - !descriptor.environments.includes(environment.name) - ) { - return; - } + // filter by environments + if ( + descriptor.environments && + !descriptor.environments.includes(environment.name) + ) { + return; + } - const rule = chain.module.rule(id); + const rule = chain.module.rule(id); - if (descriptor.test) { - rule.test(descriptor.test); - } - if (descriptor.resourceQuery) { - rule.resourceQuery(descriptor.resourceQuery); - } + if (descriptor.test) { + rule.test(descriptor.test); + } + if (descriptor.resourceQuery) { + rule.resourceQuery(descriptor.resourceQuery); + } - const loaderName = descriptor.raw - ? 'transformRawLoader.cjs' - : 'transformLoader.cjs'; - const loaderPath = join(LOADER_PATH, loaderName); + const loaderName = descriptor.raw + ? 'transformRawLoader.cjs' + : 'transformLoader.cjs'; + const loaderPath = join(LOADER_PATH, loaderName); - rule - .use(id) - .loader(loaderPath) - .options({ - id, - getEnvironment: () => environment, - } satisfies TransformLoaderOptions); - }, - ); - }; + rule + .use(id) + .loader(loaderPath) + .options({ + id, + getEnvironment: () => environment, + } satisfies TransformLoaderOptions); + }, + }); + }; - const setProcessAssets: ( - environmentMeta?: EnvironmentMeta, - ) => ProcessAssetsFn = (environmentMeta) => (descriptor, handler) => { - processAssetsFns.push({ environmentMeta, descriptor, handler }); - }; + const setProcessAssets: (environment?: string) => ProcessAssetsFn = + (environment) => (descriptor, handler) => { + processAssetsFns.push({ environment, descriptor, handler }); + }; process.on('exit', () => { hooks.onExit.call(); }); // Each plugin returns different APIs depending on the registered environment info. - return (environmentMeta?: { environment: string }) => ({ + return (environment?: string) => ({ context: publicContext, expose, - transform: getTransformFn(environmentMeta), + transform: getTransformFn(environment), useExposed, - processAssets: setProcessAssets(environmentMeta), + processAssets: setProcessAssets(environment), getRsbuildConfig, getNormalizedConfig, isPluginExists: pluginManager.isPluginExists, @@ -282,11 +280,35 @@ export function initPluginAPI({ onAfterStartProdServer: hooks.onAfterStartProdServer.tap, onBeforeStartProdServer: hooks.onBeforeStartProdServer.tap, modifyRsbuildConfig: hooks.modifyRsbuildConfig.tap, - modifyHTMLTags: hooks.modifyHTMLTags.tap(environmentMeta), - modifyBundlerChain: hooks.modifyBundlerChain.tap(environmentMeta), - modifyRspackConfig: hooks.modifyRspackConfig.tap(environmentMeta), - modifyWebpackChain: hooks.modifyWebpackChain.tap(environmentMeta), - modifyWebpackConfig: hooks.modifyWebpackConfig.tap(environmentMeta), - modifyEnvironmentConfig: hooks.modifyEnvironmentConfig.tap(environmentMeta), + modifyHTMLTags: (handler) => + hooks.modifyHTMLTags.tapEnvironment({ + environment, + handler, + }), + modifyBundlerChain: (handler) => + hooks.modifyBundlerChain.tapEnvironment({ + environment, + handler, + }), + modifyRspackConfig: (handler) => + hooks.modifyRspackConfig.tapEnvironment({ + environment, + handler, + }), + modifyWebpackChain: (handler) => + hooks.modifyWebpackChain.tapEnvironment({ + environment, + handler, + }), + modifyWebpackConfig: (handler) => + hooks.modifyWebpackConfig.tapEnvironment({ + environment, + handler, + }), + modifyEnvironmentConfig: (handler) => + hooks.modifyEnvironmentConfig.tapEnvironment({ + environment, + handler, + }), }); } diff --git a/packages/core/src/pluginManager.ts b/packages/core/src/pluginManager.ts index 73309413b2..26e87a355e 100644 --- a/packages/core/src/pluginManager.ts +++ b/packages/core/src/pluginManager.ts @@ -3,7 +3,6 @@ import { isFunction } from './helpers'; import { logger } from './logger'; import type { BundlerPluginInstance, - EnvironmentMeta, Falsy, PluginManager, PluginMeta, @@ -57,11 +56,11 @@ function validatePlugin(plugin: unknown) { export const RSBUILD_ALL_ENVIRONMENT_SYMBOL = 'RSBUILD_ALL_ENVIRONMENT_SYMBOL'; export const isPluginMatchEnvironment = ( - meta: { environment: string }, - environment: string, + metaEnvironment: string, + currentEnvironment: string, ): boolean => - meta.environment === environment || - meta.environment === RSBUILD_ALL_ENVIRONMENT_SYMBOL; + metaEnvironment === currentEnvironment || + metaEnvironment === RSBUILD_ALL_ENVIRONMENT_SYMBOL; export function createPluginManager(): PluginManager { let plugins: PluginMeta[] = []; @@ -138,7 +137,7 @@ export function createPluginManager(): PluginManager { plugins.find( (plugin) => plugin.instance.name === pluginName && - isPluginMatchEnvironment(plugin, options.environment), + isPluginMatchEnvironment(plugin.environment, options.environment), ), ); @@ -148,7 +147,9 @@ export function createPluginManager(): PluginManager { }, ) => { return plugins - .filter((p) => isPluginMatchEnvironment(p, options.environment)) + .filter((p) => + isPluginMatchEnvironment(p.environment, options.environment), + ) .map((p) => p.instance); }; return { @@ -234,7 +235,7 @@ export async function initPlugins({ getPluginAPI, pluginManager, }: { - getPluginAPI: (environmentMeta?: EnvironmentMeta) => RsbuildPluginAPI; + getPluginAPI: (environment?: string) => RsbuildPluginAPI; pluginManager: PluginManager; }): Promise { logger.debug('init plugins'); @@ -265,8 +266,8 @@ export async function initPlugins({ ) { continue; } - const { instance, ...environmentMeta } = plugin; - await instance.setup(getPluginAPI!(environmentMeta)); + const { instance, environment } = plugin; + await instance.setup(getPluginAPI!(environment)); } logger.debug('init plugins done'); diff --git a/packages/core/src/provider/initConfigs.ts b/packages/core/src/provider/initConfigs.ts index 35df1bf01b..617f9d16f4 100644 --- a/packages/core/src/provider/initConfigs.ts +++ b/packages/core/src/provider/initConfigs.ts @@ -40,14 +40,19 @@ async function modifyEnvironmentConfig( name: string, ) { logger.debug(`modify Rsbuild environment(${name}) config`); - const [modified] = await context.hooks.modifyEnvironmentConfig.call(name)( - config, - { - name, - mergeEnvironmentConfig: - mergeRsbuildConfig as ModifyEnvironmentConfigUtils['mergeEnvironmentConfig'], - }, - ); + + const [modified] = + await context.hooks.modifyEnvironmentConfig.callInEnvironment({ + environment: name, + args: [ + config, + { + name, + mergeEnvironmentConfig: + mergeRsbuildConfig as ModifyEnvironmentConfigUtils['mergeEnvironmentConfig'], + }, + ], + }); logger.debug(`modify Rsbuild environment(${name}) config done`); diff --git a/packages/core/src/provider/rspackConfig.ts b/packages/core/src/provider/rspackConfig.ts index e41461d79f..61ba01ec70 100644 --- a/packages/core/src/provider/rspackConfig.ts +++ b/packages/core/src/provider/rspackConfig.ts @@ -20,9 +20,11 @@ async function modifyRspackConfig( utils: ModifyRspackConfigUtils, ) { logger.debug('modify Rspack config'); - let [modifiedConfig] = await context.hooks.modifyRspackConfig.call( - utils.environment.name, - )(rspackConfig, utils); + let [modifiedConfig] = + await context.hooks.modifyRspackConfig.callInEnvironment({ + environment: utils.environment.name, + args: [rspackConfig, utils], + }); if (utils.environment.config.tools?.rspack) { modifiedConfig = await reduceConfigsAsyncWithContext({ diff --git a/packages/core/src/types/context.ts b/packages/core/src/types/context.ts index ebd6a131bd..b2da229d07 100644 --- a/packages/core/src/types/context.ts +++ b/packages/core/src/types/context.ts @@ -1,7 +1,7 @@ import type { Hooks } from '../initHooks'; import type { NormalizedConfig, RsbuildConfig } from './config'; import type { EnvironmentContext } from './hooks'; -import type { EnvironmentMeta, RsbuildPluginAPI } from './plugin'; +import type { RsbuildPluginAPI } from './plugin'; export type BundlerType = 'rspack' | 'webpack'; @@ -36,9 +36,10 @@ export type InternalContext = RsbuildContext & { normalizedConfig?: NormalizedConfig; /** * Get the plugin API. - * When environmentMeta is undefined, the global plugin API is returned, which can be used in all environments. + * + * When environment is undefined, the global plugin API is returned, which can be used in all environments. * */ - getPluginAPI?: (environmentMeta?: EnvironmentMeta) => RsbuildPluginAPI; + getPluginAPI?: (environment?: string) => RsbuildPluginAPI; /** The environment context. */ environments: Record; }; diff --git a/packages/core/src/types/plugin.ts b/packages/core/src/types/plugin.ts index f513b3eb4f..45311aeadf 100644 --- a/packages/core/src/types/plugin.ts +++ b/packages/core/src/types/plugin.ts @@ -45,17 +45,23 @@ export type HookDescriptor any> = { order: HookOrder; }; -export type EnvironmentMeta = { - environment: string; -}; - export type EnvironmentAsyncHook any> = { - tap: ( - environmentMeta?: EnvironmentMeta, - ) => (cb: Callback | HookDescriptor) => void; - call: ( - environment?: string, - ) => (...args: Parameters) => Promise>; + tapEnvironment: (params: { + /** + * Specify that the callback will only be executed under the specified environment + */ + environment?: string; + handler: Callback | HookDescriptor; + }) => void; + /** + * Triggered in all environments by default. + * If you need to specify the environment, please use `tapEnvironment` + */ + tap: (cb: Callback | HookDescriptor) => void; + callInEnvironment: (params: { + environment?: string; + args: Parameters; + }) => Promise>; }; export type AsyncHook any> = { @@ -103,34 +109,51 @@ export type ModifyWebpackConfigFn = ( utils: ModifyWebpackConfigUtils, ) => Promise | WebpackConfig | void; -export type PluginMeta = EnvironmentMeta & { +export type PluginMeta = { + environment: string; instance: RsbuildPlugin; }; export type PluginManager = { getPlugins: (options?: { - /** Get the plugins in the specified environment. If environment is not specified, get the global plugins. */ + /** + * Get the plugins in the specified environment. + * + * If environment is not specified, get the global plugins. + */ environment: string; }) => RsbuildPlugin[]; addPlugins: ( plugins: Array, options?: { before?: string; - /** Add a plugin for the specified environment. If environment is not specified, it will be registered as a global plugin (effective in all environments) */ + /** + * Add a plugin for the specified environment. + * + * If environment is not specified, it will be registered as a global plugin (effective in all environments) + */ environment?: string; }, ) => void; removePlugins: ( pluginNames: string[], options?: { - /** Remove the plugin in the specified environment. If environment is not specified, remove it in all environments. */ + /** + * Remove the plugin in the specified environment. + * + * If environment is not specified, remove it in all environments. + */ environment: string; }, ) => void; isPluginExists: ( pluginName: string, options?: { - /** Whether it exists in the specified environment. If environment is not specified, determine whether the plugin is a global plugin */ + /** + * Whether it exists in the specified environment. + * + * If environment is not specified, determine whether the plugin is a global plugin. + */ environment: string; }, ) => boolean; diff --git a/packages/core/tests/hooks.test.ts b/packages/core/tests/hooks.test.ts index f476416656..22e39ac984 100644 --- a/packages/core/tests/hooks.test.ts +++ b/packages/core/tests/hooks.test.ts @@ -10,19 +10,26 @@ describe('initHooks', () => { test('createEnvironmentAsyncHook should only works in specified environment', async () => { const logs: string[] = []; const hookA = createEnvironmentAsyncHook(); - hookA.tap()((msg) => { + hookA.tap((msg) => { logs.push(`[global] ${msg}`); }); - hookA.tap({ + hookA.tapEnvironment({ environment: 'a', - })((msg) => { - logs.push(msg); + handler: (msg) => { + logs.push(msg); + }, }); - await hookA.call('a')('call in a'); + await hookA.callInEnvironment({ + environment: 'a', + args: ['call in a'], + }); - await hookA.call('b')('call in b'); + await hookA.callInEnvironment({ + environment: 'b', + args: ['call in b'], + }); expect(logs).toEqual([ '[global] call in a',