Skip to content

[Bug]: rspack plugins of configs are not merged correctly when using both object and function #4759

@georgejinmeeee

Description

@georgejinmeeee

Version

System:
    OS: macOS 15.1
    CPU: (11) arm64 Apple M3 Pro
    Memory: 95.72 MB / 18.00 GB
    Shell: 5.9 - /bin/zsh
  Browsers:
    Chrome: 133.0.6943.142
    Safari: 18.1
  npmPackages:
    @rsbuild/core: 1.2.16 => 1.2.16

Details

The following script:

import { createRsbuild, defineConfig, mergeRsbuildConfig } from '@rsbuild/core';

class RspackPlugin1 {
  constructor() {
    this.name = 1;
  }
  apply(compiler) {
    compiler.hooks.done.tap('RspackPlugin1', () => {
      console.log(this.name);
    });
  }
}

class RspackPlugin2 {
  constructor() {
    this.name = 2;
  }
  apply(compiler) {
    compiler.hooks.done.tap('RspackPlugin2', () => {
      console.log(this.name);
    });
  }
}

class RspackPlugin3 {
  constructor() {
    this.name = 3;
  }
  apply(compiler) {
    compiler.hooks.done.tap('RspackPlugin3', () => {
      console.log(this.name);
    });
  }
}

const config1 = defineConfig({
  tools: {
    rspack: {
      plugins: [new RspackPlugin1()]
    }
  },
});

const config2 = defineConfig({
  tools: {
    rspack(_config, { appendPlugins }) {
      appendPlugins(new RspackPlugin2())
    }
  },
});

const config3 = defineConfig({
  tools: {
    rspack: {
      plugins: [new RspackPlugin3()]
    }
  },
});

const mergedConfig = mergeRsbuildConfig(config1, config2, config3)
const rsbuildInstance = await createRsbuild({ rsbuildConfig: mergedConfig })
rsbuildInstance.inspectConfig({ mode: 'production',  writeToDisk: true })

After generating the inspect file, RspackPlugin2 is missing.
I have researched the source code and found that:

  1. Funciton modifyRspackConfig will combine configs by lib reduce-configs, which recursively call webpack-merge.
  2. webpack-merge will return a new object after combining two plain objects.
  3. When reduce-configs meets functional config, it directly call it. Here is
 rspack(_config, { appendPlugins }) {
      appendPlugins(new RspackPlugin2())
 }
  1. Then we call appendPlugins. But in this function, we still use the original config instead of the new one returned by webpack-merge.
  2. So, RspackPlugin2 is missing.

I found this problem when using both rslib and rsdoctor. The options passed to rsdoctor is always not working. The reason is that my customized rsdoctor is not registered. I use the default one instead.

Reproduce link

Just run the script

Reproduce Steps

node index.js

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions