Skip to content

Commit 647ee03

Browse files
authored
fix(plugin-svgr): SVGs generated with different hash (#5016)
1 parent 2faec40 commit 647ee03

File tree

9 files changed

+129
-23
lines changed

9 files changed

+129
-23
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { build } from '@e2e/helper';
2+
import { expect, test } from '@playwright/test';
3+
4+
// https://github.com/web-infra-dev/rsbuild/issues/4610
5+
test('should generate the same hash digest for the same SVG', async ({
6+
page,
7+
}) => {
8+
const rsbuild = await build({
9+
cwd: __dirname,
10+
page,
11+
});
12+
13+
const files = await rsbuild.unwrapOutputJSON();
14+
15+
expect(
16+
Object.keys(files).filter((key) => key.endsWith('.svg')).length,
17+
).toEqual(1);
18+
19+
await rsbuild.close();
20+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { defineConfig } from '@rsbuild/core';
2+
import { pluginReact } from '@rsbuild/plugin-react';
3+
import { pluginSvgr } from '@rsbuild/plugin-svgr';
4+
5+
export default defineConfig({
6+
plugins: [
7+
pluginReact(),
8+
pluginSvgr({
9+
mixedImport: true,
10+
}),
11+
],
12+
output: {
13+
dataUriLimit: 0,
14+
},
15+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import svg1 from '@assets/circle.svg';
2+
import svg2 from '@assets/circle.svg?url';
3+
4+
console.log(svg1, svg2);

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"build:doc": "cd website && pnpm run build",
1414
"change": "changeset",
1515
"changeset": "changeset",
16-
"check-dependency-version": "pnpx check-dependency-version-consistency .",
16+
"check-dependency-version": "pnpx check-dependency-version-consistency . --ignore-dep loader-utils",
1717
"check-spell": "pnpx cspell && heading-case",
1818
"dev:doc": "cd website && pnpm run dev",
1919
"e2e": "cd ./e2e && pnpm test",
@@ -62,7 +62,9 @@
6262
"patchedDependencies": {
6363
6464
65-
65+
66+
"url-loader": "patches/[email protected]",
67+
"file-loader": "patches/[email protected]"
6668
}
6769
}
6870
}

packages/plugin-svgr/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"@svgr/plugin-jsx": "8.1.0",
3434
"@svgr/plugin-svgo": "8.1.0",
3535
"deepmerge": "^4.3.1",
36-
"loader-utils": "^2.0.4"
36+
"loader-utils": "^3.3.1"
3737
},
3838
"devDependencies": {
3939
"@rsbuild/core": "workspace:*",

packages/plugin-svgr/prebundle.config.mjs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22
import { readFileSync, writeFileSync } from 'node:fs';
33
import { join } from 'node:path';
44

5-
// The package size of `schema-utils` is large, and validate has a performance overhead of tens of ms.
6-
// So we skip the validation and let TypeScript to ensure type safety.
7-
const writeEmptySchemaUtils = (task) => {
8-
const schemaUtilsPath = join(task.distPath, 'schema-utils.js');
9-
writeFileSync(schemaUtilsPath, 'module.exports.validate = () => {};');
10-
};
11-
125
/** @type {import('prebundle').Config} */
136
export default {
147
prettier: true,
@@ -17,28 +10,22 @@ export default {
1710
name: 'file-loader',
1811
ignoreDts: true,
1912
externals: {
20-
'schema-utils': './schema-utils',
2113
'loader-utils': 'loader-utils',
2214
},
23-
afterBundle: writeEmptySchemaUtils,
2415
},
2516
{
2617
name: 'url-loader',
2718
ignoreDts: true,
2819
externals: {
29-
'schema-utils': './schema-utils',
3020
'loader-utils': 'loader-utils',
3121
},
3222
afterBundle(task) {
33-
writeEmptySchemaUtils(task);
34-
3523
const filePath = join(task.distPath, 'index.js');
3624
const content = readFileSync(filePath, 'utf-8');
3725
const newContent = content.replace(
3826
/['"]file-loader['"]/,
3927
'require.resolve("../file-loader")',
4028
);
41-
4229
if (newContent !== content) {
4330
writeFileSync(filePath, newContent);
4431
}

patches/[email protected]

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
diff --git a/CHANGELOG.md b/CHANGELOG.md
2+
deleted file mode 100644
3+
index 1390102e57cacd2f334ccd8992bea23d40817b88..0000000000000000000000000000000000000000
4+
diff --git a/dist/index.js b/dist/index.js
5+
index ec5d5b0eda115042642d64b8873021db24aba9f3..939d5bf423dd831c268bfa0f581e2c598a455b2f 100644
6+
--- a/dist/index.js
7+
+++ b/dist/index.js
8+
@@ -10,20 +10,12 @@ var _path = _interopRequireDefault(require("path"));
9+
10+
var _loaderUtils = require("loader-utils");
11+
12+
-var _schemaUtils = require("schema-utils");
13+
-
14+
-var _options = _interopRequireDefault(require("./options.json"));
15+
-
16+
var _utils = require("./utils");
17+
18+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19+
20+
function loader(content) {
21+
- const options = (0, _loaderUtils.getOptions)(this);
22+
- (0, _schemaUtils.validate)(_options.default, options, {
23+
- name: 'File Loader',
24+
- baseDataPath: 'options'
25+
- });
26+
+ const options = this.getOptions() || {};
27+
const context = options.context || this.rootContext;
28+
const name = options.name || '[contenthash].[ext]';
29+
const url = (0, _loaderUtils.interpolateName)(this, name, {

patches/[email protected]

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
diff --git a/CHANGELOG.md b/CHANGELOG.md
2+
deleted file mode 100644
3+
index 768f7f35c56f282c7c0cda7a4c1c5e7e0b113560..0000000000000000000000000000000000000000
4+
diff --git a/dist/index.js b/dist/index.js
5+
index 7d977a6ece276d360bcb857cad9950252353da46..a2e19ed214a00950b3ea4c27eec7f890ef3e14f8 100644
6+
--- a/dist/index.js
7+
+++ b/dist/index.js
8+
@@ -8,16 +8,10 @@ exports.raw = void 0;
9+
10+
var _path = _interopRequireDefault(require("path"));
11+
12+
-var _loaderUtils = require("loader-utils");
13+
-
14+
-var _schemaUtils = require("schema-utils");
15+
-
16+
var _mimeTypes = _interopRequireDefault(require("mime-types"));
17+
18+
var _normalizeFallback = _interopRequireDefault(require("./utils/normalizeFallback"));
19+
20+
-var _options = _interopRequireDefault(require("./options.json"));
21+
-
22+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23+
24+
function shouldTransform(limit, size) {
25+
@@ -87,11 +81,7 @@ function getEncodedData(generator, mimetype, encoding, content, resourcePath) {
26+
27+
function loader(content) {
28+
// Loader Options
29+
- const options = (0, _loaderUtils.getOptions)(this) || {};
30+
- (0, _schemaUtils.validate)(_options.default, options, {
31+
- name: 'URL Loader',
32+
- baseDataPath: 'options'
33+
- }); // No limit or within the specified limit
34+
+ const options = this.getOptions() || {};
35+
36+
if (shouldTransform(options.limit, content.length)) {
37+
const {

pnpm-lock.yaml

Lines changed: 19 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)