Skip to content

Commit 38a5705

Browse files
authored
feat: add new output.emitAssets config (#2134)
1 parent bb694b9 commit 38a5705

File tree

12 files changed

+133
-14
lines changed

12 files changed

+133
-14
lines changed

.changeset/fair-singers-chew.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rsbuild/core': patch
3+
---
4+
5+
release: 0.6.4

e2e/cases/emit-assets/index.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect, test } from '@playwright/test';
2+
import { build } from '@e2e/helper';
3+
4+
test('should allow to disable emit assets for node target', async () => {
5+
const rsbuild = await build({
6+
cwd: __dirname,
7+
});
8+
9+
const files = await rsbuild.unwrapOutputJSON();
10+
const filenames = Object.keys(files);
11+
12+
expect(
13+
filenames.some((filename) =>
14+
filename.includes('dist/static/image/icon.png'),
15+
),
16+
).toBeTruthy();
17+
18+
expect(
19+
filenames.some((filename) =>
20+
filename.includes('dist/server/static/image/icon.png'),
21+
),
22+
).toBeFalsy();
23+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { defineConfig } from '@rsbuild/core';
2+
3+
export default defineConfig({
4+
output: {
5+
filenameHash: false,
6+
targets: ['web', 'node'],
7+
emitAssets: ({ target }) => target !== 'node',
8+
},
9+
});

e2e/cases/emit-assets/src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import small from '../../../assets/icon.png?url';
2+
3+
console.log(small);

packages/core/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ const getDefaultOutputConfig = (): NormalizedOutputConfig => ({
150150
auto: true,
151151
exportLocalsConvention: 'camelCase',
152152
},
153+
emitAssets: () => true,
153154
});
154155

155156
const createDefaultConfig = (): RsbuildConfig => ({

packages/core/src/mergeConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const OVERRIDE_PATH = [
1111
'output.inlineStyles',
1212
'output.cssModules.auto',
1313
'output.targets',
14+
'output.emitAssets',
1415
'server.printUrls',
1516
'dev.startUrl',
1617
'provider',

packages/core/src/plugins/asset.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,37 @@ import {
99
type BundlerChainRule,
1010
} from '@rsbuild/shared';
1111
import type { RsbuildPlugin } from '../types';
12+
import type { GeneratorOptionsByModuleType } from '@rspack/core';
1213

1314
const chainStaticAssetRule = ({
15+
emit,
1416
rule,
1517
maxSize,
1618
filename,
1719
assetType,
1820
}: {
21+
emit: boolean;
1922
rule: BundlerChainRule;
2023
maxSize: number;
2124
filename: string;
2225
assetType: string;
2326
}) => {
27+
const generatorOptions:
28+
| GeneratorOptionsByModuleType['asset']
29+
| GeneratorOptionsByModuleType['asset/resource'] = {
30+
filename,
31+
};
32+
33+
if (emit === false) {
34+
generatorOptions.emit = false;
35+
}
36+
2437
// force to url: "foo.png?url" or "foo.png?__inline=false"
2538
rule
2639
.oneOf(`${assetType}-asset-url`)
2740
.type('asset/resource')
2841
.resourceQuery(/(__inline=false|url)/)
29-
.set('generator', {
30-
filename,
31-
});
42+
.set('generator', generatorOptions);
3243

3344
// force to inline: "foo.png?inline"
3445
rule
@@ -45,9 +56,7 @@ const chainStaticAssetRule = ({
4556
maxSize,
4657
},
4758
})
48-
.set('generator', {
49-
filename,
50-
});
59+
.set('generator', generatorOptions);
5160
};
5261

5362
export function getRegExpForExts(exts: string[]): RegExp {
@@ -66,12 +75,13 @@ export const pluginAsset = (): RsbuildPlugin => ({
6675
name: 'rsbuild:asset',
6776

6877
setup(api) {
69-
api.modifyBundlerChain((chain, { isProd }) => {
78+
api.modifyBundlerChain((chain, { isProd, target }) => {
7079
const config = api.getNormalizedConfig();
7180

7281
const createAssetRule = (
7382
assetType: 'image' | 'media' | 'font' | 'svg',
7483
exts: string[],
84+
emit: boolean,
7585
) => {
7686
const regExp = getRegExpForExts(exts);
7787
const distDir = getDistPath(config, assetType);
@@ -84,17 +94,24 @@ export const pluginAsset = (): RsbuildPlugin => ({
8494
const rule = chain.module.rule(assetType).test(regExp);
8595

8696
chainStaticAssetRule({
97+
emit,
8798
rule,
8899
maxSize,
89100
filename: path.posix.join(distDir, filename),
90101
assetType,
91102
});
92103
};
93104

94-
createAssetRule('image', IMAGE_EXTENSIONS);
95-
createAssetRule('svg', ['svg']);
96-
createAssetRule('media', [...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS]);
97-
createAssetRule('font', FONT_EXTENSIONS);
105+
const emit = config.output.emitAssets({ target });
106+
107+
createAssetRule('image', IMAGE_EXTENSIONS, emit);
108+
createAssetRule('svg', ['svg'], emit);
109+
createAssetRule(
110+
'media',
111+
[...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS],
112+
emit,
113+
);
114+
createAssetRule('font', FONT_EXTENSIONS, emit);
98115
});
99116
},
100117
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# output.emitAssets
2+
3+
- **Type:**
4+
5+
```ts
6+
type EmitAssets = (params: { target: RsbuildTarget }) => boolean;
7+
```
8+
9+
- **Default:** `() => true`
10+
11+
Control whether to emit static assets such as images, fonts, audio, video, etc.
12+
13+
In scenarios such as SSR, you may not need to emit duplicate static assets. Therefore, you can return `false` in `emitAssets` to emitting assets.
14+
15+
## Example
16+
17+
For example, the following example will emit static assets when building web bundles, and avoid emitting when building node bundles.
18+
19+
```js
20+
export default {
21+
output: {
22+
targets: ['web', 'node'],
23+
emitAssets: ({ target }) => target !== 'node',
24+
},
25+
};
26+
```

packages/document/docs/en/guide/basic/static-assets.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Import Static Assets
22

3-
Rsbuild supports import static assets, including images, fonts, and medias.
3+
Rsbuild supports import static assets, including images, fonts, audio and video.
44

55
:::tip What is Static Assets
66
Static assets are files that are part of a web application and do not change, even when the application is being used. Examples of static assets include images, fonts, medias, stylesheets, and JavaScript files. These assets are typically stored on a web server or CDN, and delivered to the user's web browser when the Web application is accessed. Because they do not change, static assets can be cached by the browser, which helps to improve the performance of the Web application.
@@ -10,7 +10,7 @@ Static assets are files that are part of a web application and do not change, ev
1010

1111
The following are the formats supported by Rsbuild by default:
1212

13-
- **image**: png, jpg, jpeg, gif, svg, bmp, webp, ico, apng, avif, tif, tiff, jfif, pjpeg, pjp.
13+
- **images**: png, jpg, jpeg, gif, svg, bmp, webp, ico, apng, avif, tif, tiff, jfif, pjpeg, pjp.
1414
- **fonts**: woff, woff2, eot, ttf, otf, ttc.
1515
- **audio**: mp3, wav, flac, aac, m4a, opus.
1616
- **video**: mp4, webm, ogg, mov.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# output.emitAssets
2+
3+
- **类型:**
4+
5+
```ts
6+
type EmitAssets = (params: { target: RsbuildTarget }) => boolean;
7+
```
8+
9+
- **默认值:** `() => true`
10+
11+
用于控制是否输出图片、字体、音频、视频等静态资源。
12+
13+
在 SSR 等场景下,你可能不需要输出重复的静态资源,因此你可以在 `emitAssets` 返回 `false` 来避免资源输出。
14+
15+
## 示例
16+
17+
比如,以下例子会在构建 web 产物时输出静态资源,而在构建 node 产物时避免输出。
18+
19+
```js
20+
export default {
21+
output: {
22+
targets: ['web', 'node'],
23+
emitAssets: ({ target }) => target !== 'node',
24+
},
25+
};
26+
```

0 commit comments

Comments
 (0)