diff --git a/.changeset/spotty-walls-dress.md b/.changeset/spotty-walls-dress.md new file mode 100644 index 000000000..1ba4eb5ea --- /dev/null +++ b/.changeset/spotty-walls-dress.md @@ -0,0 +1,8 @@ +--- +'@rsdoctor/webpack-plugin': patch +'@rsdoctor/rspack-plugin': patch +'@rsdoctor/core': patch +'@rsdoctor/sdk': patch +--- + +feat(rspack): support the rspack multi plugin diff --git a/examples/rspack-minimal/build.ts b/examples/rspack-minimal/build.ts new file mode 100644 index 000000000..c9c95b108 --- /dev/null +++ b/examples/rspack-minimal/build.ts @@ -0,0 +1,88 @@ +import rspack from '@rspack/core'; +import { resolve } from 'path'; +const config = require('./rspack.config.js'); +const ReactRefreshPlugin = require('@rspack/plugin-react-refresh'); +const { RsdoctorRspackMultiplePlugin } = require('@rsdoctor/rspack-plugin'); + +// console.log(config) + +function rspackBuild(config: rspack.Configuration) { + return new Promise((resolve) => { + rspack.rspack(config, (err, stats) => { + if (err) { + throw err; + } + + console.log(); + + if (stats) { + console.log( + stats.toString({ + chunks: false, + chunkModules: false, + colors: true, + modules: false, + children: false, + }), + ); + } + + resolve(); + }); + }); +} + +async function build() { + await Promise.all([ + rspackBuild({ + ...config, + name: 'Builder 1', + target: 'web', + plugins: [ + new ReactRefreshPlugin(), + new RsdoctorRspackMultiplePlugin({ + stage: 0, + disableClientServer: false, + features: ['bundle', 'plugins', 'loader'], + }), + new rspack.HtmlRspackPlugin({ + template: './index.html', + }), + new rspack.CopyRspackPlugin({ + patterns: [ + { + from: 'public', + }, + ], + }), + ], + }), + rspackBuild({ + ...config, + entry: './src/index.ts', + mode: 'none', + name: 'Builder 2', + target: 'node', + output: { + path: resolve(__dirname, 'dist/node'), + filename: 'index.js', + }, + plugins: [ + new RsdoctorRspackMultiplePlugin({ + stage: 1, + disableClientServer: false, + features: ['bundle', 'plugins', 'loader'], + }), + new rspack.CopyRspackPlugin({ + patterns: [ + { + from: 'public', + }, + ], + }), + ], + }), + ]); +} + +build(); diff --git a/examples/rspack-minimal/package.json b/examples/rspack-minimal/package.json index d65906589..c4bad8adb 100644 --- a/examples/rspack-minimal/package.json +++ b/examples/rspack-minimal/package.json @@ -4,8 +4,10 @@ "private": true, "scripts": { "dev": "ENABLE_CLIENT_SERVER=true NODE_ENV=development rspack serve", - "build": "ENABLE_CLIENT_SERVER=false NODE_ENV=production rspack build", - "build:analysis": "ENABLE_CLIENT_SERVER=true NODE_ENV=production rspack build" + "build:s": "ENABLE_CLIENT_SERVER=false NODE_ENV=production rspack build -c ./rspack.config.js", + "build:analysis": "ENABLE_CLIENT_SERVER=true NODE_ENV=production rspack build", + "build:m": "ENABLE_CLIENT_SERVER=false NODE_ENV=production node -r tsm build.ts", + "build": "npm run build:s && npm run build:m" }, "dependencies": { "@arco-design/web-react": "^2.58.3", @@ -18,6 +20,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "tsm": "2.3.0", "@rsdoctor/rspack-plugin": "workspace:*", "@rspack/cli": "0.5.1", "@rspack/core": "0.5.1", diff --git a/examples/rspack-minimal/src/App.tsx b/examples/rspack-minimal/src/App.tsx index f76940766..5d64e89ea 100644 --- a/examples/rspack-minimal/src/App.tsx +++ b/examples/rspack-minimal/src/App.tsx @@ -1,28 +1,17 @@ import React from 'react'; -import logo from './logo.svg'; -import Footer from './Footer'; -import './App.css'; +import { Button } from '@arco-design/web-react'; +import '@arco-design/web-react/dist/css/arco.css'; +import styles from './index.module.less'; -function App() { +export function App({ name }: any) { return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
-
+ <> + Hello {name}. Welcome! + <> +

标题{name}

+

内容

+ , + + ); } - -export default App; diff --git a/examples/rspack-minimal/src/app1.tsx b/examples/rspack-minimal/src/app1.tsx new file mode 100644 index 000000000..5d64e89ea --- /dev/null +++ b/examples/rspack-minimal/src/app1.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Button } from '@arco-design/web-react'; +import '@arco-design/web-react/dist/css/arco.css'; +import styles from './index.module.less'; + +export function App({ name }: any) { + return ( + <> + Hello {name}. Welcome! + <> +

标题{name}

+

内容

+ , + + + ); +} diff --git a/examples/rspack-minimal/src/declaration.d.ts b/examples/rspack-minimal/src/declaration.d.ts new file mode 100644 index 000000000..8eb1c2abd --- /dev/null +++ b/examples/rspack-minimal/src/declaration.d.ts @@ -0,0 +1,27 @@ +declare module '*.svg' { + const content: React.FunctionComponent>; + export default content; +} + +declare module '*.less' { + const classes: { [className: string]: string }; + export default classes; +} + +declare module '*/settings.json' { + const value: { + colorWeek: boolean; + navbar: boolean; + menu: boolean; + footer: boolean; + themeColor: string; + menuWidth: number; + }; + + export default value; +} + +declare module '*.png' { + const value: string; + export default value; +} diff --git a/examples/rspack-minimal/src/index.module.less b/examples/rspack-minimal/src/index.module.less new file mode 100644 index 000000000..3e09cbafe --- /dev/null +++ b/examples/rspack-minimal/src/index.module.less @@ -0,0 +1,4 @@ +.header { + background: var(--color-bg-2); + padding: 20px; +} diff --git a/examples/rspack-minimal/src/index.ts b/examples/rspack-minimal/src/index.ts new file mode 100644 index 000000000..5f1de5702 --- /dev/null +++ b/examples/rspack-minimal/src/index.ts @@ -0,0 +1,9 @@ +import { render } from 'react-dom'; +import { createElement } from 'react'; + +import { App } from './app1'; + +render( + createElement(App, { name: 'Taylor' }), + document.getElementById('root')!, +); diff --git a/packages/core/src/inner-plugins/utils/loader.ts b/packages/core/src/inner-plugins/utils/loader.ts index ac86da63b..232007e98 100644 --- a/packages/core/src/inner-plugins/utils/loader.ts +++ b/packages/core/src/inner-plugins/utils/loader.ts @@ -197,7 +197,8 @@ export async function reportLoader( // sdk exists means in the same process const sdk = getSDK(); - if (sdk?.reportLoader) { + + if (sdk?.reportLoader && !('parent' in sdk && sdk.parent)) { sdk.reportLoader(loaderData); sdk.reportSourceMap(sourceMapData); return loaderData; diff --git a/packages/core/src/inner-plugins/utils/sdk.ts b/packages/core/src/inner-plugins/utils/sdk.ts index 5315615e8..ba2709cd6 100644 --- a/packages/core/src/inner-plugins/utils/sdk.ts +++ b/packages/core/src/inner-plugins/utils/sdk.ts @@ -1,4 +1,4 @@ -import { RsdoctorWebpackSDK } from '@rsdoctor/sdk'; +import { RsdoctorSlaveSDK, RsdoctorWebpackSDK } from '@rsdoctor/sdk'; let sdk: RsdoctorWebpackSDK; @@ -6,6 +6,13 @@ export function setSDK(t: RsdoctorWebpackSDK) { sdk = t; } -export function getSDK(): RsdoctorWebpackSDK { +export function getSDK(builderName?: string) { + if (sdk && builderName && 'parent' in sdk) { + const _sdk = sdk as unknown as RsdoctorSlaveSDK; + const slaveSDK = _sdk.parent.slaves.find( + (_sdk: { name: string }) => _sdk.name === builderName, + ); + return slaveSDK || sdk; + } return sdk; } diff --git a/packages/core/src/types/plugin.ts b/packages/core/src/types/plugin.ts index 143cfc430..3f3d9fd9b 100644 --- a/packages/core/src/types/plugin.ts +++ b/packages/core/src/types/plugin.ts @@ -60,7 +60,7 @@ export interface RsdoctorWebpackPluginOptions< innerClientPath?: string; } -export interface RsdoctorWebpackMultiplePluginOptions< +export interface RsdoctorMultiplePluginOptions< Rules extends LinterType.ExtendRuleData[] = LinterType.ExtendRuleData[], > extends Omit, 'sdkInstance'>, Pick[0], 'stage'> { diff --git a/packages/rspack-plugin/src/builtinLoaderPlugin.ts b/packages/rspack-plugin/src/builtinLoaderPlugin.ts index 28edaae7f..8b4915ed2 100644 --- a/packages/rspack-plugin/src/builtinLoaderPlugin.ts +++ b/packages/rspack-plugin/src/builtinLoaderPlugin.ts @@ -21,13 +21,23 @@ export class BuiltinLoaderPlugin { typeof _builtinRule.options === 'string' ? {} : { ..._builtinRule }; + rule.use.splice(index, 0, { loader: path.join(__dirname, './probeLoader.js'), - options: { ..._options, type: 'end' }, + options: { + ..._options, + type: 'end', + builderName: compiler.options.name, + }, }); + rule.use.splice(index + 2, 0, { loader: path.join(__dirname, './probeLoader.js'), - options: { ..._options, type: 'start' }, + options: { + ..._options, + type: 'start', + builderName: compiler.options.name, + }, }); } return rule; diff --git a/packages/rspack-plugin/src/index.ts b/packages/rspack-plugin/src/index.ts index 1110b6451..d9c880bf8 100644 --- a/packages/rspack-plugin/src/index.ts +++ b/packages/rspack-plugin/src/index.ts @@ -1 +1,2 @@ export * from './plugin'; +export * from './multiple'; diff --git a/packages/rspack-plugin/src/multiple.ts b/packages/rspack-plugin/src/multiple.ts new file mode 100644 index 000000000..7d581875e --- /dev/null +++ b/packages/rspack-plugin/src/multiple.ts @@ -0,0 +1,38 @@ +import { RsdoctorSDKController } from '@rsdoctor/sdk'; +import type { Linter } from '@rsdoctor/types'; +import type { RsdoctorMultiplePluginOptions } from '@rsdoctor/core'; + +import { RsdoctorRspackPlugin } from './plugin'; + +let globalController: RsdoctorSDKController | undefined; + +export class RsdoctorRspackMultiplePlugin< + Rules extends Linter.ExtendRuleData[], +> extends RsdoctorRspackPlugin { + // @ts-expect-error + private controller: RsdoctorSDKController; + + constructor(options: RsdoctorMultiplePluginOptions = {}) { + const controller = (() => { + if (globalController) { + return globalController; + } + const controller = new RsdoctorSDKController(); + globalController = controller; + return controller; + })(); + + const instance = controller.createSlave({ + name: options.name || 'Builder', + stage: options.stage, + extraConfig: { disableTOSUpload: options.disableTOSUpload || false }, + }); + + super({ + ...options, + sdkInstance: instance, + }); + + this.controller = controller; + } +} diff --git a/packages/rspack-plugin/src/plugin.ts b/packages/rspack-plugin/src/plugin.ts index f2cf17adb..c6452a340 100644 --- a/packages/rspack-plugin/src/plugin.ts +++ b/packages/rspack-plugin/src/plugin.ts @@ -1,6 +1,6 @@ import type { Compiler, Configuration, RuleSetRule } from '@rspack/core'; import { ModuleGraph } from '@rsdoctor/graph'; -import { RsdoctorWebpackSDK } from '@rsdoctor/sdk'; +import { RsdoctorSlaveSDK, RsdoctorWebpackSDK } from '@rsdoctor/sdk'; import { InternalLoaderPlugin, InternalPluginsPlugin, @@ -35,7 +35,7 @@ export class RsdoctorRspackPlugin { public readonly name = pluginTapName; - public readonly sdk: RsdoctorWebpackSDK; + public readonly sdk: RsdoctorWebpackSDK | RsdoctorSlaveSDK; public _bootstrapTask!: Promise; @@ -47,13 +47,15 @@ export class RsdoctorRspackPlugin constructor(options?: RsdoctorRspackPluginOptions) { this.options = normalizeUserConfig(options); - this.sdk = new RsdoctorWebpackSDK({ - name: pluginTapName, - root: process.cwd(), - type: SDK.ToDataType.Normal, - config: { disableTOSUpload: this.options.disableTOSUpload }, - innerClientPath: this.options.innerClientPath, - }); + this.sdk = + this.options.sdkInstance ?? + new RsdoctorWebpackSDK({ + name: pluginTapName, + root: process.cwd(), + type: SDK.ToDataType.Normal, + config: { disableTOSUpload: this.options.disableTOSUpload }, + innerClientPath: this.options.innerClientPath, + }); this.modulesGraph = new ModuleGraph(); } @@ -67,6 +69,10 @@ export class RsdoctorRspackPlugin this._bootstrapTask = this.sdk.bootstrap(); } + if (compiler.options.name) { + this.sdk.setName(compiler.options.name); + } + setSDK(this.sdk); compiler.hooks.done.tapPromise( @@ -164,9 +170,5 @@ export class RsdoctorRspackPlugin this.sdk.setOutputDir( path.resolve(compiler.outputPath, `./${Constants.RsdoctorOutputFolder}`), ); - - if (configuration.name) { - this.sdk.setName(configuration.name); - } } } diff --git a/packages/rspack-plugin/src/probeLoader.ts b/packages/rspack-plugin/src/probeLoader.ts index cca65bfd9..3eb33de42 100644 --- a/packages/rspack-plugin/src/probeLoader.ts +++ b/packages/rspack-plugin/src/probeLoader.ts @@ -11,12 +11,13 @@ const loaderModule: Plugin.LoaderDefinition< > = function (...args) { const time = Date.now(); const code = args[0]; - const sdk = getSDK(); const _options = this.getOptions() as unknown as { loader: string; options: Record; type: 'start' | 'end'; + builderName: string; }; + const sdk = getSDK(_options.builderName); const loaderData: SDK.ResourceLoaderData = { resource: { diff --git a/packages/sdk/src/sdk/multiple/slave.ts b/packages/sdk/src/sdk/multiple/slave.ts index 3df673628..f31c2e3db 100644 --- a/packages/sdk/src/sdk/multiple/slave.ts +++ b/packages/sdk/src/sdk/multiple/slave.ts @@ -21,15 +21,20 @@ interface RsdoctorSlaveSDKOptions { export class RsdoctorSlaveSDK extends RsdoctorWebpackSDK { id: number; - public readonly stage: number; + parent: RsdoctorSDKController; - private parent: RsdoctorSDKController; + public readonly stage: number; private uploadPieces!: Promise; private finishUploadPieceSwitch!: () => void; - constructor({ name, stage, controller, extraConfig }: RsdoctorSlaveSDKOptions) { + constructor({ + name, + stage, + controller, + extraConfig, + }: RsdoctorSlaveSDKOptions) { super({ name, root: controller.root }); const lastSdk = controller.getLastSdk(); diff --git a/packages/webpack-plugin/src/multiple.ts b/packages/webpack-plugin/src/multiple.ts index fc01abe89..3ea9b118b 100644 --- a/packages/webpack-plugin/src/multiple.ts +++ b/packages/webpack-plugin/src/multiple.ts @@ -1,6 +1,6 @@ import { RsdoctorSDKController } from '@rsdoctor/sdk'; import type { Linter } from '@rsdoctor/types'; -import type { RsdoctorWebpackMultiplePluginOptions } from '@rsdoctor/core'; +import type { RsdoctorMultiplePluginOptions } from '@rsdoctor/core'; import { RsdoctorWebpackPlugin } from './plugin'; @@ -12,7 +12,7 @@ export class RsdoctorWebpackMultiplePlugin< // @ts-expect-error private controller: RsdoctorSDKController; - constructor(options: RsdoctorWebpackMultiplePluginOptions = {}) { + constructor(options: RsdoctorMultiplePluginOptions = {}) { const controller = (() => { if (globalController) { return globalController; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 629c4a849..722510d5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,6 +291,9 @@ importers: react-refresh: specifier: ^0.14.0 version: 0.14.0 + tsm: + specifier: 2.3.0 + version: 2.3.0 web-vitals: specifier: ^2.1.4 version: 2.1.4