diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 8ae913792..277df2f0f 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -1230,7 +1230,8 @@ const composeBundlelessExternalConfig = ( const styleRedirectExtension = redirect.style?.extension ?? true; const jsRedirectPath = redirect.js?.path ?? true; const jsRedirectExtension = redirect.js?.extension ?? true; - const assetRedirect = redirect.asset ?? true; + const assetRedirectPath = redirect.asset?.path ?? true; + const assetRedirectExtension = redirect.asset?.extension ?? true; let resolver: RspackResolver | undefined; @@ -1302,6 +1303,7 @@ const composeBundlelessExternalConfig = ( if (issuer) { let resolvedRequest: string = request; + const redirectedPath = await redirectPath(resolvedRequest); const cssExternal = await cssExternalHandler( resolvedRequest, callback, @@ -1309,7 +1311,7 @@ const composeBundlelessExternalConfig = ( cssModulesAuto, styleRedirectPath, styleRedirectExtension, - redirectPath, + redirectedPath, issuer, ); @@ -1317,11 +1319,11 @@ const composeBundlelessExternalConfig = ( return cssExternal; } + if (redirectedPath === undefined) { + return callback(undefined, request); + } + if (jsRedirectPath) { - const redirectedPath = await redirectPath(resolvedRequest); - if (redirectedPath === undefined) { - return callback(undefined, request); - } resolvedRequest = redirectedPath; } @@ -1346,7 +1348,11 @@ const composeBundlelessExternalConfig = ( } else { // 2. asset files, does not match jsExtensionsPattern, eg: ./foo.png -> ./foo.mjs // non-js && non-css files - if (assetRedirect) { + resolvedRequest = assetRedirectPath + ? redirectedPath + : request; + + if (assetRedirectExtension) { resolvedRequest = resolvedRequest.replace( /\.[^.]+$/, jsExtension, diff --git a/packages/core/src/css/cssConfig.ts b/packages/core/src/css/cssConfig.ts index fb0c8ffbc..c0ff16173 100644 --- a/packages/core/src/css/cssConfig.ts +++ b/packages/core/src/css/cssConfig.ts @@ -20,7 +20,7 @@ export async function cssExternalHandler( auto: CssLoaderOptionsAuto, styleRedirectPath: boolean, styleRedirectExtension: boolean, - redirectPath: (request: string) => Promise, + redirectedPath: string | undefined, issuer: string, ): Promise { // cssExtract: do not external @rsbuild/core/compiled/css-loader/noSourceMaps.js, sourceMaps.js, api.mjs etc. @@ -32,9 +32,8 @@ export async function cssExternalHandler( let resolvedRequest = request; if (styleRedirectPath) { - const redirectedPath = await redirectPath(resolvedRequest); if (redirectedPath === undefined) { - return callback(undefined, request); + return false; } resolvedRequest = redirectedPath; } diff --git a/packages/core/src/types/config.ts b/packages/core/src/types/config.ts index 60549bc50..e1f973884 100644 --- a/packages/core/src/types/config.ts +++ b/packages/core/src/types/config.ts @@ -191,6 +191,19 @@ export type StyleRedirect = { extension?: boolean; }; +export type AssetRedirect = { + /** + * Whether to automatically redirect the import paths of asset output files. + * @defaultValue `true` + */ + path?: boolean; + /** + * Whether to automatically redirect the file extension to import paths based on the asset output files. + * @defaultValue `true` + */ + extension?: boolean; +}; + export type DtsRedirect = { /** * Whether to automatically redirect the import paths of TypeScript declaration output files. @@ -210,7 +223,7 @@ export type Redirect = { /** Controls the redirect of the import paths of output style files. */ style?: StyleRedirect; /** Controls the redirect of the import paths of output asset files. */ - asset?: boolean; + asset?: AssetRedirect; /** Controls the redirect of the import paths of output TypeScript declaration files. */ dts?: DtsRedirect; }; diff --git a/tests/integration/redirect/asset.test.ts b/tests/integration/redirect/asset.test.ts index aae5eeffc..6ae437898 100644 --- a/tests/integration/redirect/asset.test.ts +++ b/tests/integration/redirect/asset.test.ts @@ -16,9 +16,23 @@ test('0. default', async () => { expect(indexCjs).toContain('require("./assets/logo.cjs")'); }); -test('1. redirect.asset = false', async () => { +test('1. redirect.asset.extension = false', async () => { const { content: indexJs } = queryContent(contents.esm1!, /index\.js/); const { content: indexCjs } = queryContent(contents.cjs1!, /index\.cjs/); expect(indexJs).toContain('import logo from "./assets/logo.svg";'); expect(indexCjs).toContain('require("./assets/logo.svg")'); }); + +test('2. redirect.asset.path = false', async () => { + const { content: indexJs } = queryContent(contents.esm2!, /index\.js/); + const { content: indexCjs } = queryContent(contents.cjs2!, /index\.cjs/); + expect(indexJs).toContain('import logo from "@/assets/logo.js";'); + expect(indexCjs).toContain('require("@/assets/logo.cjs")'); +}); + +test('3. redirect.asset.path = false, redirect.js.path = false', async () => { + const { content: indexJs } = queryContent(contents.esm3!, /index\.js/); + const { content: indexCjs } = queryContent(contents.cjs3!, /index\.cjs/); + expect(indexJs).toContain('import logo from "@/assets/logo.svg";'); + expect(indexCjs).toContain('require("@/assets/logo.svg")'); +}); diff --git a/tests/integration/redirect/asset/rslib.config.ts b/tests/integration/redirect/asset/rslib.config.ts index 697e61607..4fca60dde 100644 --- a/tests/integration/redirect/asset/rslib.config.ts +++ b/tests/integration/redirect/asset/rslib.config.ts @@ -20,10 +20,69 @@ export default defineConfig({ }, }, }), - // 1. redirect.asset: false + // 1. redirect.asset.extension: false generateBundleEsmConfig({ bundle: false, - redirect: { asset: false }, + redirect: { + asset: { + extension: false, + }, + }, + output: { + distPath: { + root: 'dist/asset-extension-false/esm', + }, + }, + }), + generateBundleCjsConfig({ + bundle: false, + redirect: { + asset: { + extension: false, + }, + }, + output: { + distPath: { + root: 'dist/asset-extension-false/cjs', + }, + }, + }), + // 2. redirect.asset.path: false + generateBundleEsmConfig({ + bundle: false, + redirect: { + asset: { + path: false, + }, + }, + output: { + distPath: { + root: 'dist/asset-path-false/esm', + }, + }, + }), + generateBundleCjsConfig({ + bundle: false, + redirect: { + asset: { + path: false, + }, + }, + output: { + distPath: { + root: 'dist/asset-path-false/cjs', + }, + }, + }), + // 3. redirect.asset.extension: false + redirect.asset.path: false + generateBundleEsmConfig({ + bundle: false, + redirect: { + asset: { + path: false, + extension: false, + }, + }, output: { distPath: { root: 'dist/asset-false/esm', @@ -32,7 +91,12 @@ export default defineConfig({ }), generateBundleCjsConfig({ bundle: false, - redirect: { asset: false }, + redirect: { + asset: { + path: false, + extension: false, + }, + }, output: { distPath: { root: 'dist/asset-false/cjs', @@ -43,4 +107,9 @@ export default defineConfig({ output: { target: 'web', }, + resolve: { + alias: { + '@': './src', + }, + }, }); diff --git a/tests/integration/redirect/asset/src/index.js b/tests/integration/redirect/asset/src/index.js index ffce93448..6bcc44ffb 100644 --- a/tests/integration/redirect/asset/src/index.js +++ b/tests/integration/redirect/asset/src/index.js @@ -1,3 +1,3 @@ -import svg from './assets/logo.svg'; +import svg from '@/assets/logo.svg'; console.log('svg: ', svg); diff --git a/website/docs/en/config/lib/redirect.mdx b/website/docs/en/config/lib/redirect.mdx index f46a57fce..bbd5e17c7 100644 --- a/website/docs/en/config/lib/redirect.mdx +++ b/website/docs/en/config/lib/redirect.mdx @@ -23,6 +23,11 @@ type StyleRedirect = { extension?: boolean; }; +type AssetRedirect = { + path?: boolean; + extension?: boolean; +}; + type DtsRedirect = { path?: boolean; extension?: boolean; @@ -31,7 +36,7 @@ type DtsRedirect = { type Redirect = { js?: JsRedirect; style?: StyleRedirect; - asset?: boolean; + asset?: AssetRedirect; dts?: DtsRedirect; }; ``` @@ -48,7 +53,10 @@ const defaultRedirect = { path: true, extension: true, }, - asset: true, + asset: { + path: true, + extension: true, + }, dts: { path: true, extension: false, @@ -187,6 +195,28 @@ import styles from './index.module.mjs'; // expected output Controls the redirect of the import paths of output asset files. +### redirect.asset.path + +Whether to automatically redirect the import paths of asset output files. + +- **Type:** `boolean` +- **Default:** `true` + +When set to `true`, the relevant redirect rules are the same as [redirect.js.path](/config/lib/redirect#redirectjspath). + +When set to `false`, the original import path will remain unchanged. + +- **Example:** + +```ts +import url from '@/assets/logo.svg'; // source code of './src/foo.ts' ↓ +import url from './assets/logo.svg'; // expected output of './dist/foo.js' +``` + +### redirect.asset.extension + +Whether to automatically redirect the file extension to import paths based on the asset output files. + - **Type:** `boolean` - **Default:** `true` @@ -197,10 +227,16 @@ When set to `false`, the file extension will remain unchanged from the original - **Example:** ```ts -import url from './assets/logo.svg'; // source code ↓ -import url from './assets/logo.mjs'; // expected output +import url from './assets/logo.svg'; // source code of './src/foo.ts' ↓ +import url from './assets/logo.mjs'; // expected output of './dist/foo.mjs' ``` +::: note + +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). + +::: + ## redirect.dts Controls the redirect of the import paths of output TypeScript declaration files. diff --git a/website/docs/en/guide/faq/features.mdx b/website/docs/en/guide/faq/features.mdx index 20292a44f..a61d1bd44 100644 --- a/website/docs/en/guide/faq/features.mdx +++ b/website/docs/en/guide/faq/features.mdx @@ -35,6 +35,41 @@ export default defineConfig({ }); ``` +## Static assets processing + +### How to skip the processing of static asset files in bundleless mode? + +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: + +1. Set `source.entry` to remove static asset files from the entry. +2. Set `output.copy` to copy static asset files to the output directory. +3. Set `redirect.asset.extension` to `false` to disable the redirect behavior for the import path of static asset files. + +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. + +```ts title="rslib.config.ts" +export default defineConfig({ + lib: [ + { + // ... + source: { + entry: { + index: ['./src/**', '!src/**/*.png'], + }, + }, + output: { + copy: [{ from: '**/*.png', context: path.join(__dirname, 'src') }], + }, + redirect: { + asset: { + extension: false, + }, + }, + }, + ], +}); +``` + ## Code minification ### How to preserve all comments in the output files? diff --git a/website/docs/zh/config/lib/redirect.mdx b/website/docs/zh/config/lib/redirect.mdx index c69ae9d7c..8a0f73534 100644 --- a/website/docs/zh/config/lib/redirect.mdx +++ b/website/docs/zh/config/lib/redirect.mdx @@ -23,6 +23,11 @@ type StyleRedirect = { extension?: boolean; }; +type AssetRedirect = { + path?: boolean; + extension?: boolean; +}; + type DtsRedirect = { path?: boolean; extension?: boolean; @@ -31,7 +36,7 @@ type DtsRedirect = { type Redirect = { js?: JsRedirect; style?: StyleRedirect; - asset?: boolean; + asset?: AssetRedirect; dts?: DtsRedirect; }; ``` @@ -48,7 +53,10 @@ const defaultRedirect = { path: true, extension: true, }, - asset: true, + asset: { + path: true, + extension: true, + }, dts: { path: true, extension: false, @@ -187,6 +195,28 @@ import styles from './index.module.mjs'; // 预期生成的代码 控制资源文件导入路径的重定向。 +### redirect.asset.path + +是否自动重定向资源文件的导入路径。 + +- **类型:** `boolean` +- **默认值:** `true` + +当设置为 `true` 时,相关重定向规则与 [redirect.js.path](/config/lib/redirect#redirectjspath) 相同。 + +当设置为 `false` 时,将保持原始导入路径不变。 + +- **示例:** + +```ts +import url from '@/assets/logo.svg'; // './src/foo.ts' 的源码 ↓ +import url from './assets/logo.svg'; // './dist/foo.js' 预期生成的代码 +``` + +### redirect.asset.extension + +是否根据资源产物文件自动重定向文件扩展名到导入路径。 + - **类型:** `boolean` - **默认值:** `true` @@ -197,10 +227,16 @@ import styles from './index.module.mjs'; // 预期生成的代码 - **示例:** ```ts -import url from './assets/logo.svg'; // 源码 ↓ -import url from './assets/logo.mjs'; // 预期生成的代码 +import url from './assets/logo.svg'; // './src/foo.ts' 的源码 ↓ +import url from './assets/logo.mjs'; // './dist/foo.mjs' 预期生成的代码 ``` +::: note + +在 JavaScript 文件中引用静态资源的方式及对应的产物结构,请查看 [引用静态资源](/guide/advanced/static-assets#在-JavaScript-文件中引用)。 + +::: + ## redirect.dts 控制 TypeScript 类型文件中导入路径的重定向。 diff --git a/website/docs/zh/guide/faq/features.mdx b/website/docs/zh/guide/faq/features.mdx index 088879d79..d29ec0ce0 100644 --- a/website/docs/zh/guide/faq/features.mdx +++ b/website/docs/zh/guide/faq/features.mdx @@ -35,6 +35,41 @@ export default defineConfig({ }); ``` +## 静态资源处理 + +### bundleless 模式如何跳过对静态资源文件的处理? + +在 bundleless 模式下,Rslib 默认会将静态资源文件转化为一个 JavaScript 文件和一个根据 [output.distPath](/config/rsbuild/output#outputdistpath) 输出的静态资源文件,并保留引用静态资源的 `import` 或 `require` 语句。跳过对静态资源文件的上述处理需要: + +1. 设置 `source.entry` 将静态资源文件从入口里移除。 +2. 设置 `output.copy` 将静态资源文件拷贝到产物目录。 +3. 设置 `redirect.asset.extension` 为 `false` 禁用对静态资源文件导入路径的重定向行为。 + +下面是一个跳过 `.png` 文件处理的例子,`src` 里面所有的 `.png` 文件都会被拷贝到产物目录下并且保留一致的相对路径。 + +```ts title="rslib.config.ts" +export default defineConfig({ + lib: [ + { + // ... + source: { + entry: { + index: ['./src/**', '!src/**/*.png'], + }, + }, + output: { + copy: [{ from: '**/*.png', context: path.join(__dirname, 'src') }], + }, + redirect: { + asset: { + extension: false, + }, + }, + }, + ], +}); +``` + ## 代码压缩 ### 如何保留产物文件代码中的注释?