Skip to content

Commit f6e761c

Browse files
authored
feat!: support redirect.asset.path and redirect.asset.extension (#1119)
1 parent 80e3941 commit f6e761c

File tree

10 files changed

+267
-24
lines changed

10 files changed

+267
-24
lines changed

packages/core/src/config.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,7 +1230,8 @@ const composeBundlelessExternalConfig = (
12301230
const styleRedirectExtension = redirect.style?.extension ?? true;
12311231
const jsRedirectPath = redirect.js?.path ?? true;
12321232
const jsRedirectExtension = redirect.js?.extension ?? true;
1233-
const assetRedirect = redirect.asset ?? true;
1233+
const assetRedirectPath = redirect.asset?.path ?? true;
1234+
const assetRedirectExtension = redirect.asset?.extension ?? true;
12341235

12351236
let resolver: RspackResolver | undefined;
12361237

@@ -1302,26 +1303,27 @@ const composeBundlelessExternalConfig = (
13021303
if (issuer) {
13031304
let resolvedRequest: string = request;
13041305

1306+
const redirectedPath = await redirectPath(resolvedRequest);
13051307
const cssExternal = await cssExternalHandler(
13061308
resolvedRequest,
13071309
callback,
13081310
jsExtension,
13091311
cssModulesAuto,
13101312
styleRedirectPath,
13111313
styleRedirectExtension,
1312-
redirectPath,
1314+
redirectedPath,
13131315
issuer,
13141316
);
13151317

13161318
if (cssExternal !== false) {
13171319
return cssExternal;
13181320
}
13191321

1322+
if (redirectedPath === undefined) {
1323+
return callback(undefined, request);
1324+
}
1325+
13201326
if (jsRedirectPath) {
1321-
const redirectedPath = await redirectPath(resolvedRequest);
1322-
if (redirectedPath === undefined) {
1323-
return callback(undefined, request);
1324-
}
13251327
resolvedRequest = redirectedPath;
13261328
}
13271329

@@ -1346,7 +1348,11 @@ const composeBundlelessExternalConfig = (
13461348
} else {
13471349
// 2. asset files, does not match jsExtensionsPattern, eg: ./foo.png -> ./foo.mjs
13481350
// non-js && non-css files
1349-
if (assetRedirect) {
1351+
resolvedRequest = assetRedirectPath
1352+
? redirectedPath
1353+
: request;
1354+
1355+
if (assetRedirectExtension) {
13501356
resolvedRequest = resolvedRequest.replace(
13511357
/\.[^.]+$/,
13521358
jsExtension,

packages/core/src/css/cssConfig.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export async function cssExternalHandler(
2020
auto: CssLoaderOptionsAuto,
2121
styleRedirectPath: boolean,
2222
styleRedirectExtension: boolean,
23-
redirectPath: (request: string) => Promise<string | undefined>,
23+
redirectedPath: string | undefined,
2424
issuer: string,
2525
): Promise<false | void> {
2626
// cssExtract: do not external @rsbuild/core/compiled/css-loader/noSourceMaps.js, sourceMaps.js, api.mjs etc.
@@ -32,9 +32,8 @@ export async function cssExternalHandler(
3232
let resolvedRequest = request;
3333

3434
if (styleRedirectPath) {
35-
const redirectedPath = await redirectPath(resolvedRequest);
3635
if (redirectedPath === undefined) {
37-
return callback(undefined, request);
36+
return false;
3837
}
3938
resolvedRequest = redirectedPath;
4039
}

packages/core/src/types/config.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,19 @@ export type StyleRedirect = {
191191
extension?: boolean;
192192
};
193193

194+
export type AssetRedirect = {
195+
/**
196+
* Whether to automatically redirect the import paths of asset output files.
197+
* @defaultValue `true`
198+
*/
199+
path?: boolean;
200+
/**
201+
* Whether to automatically redirect the file extension to import paths based on the asset output files.
202+
* @defaultValue `true`
203+
*/
204+
extension?: boolean;
205+
};
206+
194207
export type DtsRedirect = {
195208
/**
196209
* Whether to automatically redirect the import paths of TypeScript declaration output files.
@@ -210,7 +223,7 @@ export type Redirect = {
210223
/** Controls the redirect of the import paths of output style files. */
211224
style?: StyleRedirect;
212225
/** Controls the redirect of the import paths of output asset files. */
213-
asset?: boolean;
226+
asset?: AssetRedirect;
214227
/** Controls the redirect of the import paths of output TypeScript declaration files. */
215228
dts?: DtsRedirect;
216229
};

tests/integration/redirect/asset.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,23 @@ test('0. default', async () => {
1616
expect(indexCjs).toContain('require("./assets/logo.cjs")');
1717
});
1818

19-
test('1. redirect.asset = false', async () => {
19+
test('1. redirect.asset.extension = false', async () => {
2020
const { content: indexJs } = queryContent(contents.esm1!, /index\.js/);
2121
const { content: indexCjs } = queryContent(contents.cjs1!, /index\.cjs/);
2222
expect(indexJs).toContain('import logo from "./assets/logo.svg";');
2323
expect(indexCjs).toContain('require("./assets/logo.svg")');
2424
});
25+
26+
test('2. redirect.asset.path = false', async () => {
27+
const { content: indexJs } = queryContent(contents.esm2!, /index\.js/);
28+
const { content: indexCjs } = queryContent(contents.cjs2!, /index\.cjs/);
29+
expect(indexJs).toContain('import logo from "@/assets/logo.js";');
30+
expect(indexCjs).toContain('require("@/assets/logo.cjs")');
31+
});
32+
33+
test('3. redirect.asset.path = false, redirect.js.path = false', async () => {
34+
const { content: indexJs } = queryContent(contents.esm3!, /index\.js/);
35+
const { content: indexCjs } = queryContent(contents.cjs3!, /index\.cjs/);
36+
expect(indexJs).toContain('import logo from "@/assets/logo.svg";');
37+
expect(indexCjs).toContain('require("@/assets/logo.svg")');
38+
});

tests/integration/redirect/asset/rslib.config.ts

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,69 @@ export default defineConfig({
2020
},
2121
},
2222
}),
23-
// 1. redirect.asset: false
23+
// 1. redirect.asset.extension: false
2424
generateBundleEsmConfig({
2525
bundle: false,
26-
redirect: { asset: false },
26+
redirect: {
27+
asset: {
28+
extension: false,
29+
},
30+
},
31+
output: {
32+
distPath: {
33+
root: 'dist/asset-extension-false/esm',
34+
},
35+
},
36+
}),
37+
generateBundleCjsConfig({
38+
bundle: false,
39+
redirect: {
40+
asset: {
41+
extension: false,
42+
},
43+
},
44+
output: {
45+
distPath: {
46+
root: 'dist/asset-extension-false/cjs',
47+
},
48+
},
49+
}),
50+
// 2. redirect.asset.path: false
51+
generateBundleEsmConfig({
52+
bundle: false,
53+
redirect: {
54+
asset: {
55+
path: false,
56+
},
57+
},
58+
output: {
59+
distPath: {
60+
root: 'dist/asset-path-false/esm',
61+
},
62+
},
63+
}),
64+
generateBundleCjsConfig({
65+
bundle: false,
66+
redirect: {
67+
asset: {
68+
path: false,
69+
},
70+
},
71+
output: {
72+
distPath: {
73+
root: 'dist/asset-path-false/cjs',
74+
},
75+
},
76+
}),
77+
// 3. redirect.asset.extension: false + redirect.asset.path: false
78+
generateBundleEsmConfig({
79+
bundle: false,
80+
redirect: {
81+
asset: {
82+
path: false,
83+
extension: false,
84+
},
85+
},
2786
output: {
2887
distPath: {
2988
root: 'dist/asset-false/esm',
@@ -32,7 +91,12 @@ export default defineConfig({
3291
}),
3392
generateBundleCjsConfig({
3493
bundle: false,
35-
redirect: { asset: false },
94+
redirect: {
95+
asset: {
96+
path: false,
97+
extension: false,
98+
},
99+
},
36100
output: {
37101
distPath: {
38102
root: 'dist/asset-false/cjs',
@@ -43,4 +107,9 @@ export default defineConfig({
43107
output: {
44108
target: 'web',
45109
},
110+
resolve: {
111+
alias: {
112+
'@': './src',
113+
},
114+
},
46115
});
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import svg from './assets/logo.svg';
1+
import svg from '@/assets/logo.svg';
22

33
console.log('svg: ', svg);

website/docs/en/config/lib/redirect.mdx

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ type StyleRedirect = {
2323
extension?: boolean;
2424
};
2525

26+
type AssetRedirect = {
27+
path?: boolean;
28+
extension?: boolean;
29+
};
30+
2631
type DtsRedirect = {
2732
path?: boolean;
2833
extension?: boolean;
@@ -31,7 +36,7 @@ type DtsRedirect = {
3136
type Redirect = {
3237
js?: JsRedirect;
3338
style?: StyleRedirect;
34-
asset?: boolean;
39+
asset?: AssetRedirect;
3540
dts?: DtsRedirect;
3641
};
3742
```
@@ -48,7 +53,10 @@ const defaultRedirect = {
4853
path: true,
4954
extension: true,
5055
},
51-
asset: true,
56+
asset: {
57+
path: true,
58+
extension: true,
59+
},
5260
dts: {
5361
path: true,
5462
extension: false,
@@ -187,6 +195,28 @@ import styles from './index.module.mjs'; // expected output
187195

188196
Controls the redirect of the import paths of output asset files.
189197

198+
### redirect.asset.path
199+
200+
Whether to automatically redirect the import paths of asset output files.
201+
202+
- **Type:** `boolean`
203+
- **Default:** `true`
204+
205+
When set to `true`, the relevant redirect rules are the same as [redirect.js.path](/config/lib/redirect#redirectjspath).
206+
207+
When set to `false`, the original import path will remain unchanged.
208+
209+
- **Example:**
210+
211+
```ts
212+
import url from '@/assets/logo.svg'; // source code of './src/foo.ts' ↓
213+
import url from './assets/logo.svg'; // expected output of './dist/foo.js'
214+
```
215+
216+
### redirect.asset.extension
217+
218+
Whether to automatically redirect the file extension to import paths based on the asset output files.
219+
190220
- **Type:** `boolean`
191221
- **Default:** `true`
192222

@@ -197,10 +227,16 @@ When set to `false`, the file extension will remain unchanged from the original
197227
- **Example:**
198228

199229
```ts
200-
import url from './assets/logo.svg'; // source code ↓
201-
import url from './assets/logo.mjs'; // expected output
230+
import url from './assets/logo.svg'; // source code of './src/foo.ts'
231+
import url from './assets/logo.mjs'; // expected output of './dist/foo.mjs'
202232
```
203233

234+
::: note
235+
236+
The way to import static assets in a JavaScript file and the corresponding output structure, please see [Import static assets](/guide/advanced/static-assets#import-assets-in-javascript-file).
237+
238+
:::
239+
204240
## redirect.dts
205241

206242
Controls the redirect of the import paths of output TypeScript declaration files.

website/docs/en/guide/faq/features.mdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ export default defineConfig({
3535
});
3636
```
3737

38+
## Static assets processing
39+
40+
### How to skip the processing of static asset files in bundleless mode?
41+
42+
In bundleless mode, Rslib transforms the source static asset file into a JavaScript file and a static asset file that is emitted according to [output.distPath](/config/rsbuild/output#outputdistpath) by default with preserving the `import` or `require` statements for static assets. To skip the above processing of static asset files, you need to:
43+
44+
1. Set `source.entry` to remove static asset files from the entry.
45+
2. Set `output.copy` to copy static asset files to the output directory.
46+
3. Set `redirect.asset.extension` to `false` to disable the redirect behavior for the import path of static asset files.
47+
48+
Below is an example of skipping the `.png` file processing. All `.png` files in `src` will be copied to the output directory and retained with consistent relative paths.
49+
50+
```ts title="rslib.config.ts"
51+
export default defineConfig({
52+
lib: [
53+
{
54+
// ...
55+
source: {
56+
entry: {
57+
index: ['./src/**', '!src/**/*.png'],
58+
},
59+
},
60+
output: {
61+
copy: [{ from: '**/*.png', context: path.join(__dirname, 'src') }],
62+
},
63+
redirect: {
64+
asset: {
65+
extension: false,
66+
},
67+
},
68+
},
69+
],
70+
});
71+
```
72+
3873
## Code minification
3974

4075
### How to preserve all comments in the output files?

0 commit comments

Comments
 (0)