Skip to content
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: support add plugins for specified environment #2986

Merged
merged 10 commits into from
Jul 23, 2024
Merged
60 changes: 60 additions & 0 deletions e2e/cases/environments/plugins/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { build, gotoPage } from '@e2e/helper';
import { expect, test } from '@playwright/test';

import { pluginReact } from '@rsbuild/plugin-react';

test('should add single environment plugin correctly', async ({ page }) => {
const rsbuild = await build({
cwd: __dirname,
rsbuildConfig: {
environments: {
web: {
output: {
filenameHash: false,
},
plugins: [pluginReact()],
},
web1: {
source: {
entry: {
main: './src/index1.ts',
},
},
output: {
assetPrefix: 'auto',
filenameHash: false,
distPath: {
root: 'dist/web1',
},
},
},
},
},
runServer: true,
});

await gotoPage(page, rsbuild);

const button = page.locator('#test');
await expect(button).toHaveText('Hello Rsbuild!');

const web1Url = new URL(`http://localhost:${rsbuild.port}/web1/main`);

await page.goto(web1Url.href);

await expect(page.locator('#test1')).toHaveText('Hello Rsbuild!');

const files = await rsbuild.unwrapOutputJSON();
const filenames = Object.keys(files);

expect(
filenames.some((filename) =>
filename.includes('dist/static/js/lib-react.js'),
),
).toBeTruthy();
expect(
filenames.some((filename) =>
filename.includes('dist/web1/static/js/lib-react.js'),
),
).toBeFalsy();
});
3 changes: 3 additions & 0 deletions e2e/cases/environments/plugins/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const App = () => <div id="test">Hello Rsbuild!</div>;

export default App;
9 changes: 9 additions & 0 deletions e2e/cases/environments/plugins/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(React.createElement(App));
}
5 changes: 5 additions & 0 deletions e2e/cases/environments/plugins/src/index1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
document.getElementById('root').innerHTML = `
<div>
<div id="test1">Hello Rsbuild!</div>
</div>
`;
10 changes: 4 additions & 6 deletions packages/compat/webpack/src/webpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ async function modifyWebpackChain(
logger.debug('modify webpack chain');

const [modifiedChain] = await context.hooks.modifyWebpackChain.call(
chain,
utils,
);
utils.environment.name,
)(chain, utils);

if (utils.environment.config.tools?.webpackChain) {
for (const item of castArray(utils.environment.config.tools.webpackChain)) {
Expand All @@ -49,9 +48,8 @@ async function modifyWebpackConfig(
): Promise<WebpackConfig> {
logger.debug('modify webpack config');
let [modifiedConfig] = await context.hooks.modifyWebpackConfig.call(
webpackConfig,
utils,
);
utils.environment.name,
)(webpackConfig, utils);

if (utils.environment.config.tools?.webpack) {
modifiedConfig = reduceConfigsWithContext({
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,9 @@ export const getRsbuildInspectConfig = ({
for (const [name, config] of Object.entries(environments)) {
const debugConfig = {
...config,
pluginNames,
pluginNames: pluginManager
.getPlugins({ environment: name })
.map((p) => p.name),
};
rawEnvironmentConfigs.push({
name,
Expand Down
17 changes: 4 additions & 13 deletions packages/core/src/configChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import RspackChain from 'rspack-chain';
import { castArray, isPlainObject } from './helpers';
import { logger } from './logger';
import type {
CreateAsyncHook,
ModifyBundlerChainFn,
InternalContext,
ModifyBundlerChainUtils,
RsbuildConfig,
RsbuildContext,
RsbuildEntry,
Rspack,
RspackConfig,
Expand All @@ -19,22 +16,16 @@ export function getBundlerChain(): RspackChain {
}

export async function modifyBundlerChain(
context: RsbuildContext & {
hooks: {
modifyBundlerChain: CreateAsyncHook<ModifyBundlerChainFn>;
};
config: Readonly<RsbuildConfig>;
},
context: InternalContext,
utils: ModifyBundlerChainUtils,
): Promise<RspackChain> {
logger.debug('modify bundler chain');

const bundlerChain = getBundlerChain();

const [modifiedBundlerChain] = await context.hooks.modifyBundlerChain.call(
bundlerChain,
utils,
);
utils.environment.name,
)(bundlerChain, utils);

if (utils.environment.config.tools?.bundlerChain) {
for (const item of castArray(utils.environment.config.tools.bundlerChain)) {
Expand Down
31 changes: 24 additions & 7 deletions packages/core/src/createRsbuild.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext } from './createContext';
import { pick } from './helpers';
import { getPluginAPI } from './initPlugins';
import { initPluginAPI } from './initPlugins';
import { initRsbuildConfig } from './internal';
import { logger } from './logger';
import { setCssExtractPlugin } from './pluginHelper';
Expand Down Expand Up @@ -37,8 +37,10 @@ async function applyDefaultPlugins(
),
import('./plugins/asset').then(({ pluginAsset }) => pluginAsset()),
import('./plugins/html').then(({ pluginHtml }) =>
pluginHtml(async (...args) => {
const result = await context.hooks.modifyHTMLTags.call(...args);
pluginHtml((environment: string) => async (...args) => {
const result = await context.hooks.modifyHTMLTags.call(environment)(
...args,
);
return result[0];
}),
),
Expand Down Expand Up @@ -110,8 +112,9 @@ export async function createRsbuild(
rsbuildConfig.provider ? 'webpack' : 'rspack',
);

const pluginAPI = getPluginAPI({ context, pluginManager });
context.pluginAPI = pluginAPI;
const getPluginAPI = initPluginAPI({ context, pluginManager });
context.getPluginAPI = getPluginAPI;
const globalPluginAPI = getPluginAPI();

logger.debug('add default plugins');
await applyDefaultPlugins(pluginManager, context);
Expand Down Expand Up @@ -140,7 +143,7 @@ export async function createRsbuild(
'removePlugins',
'isPluginExists',
]),
...pick(pluginAPI, [
...pick(globalPluginAPI, [
'onBeforeBuild',
'onBeforeCreateCompiler',
'onBeforeStartDevServer',
Expand All @@ -164,13 +167,27 @@ export async function createRsbuild(
'startDevServer',
]),
preview,
context: pluginAPI.context,
context: globalPluginAPI.context,
};

if (rsbuildConfig.plugins) {
const plugins = await Promise.all(rsbuildConfig.plugins);
rsbuild.addPlugins(plugins);
}

// Register environment plugin
if (rsbuildConfig.environments) {
await Promise.all(
Object.entries(rsbuildConfig.environments).map(async ([name, config]) => {
if (config.plugins) {
const plugins = await Promise.all(config.plugins);
rsbuild.addPlugins(plugins, {
environment: name,
});
}
}),
);
}

return rsbuild;
}
99 changes: 87 additions & 12 deletions packages/core/src/initHooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { isFunction } from './helpers';
import { isPluginMatchEnvironment } from './pluginManager';
import type {
AsyncHook,
EnvironmentAsyncHook,
EnvironmentMeta,
HookDescriptor,
ModifyBundlerChainFn,
ModifyEnvironmentConfigFn,
Expand All @@ -22,6 +25,75 @@ import type {
OnExitFn,
} from './types';

export function createEnvironmentAsyncHook<
Callback extends (...args: any[]) => any,
>(): EnvironmentAsyncHook<Callback> {
type Hook = {
environmentMeta?: EnvironmentMeta;
handler: Callback;
};
const preGroup: Hook[] = [];
const postGroup: Hook[] = [];
const defaultGroup: Hook[] = [];

const tap =
(environmentMeta?: EnvironmentMeta) =>
(cb: Callback | HookDescriptor<Callback>) => {
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 call =
(environment?: string) =>
9aoy marked this conversation as resolved.
Show resolved Hide resolved
async (...args: Parameters<Callback>) => {
const params = args.slice(0) as Parameters<Callback>;
9aoy marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}

return params;
};

return {
tap,
call,
};
}

export function createAsyncHook<
Callback extends (...args: any[]) => any,
>(): AsyncHook<Callback> {
Expand Down Expand Up @@ -63,6 +135,7 @@ export function createAsyncHook<
}

export function initHooks(): {
/** The following hooks are global hooks */
onExit: AsyncHook<OnExitFn>;
onAfterBuild: AsyncHook<OnAfterBuildFn>;
onBeforeBuild: AsyncHook<OnBeforeBuildFn>;
Expand All @@ -74,13 +147,14 @@ export function initHooks(): {
onBeforeStartProdServer: AsyncHook<OnBeforeStartProdServerFn>;
onAfterCreateCompiler: AsyncHook<OnAfterCreateCompilerFn>;
onBeforeCreateCompiler: AsyncHook<OnBeforeCreateCompilerFn>;
modifyHTMLTags: AsyncHook<ModifyHTMLTagsFn>;
modifyRspackConfig: AsyncHook<ModifyRspackConfigFn>;
modifyBundlerChain: AsyncHook<ModifyBundlerChainFn>;
modifyWebpackChain: AsyncHook<ModifyWebpackChainFn>;
modifyWebpackConfig: AsyncHook<ModifyWebpackConfigFn>;
/** The following hooks are related to the environment */
modifyHTMLTags: EnvironmentAsyncHook<ModifyHTMLTagsFn>;
modifyRspackConfig: EnvironmentAsyncHook<ModifyRspackConfigFn>;
modifyBundlerChain: EnvironmentAsyncHook<ModifyBundlerChainFn>;
modifyWebpackChain: EnvironmentAsyncHook<ModifyWebpackChainFn>;
modifyWebpackConfig: EnvironmentAsyncHook<ModifyWebpackConfigFn>;
modifyRsbuildConfig: AsyncHook<ModifyRsbuildConfigFn>;
modifyEnvironmentConfig: AsyncHook<ModifyEnvironmentConfigFn>;
modifyEnvironmentConfig: EnvironmentAsyncHook<ModifyEnvironmentConfigFn>;
} {
return {
onExit: createAsyncHook<OnExitFn>(),
Expand All @@ -94,13 +168,14 @@ export function initHooks(): {
onBeforeStartProdServer: createAsyncHook<OnBeforeStartProdServerFn>(),
onAfterCreateCompiler: createAsyncHook<OnAfterCreateCompilerFn>(),
onBeforeCreateCompiler: createAsyncHook<OnBeforeCreateCompilerFn>(),
modifyHTMLTags: createAsyncHook<ModifyHTMLTagsFn>(),
modifyRspackConfig: createAsyncHook<ModifyRspackConfigFn>(),
modifyBundlerChain: createAsyncHook<ModifyBundlerChainFn>(),
modifyWebpackChain: createAsyncHook<ModifyWebpackChainFn>(),
modifyWebpackConfig: createAsyncHook<ModifyWebpackConfigFn>(),
modifyHTMLTags: createEnvironmentAsyncHook<ModifyHTMLTagsFn>(),
modifyRspackConfig: createEnvironmentAsyncHook<ModifyRspackConfigFn>(),
modifyBundlerChain: createEnvironmentAsyncHook<ModifyBundlerChainFn>(),
modifyWebpackChain: createEnvironmentAsyncHook<ModifyWebpackChainFn>(),
modifyWebpackConfig: createEnvironmentAsyncHook<ModifyWebpackConfigFn>(),
modifyRsbuildConfig: createAsyncHook<ModifyRsbuildConfigFn>(),
modifyEnvironmentConfig: createAsyncHook<ModifyEnvironmentConfigFn>(),
modifyEnvironmentConfig:
createEnvironmentAsyncHook<ModifyEnvironmentConfigFn>(),
};
}

Expand Down
Loading
Loading