Skip to content

Commit 970b573

Browse files
authored
fix(plugin-vue2): fix loaders duplicating problem caused by vue-loader@15 (#2142)
1 parent bdde12d commit 970b573

File tree

6 files changed

+74
-2
lines changed

6 files changed

+74
-2
lines changed

e2e/cases/vue2/sfc-style/index.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from '@playwright/test';
2-
import { build, gotoPage, rspackOnlyTest } from '@e2e/helper';
2+
import { build, gotoPage, rspackOnlyTest as test } from '@e2e/helper';
33

4-
rspackOnlyTest('should build Vue sfc style correctly', async ({ page }) => {
4+
test('should build Vue sfc style correctly', async ({ page }) => {
55
const rsbuild = await build({
66
cwd: __dirname,
77
runServer: true,
@@ -15,5 +15,7 @@ rspackOnlyTest('should build Vue sfc style correctly', async ({ page }) => {
1515
const body = page.locator('body');
1616
await expect(body).toHaveCSS('background-color', 'rgb(0, 0, 255)');
1717

18+
await expect(body).toHaveCSS('padding', '16px');
19+
1820
await rsbuild.close();
1921
});

e2e/cases/vue2/sfc-style/src/App.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@ export default {};
1212
background-color: green;
1313
}
1414
</style>
15+
16+
<style lang="less">
17+
body {
18+
// this case is to test if loaders run twice
19+
// if the next line is transformed twice, less will throw.
20+
padding: -webkit-calc(~'16px + env(safe-area-inset-bottom)');
21+
}
22+
</style>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { Rspack } from '@rsbuild/shared';
2+
3+
/**
4+
* this plugin is a quick fix for issue https://github.com/web-infra-dev/rsbuild/issues/2093
5+
*/
6+
export class VueLoader15PitchFixPlugin implements Rspack.RspackPluginInstance {
7+
readonly name = 'VueLoader15PitchFixPlugin';
8+
9+
apply(compiler: Rspack.Compiler) {
10+
const { NormalModule } = compiler.webpack;
11+
compiler.hooks.compilation.tap(this.name, (compilation) => {
12+
const isExpCssOn = compilation.compiler.options?.experiments?.css;
13+
// the related issue only happens when experiments.css is on
14+
if (!isExpCssOn) return;
15+
16+
NormalModule.getCompilationHooks(compilation).loader.tap(
17+
this.name,
18+
(loaderContext) => {
19+
if (
20+
// the related issue only happens for <style>
21+
/[?&]type=style/.test(loaderContext.resourceQuery) &&
22+
// the fix should be applied before `pitch` phase completed.
23+
// once `pitch` phase completed, vue-loader will remove its pitcher loader.
24+
/[\\/]vue-loader[\\/]lib[\\/]loaders[\\/]pitcher/.test(
25+
loaderContext.loaders?.[0]?.path || '',
26+
)
27+
) {
28+
const seen = new Set<string>();
29+
const loaders = [];
30+
// deduplicate loaders
31+
for (const loader of loaderContext.loaders || []) {
32+
const identifier =
33+
typeof loader === 'string'
34+
? loader
35+
: loader.path + loader.query;
36+
37+
if (!seen.has(identifier)) {
38+
seen.add(identifier);
39+
loaders.push(loader);
40+
}
41+
}
42+
43+
loaderContext.loaders = loaders;
44+
}
45+
},
46+
);
47+
});
48+
}
49+
}

packages/plugin-vue2/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { VueLoaderPlugin } from 'vue-loader';
33
import type { RsbuildPlugin } from '@rsbuild/core';
44
import type { VueLoaderOptions } from 'vue-loader';
55
import { applySplitChunksRule } from './splitChunks';
6+
import { VueLoader15PitchFixPlugin } from './VueLoader15PitchFixPlugin';
67

78
export type SplitVueChunkOptions = {
89
/**
@@ -60,6 +61,10 @@ export function pluginVue2(options: PluginVueOptions = {}): RsbuildPlugin {
6061
.options(vueLoaderOptions);
6162

6263
chain.plugin(CHAIN_ID.PLUGIN.VUE_LOADER_PLUGIN).use(VueLoaderPlugin);
64+
// we could remove this once a new vue-loader@15 is released with https://github.com/vuejs/vue-loader/pull/2071 shipped
65+
chain
66+
.plugin(CHAIN_ID.PLUGIN.VUE_LOADER_15_PITCH_FIX_PLUGIN)
67+
.use(VueLoader15PitchFixPlugin);
6368
});
6469

6570
applySplitChunksRule(api, options.splitChunks);

packages/plugin-vue2/tests/__snapshots__/index.test.ts.snap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ exports[`plugin-vue2 > should add vue-loader and VueLoaderPlugin correctly 1`] =
2222
},
2323
"plugins": [
2424
VueLoaderPlugin {},
25+
VueLoader15PitchFixPlugin {
26+
"name": "VueLoader15PitchFixPlugin",
27+
},
2528
],
2629
"resolve": {
2730
"alias": {
@@ -57,6 +60,9 @@ exports[`plugin-vue2 > should allow to configure vueLoader options 1`] = `
5760
},
5861
"plugins": [
5962
VueLoaderPlugin {},
63+
VueLoader15PitchFixPlugin {
64+
"name": "VueLoader15PitchFixPlugin",
65+
},
6066
],
6167
"resolve": {
6268
"alias": {

packages/shared/src/chain.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ export const CHAIN_ID = {
214214
ASSETS_RETRY: 'assets-retry',
215215
/** AutoSetRootFontSizePlugin */
216216
AUTO_SET_ROOT_SIZE: 'auto-set-root-size',
217+
/** VueLoader15PitchFixPlugin */
218+
VUE_LOADER_15_PITCH_FIX_PLUGIN: 'vue-loader-15-pitch-fix',
217219
},
218220
/** Predefined minimizers */
219221
MINIMIZER: {

0 commit comments

Comments
 (0)