diff --git a/packages/compat/webpack/src/shared.ts b/packages/compat/webpack/src/shared.ts index 30f20d7cbe..f872febc65 100644 --- a/packages/compat/webpack/src/shared.ts +++ b/packages/compat/webpack/src/shared.ts @@ -33,6 +33,7 @@ export const applyDefaultPlugins = (plugins: Plugins) => import('./plugins/sass').then((m) => m.pluginSass()), import('./plugins/less').then((m) => m.pluginLess()), plugins.bundleAnalyzer(), + plugins.rsdoctor(), plugins.splitChunks(), plugins.startUrl?.(), plugins.inlineChunk(), diff --git a/packages/core/src/plugins/index.ts b/packages/core/src/plugins/index.ts index f8af913d7d..5491a4934e 100644 --- a/packages/core/src/plugins/index.ts +++ b/packages/core/src/plugins/index.ts @@ -13,6 +13,7 @@ export const plugins: Plugins = { inlineChunk: () => import('./inlineChunk').then((m) => m.pluginInlineChunk()), bundleAnalyzer: () => import('./bundleAnalyzer').then((m) => m.pluginBundleAnalyzer()), + rsdoctor: () => import('./rsdoctor').then((m) => m.pluginRsdoctor()), asset: () => import('./asset').then((m) => m.pluginAsset()), wasm: () => import('./wasm').then((m) => m.pluginWasm()), moment: () => import('./moment').then((m) => m.pluginMoment()), diff --git a/packages/core/src/plugins/rsdoctor.ts b/packages/core/src/plugins/rsdoctor.ts new file mode 100644 index 0000000000..210b987488 --- /dev/null +++ b/packages/core/src/plugins/rsdoctor.ts @@ -0,0 +1,66 @@ +import { color, logger, type BundlerPluginInstance } from '@rsbuild/shared'; +import type { RsbuildPlugin } from '../types'; + +export function pluginRsdoctor(): RsbuildPlugin { + return { + name: 'rsbuild:rsdoctor', + + setup(api) { + api.onBeforeCreateCompiler(async ({ bundlerConfigs }) => { + if (process.env.RSDOCTOR !== 'true') { + return; + } + + const isRspack = api.context.bundlerType === 'rspack'; + const packageName = isRspack + ? '@rsdoctor/rspack-plugin' + : '@rsdoctor/webpack-plugin'; + + let module: { + RsdoctorRspackPlugin: { new (): BundlerPluginInstance }; + RsdoctorWebpackPlugin: { new (): BundlerPluginInstance }; + }; + + try { + const path = require.resolve(packageName, { + paths: [api.context.rootPath], + }); + module = await import(path); + } catch (err) { + logger.warn( + `\`process.env.RSDOCTOR\` enabled, please install ${color.bold(color.yellow(packageName))} package.`, + ); + return; + } + + const pluginName = isRspack + ? 'RsdoctorRspackPlugin' + : 'RsdoctorWebpackPlugin'; + + if (!module || !module[pluginName]) { + return; + } + + let isAutoRegister = false; + + bundlerConfigs.forEach((config) => { + const registered = config.plugins?.some( + (plugin) => plugin?.constructor?.name === pluginName, + ); + + if (registered) { + return; + } + + config.plugins ||= []; + config.plugins.push(new module[pluginName]()); + isAutoRegister = true; + }); + + if (isAutoRegister) { + logger.info(`${color.bold(color.yellow(packageName))} enabled.`); + } + }); + }, + }; +} diff --git a/packages/core/src/provider/shared.ts b/packages/core/src/provider/shared.ts index ea37968aa1..603669e00e 100644 --- a/packages/core/src/provider/shared.ts +++ b/packages/core/src/provider/shared.ts @@ -40,6 +40,7 @@ export const applyDefaultPlugins = (plugins: Plugins) => plugins.startUrl(), plugins.inlineChunk(), plugins.bundleAnalyzer(), + plugins.rsdoctor(), plugins.networkPerformance(), plugins.preloadOrPrefetch(), plugins.performance(), diff --git a/packages/shared/src/types/plugin.ts b/packages/shared/src/types/plugin.ts index 33b971e144..87bbc01f6b 100644 --- a/packages/shared/src/types/plugin.ts +++ b/packages/shared/src/types/plugin.ts @@ -123,6 +123,7 @@ export type Plugins = { splitChunks: PluginsFn; inlineChunk: PluginsFn; bundleAnalyzer: PluginsFn; + rsdoctor: PluginsFn; asset: PluginsFn; html: PluginsFn; wasm: PluginsFn;