diff --git a/examples/preact-component-bundle-false/rslib.config.ts b/examples/preact-component-bundle-false/rslib.config.ts index 25877d47c..a61a39bed 100644 --- a/examples/preact-component-bundle-false/rslib.config.ts +++ b/examples/preact-component-bundle-false/rslib.config.ts @@ -3,11 +3,6 @@ import { pluginSass } from '@rsbuild/plugin-sass'; import { defineConfig } from '@rslib/core'; export default defineConfig({ - source: { - entry: { - index: ['./src/**'], - }, - }, lib: [ { bundle: false, diff --git a/tests/integration/asset/limit/assets/logo.svg b/examples/preact-component-bundle-false/src/assets/logo.svg similarity index 100% rename from tests/integration/asset/limit/assets/logo.svg rename to examples/preact-component-bundle-false/src/assets/logo.svg diff --git a/examples/preact-component-bundle-false/src/components/CounterButton/index.tsx b/examples/preact-component-bundle-false/src/components/CounterButton/index.tsx index 09f8b087a..52b41d1bb 100644 --- a/examples/preact-component-bundle-false/src/components/CounterButton/index.tsx +++ b/examples/preact-component-bundle-false/src/components/CounterButton/index.tsx @@ -1,4 +1,5 @@ import type { FunctionComponent } from 'preact'; +import logo from '../../assets/logo.svg'; import styles from './index.module.scss'; interface CounterButtonProps { @@ -10,7 +11,12 @@ export const CounterButton: FunctionComponent = ({ onClick, label, }) => ( - ); diff --git a/examples/preact-component-bundle-false/src/env.d.ts b/examples/preact-component-bundle-false/src/env.d.ts index 0506fbcb4..8fa0ce11c 100644 --- a/examples/preact-component-bundle-false/src/env.d.ts +++ b/examples/preact-component-bundle-false/src/env.d.ts @@ -2,3 +2,8 @@ declare module '*.module.scss' { const classes: { [key: string]: string }; export default classes; } + +declare module '*.svg' { + const url: string; + export default url; +} diff --git a/examples/preact-component-bundle-false/src/index.scss b/examples/preact-component-bundle-false/src/index.scss index 2e506a0ac..f4c82dce4 100644 --- a/examples/preact-component-bundle-false/src/index.scss +++ b/examples/preact-component-bundle-false/src/index.scss @@ -1,3 +1,10 @@ +.counter-title { + width: 100px; + height: 100px; + background: no-repeat url('./assets/logo.svg'); + background-size: cover; +} + .counter-text { font-size: 50px; } diff --git a/examples/react-component-bundle-false/rslib.config.ts b/examples/react-component-bundle-false/rslib.config.ts index e02f94d36..cf6bb0639 100644 --- a/examples/react-component-bundle-false/rslib.config.ts +++ b/examples/react-component-bundle-false/rslib.config.ts @@ -3,11 +3,6 @@ import { pluginSass } from '@rsbuild/plugin-sass'; import { defineConfig } from '@rslib/core'; export default defineConfig({ - source: { - entry: { - index: ['./src/**'], - }, - }, lib: [ { format: 'esm', @@ -32,7 +27,6 @@ export default defineConfig({ ], output: { target: 'web', - assetPrefix: 'auto', // TODO: move this line to packages/core/src/asset/assetConfig.ts, }, plugins: [pluginReact(), pluginSass()], }); diff --git a/examples/react-component-bundle-false/src/components/CounterButton/index.tsx b/examples/react-component-bundle-false/src/components/CounterButton/index.tsx index be37643e0..2638731c6 100644 --- a/examples/react-component-bundle-false/src/components/CounterButton/index.tsx +++ b/examples/react-component-bundle-false/src/components/CounterButton/index.tsx @@ -1,4 +1,5 @@ import type React from 'react'; +import logo from '../../assets/logo.svg'; import styles from './index.module.scss'; interface CounterButtonProps { @@ -10,7 +11,12 @@ export const CounterButton: React.FC = ({ onClick, label, }) => ( - ); diff --git a/examples/react-component-bundle-false/src/env.d.ts b/examples/react-component-bundle-false/src/env.d.ts index 0506fbcb4..8fa0ce11c 100644 --- a/examples/react-component-bundle-false/src/env.d.ts +++ b/examples/react-component-bundle-false/src/env.d.ts @@ -2,3 +2,8 @@ declare module '*.module.scss' { const classes: { [key: string]: string }; export default classes; } + +declare module '*.svg' { + const url: string; + export default url; +} diff --git a/examples/react-component-bundle/rslib.config.ts b/examples/react-component-bundle/rslib.config.ts index c00c22eb5..59d42518d 100644 --- a/examples/react-component-bundle/rslib.config.ts +++ b/examples/react-component-bundle/rslib.config.ts @@ -25,7 +25,6 @@ export default defineConfig({ ], output: { target: 'web', - assetPrefix: 'auto', // TODO: move this line to packages/core/src/asset/assetConfig.ts }, plugins: [pluginReact(), pluginSass()], }); diff --git a/examples/react-component-bundle/src/components/CounterButton/index.tsx b/examples/react-component-bundle/src/components/CounterButton/index.tsx index be37643e0..2638731c6 100644 --- a/examples/react-component-bundle/src/components/CounterButton/index.tsx +++ b/examples/react-component-bundle/src/components/CounterButton/index.tsx @@ -1,4 +1,5 @@ import type React from 'react'; +import logo from '../../assets/logo.svg'; import styles from './index.module.scss'; interface CounterButtonProps { @@ -10,7 +11,12 @@ export const CounterButton: React.FC = ({ onClick, label, }) => ( - ); diff --git a/examples/react-component-bundle/src/env.d.ts b/examples/react-component-bundle/src/env.d.ts index 0506fbcb4..8fa0ce11c 100644 --- a/examples/react-component-bundle/src/env.d.ts +++ b/examples/react-component-bundle/src/env.d.ts @@ -2,3 +2,8 @@ declare module '*.module.scss' { const classes: { [key: string]: string }; export default classes; } + +declare module '*.svg' { + const url: string; + export default url; +} diff --git a/packages/core/src/asset/LibSvgrPatchPlugin.ts b/packages/core/src/asset/LibSvgrPatchPlugin.ts new file mode 100644 index 000000000..c696338b5 --- /dev/null +++ b/packages/core/src/asset/LibSvgrPatchPlugin.ts @@ -0,0 +1,68 @@ +import { type Rspack, rspack } from '@rsbuild/core'; +import { getUndoPath } from '../css/utils'; + +const pluginName = 'LIB_SVGR_PATCH_PLUGIN'; + +export const PUBLIC_PATH_PLACEHOLDER = '__RSLIB_SVGR_AUTO_PUBLIC_PATH__'; + +export class LibSvgrPatchPlugin implements Rspack.RspackPluginInstance { + readonly name: string = pluginName; + apply(compiler: Rspack.Compiler): void { + compiler.hooks.make.tap(this.name, (compilation) => { + compilation.hooks.processAssets.tap(this.name, (assets) => { + const isEsm = Boolean(compilation.options.output.module); + const chunkAsset = Object.keys(assets).filter((name) => + /js$/.test(name), + ); + for (const name of chunkAsset) { + compilation.updateAsset(name, (old) => { + const oldSource = old.source().toString(); + const newSource = new rspack.sources.ReplaceSource(old); + + const pattern = new RegExp( + `\\(?['"]${PUBLIC_PATH_PLACEHOLDER}(.*)['"]\\)?`, + 'g', + ); + + const matches = [...oldSource.matchAll(pattern)]; + const len = matches.length; + if (len === 0) { + return old; + } + + const undoPath = getUndoPath( + name, + compilation.outputOptions.path!, + true, + ); + for (let i = 0; i < len; i++) { + const match = matches[i]!; + const filename = match[1]; + const requirePath = `${undoPath}${filename}`; + let replaced = ''; + if (isEsm) { + replaced = `__rslib_svgr_url__${i}__`; + } else { + replaced = `require("${requirePath}")`; + } + newSource.replace( + match.index, + match.index + match[0].length - 1, + replaced, + ); + + if (isEsm) { + newSource.insert( + 0, + `import __rslib_svgr_url__${i}__ from "${requirePath}";\n`, + ); + } + } + + return newSource; + }); + } + }); + }); + } +} diff --git a/packages/core/src/asset/assetConfig.ts b/packages/core/src/asset/assetConfig.ts index 81598e6ea..0c5cbf9a1 100644 --- a/packages/core/src/asset/assetConfig.ts +++ b/packages/core/src/asset/assetConfig.ts @@ -1,5 +1,146 @@ -import type { EnvironmentConfig } from '@rsbuild/core'; +import type { EnvironmentConfig, RsbuildPlugin } from '@rsbuild/core'; +import { CSS_EXTENSIONS_PATTERN } from '../constant'; import type { Format } from '../types'; +import { + LibSvgrPatchPlugin, + PUBLIC_PATH_PLACEHOLDER, +} from './LibSvgrPatchPlugin'; + +const PLUGIN_NAME = 'rsbuild:lib-asset'; + +const RSBUILD_SVGR_PLUGIN_NAME = 'rsbuild:svgr'; + +/** + * Be compatible to css-extract importModule and experimentalLibPreserveExports + * when set experimentalLibPreserveExports to true, the css-loader result can not executed in node side, so clone the assets rule + * 1. js assets: original rule set issuer and experimentalLibPreserveExports: true + * 2. css assets: a copy of original rule + */ +const pluginLibAsset = ({ bundle }: { bundle: boolean }): RsbuildPlugin => ({ + name: PLUGIN_NAME, + pre: [RSBUILD_SVGR_PLUGIN_NAME], + setup(api) { + api.modifyBundlerChain((config, { CHAIN_ID }) => { + // 1. modify svg rule first, svg is special because of svgr + const svgAssetRule = config.module.rules + .get(CHAIN_ID.RULE.SVG) + .oneOfs.get(CHAIN_ID.ONE_OF.SVG_ASSET); + const originalTypeOptions = svgAssetRule.get('type'); + const originalParserOptions = svgAssetRule.get('parser'); + const originalGeneratorOptions = svgAssetRule.get('generator'); + + const isUserSetPublicPath = config.output.get('publicPath') !== 'auto'; + + // if user sets publicPath, do not preserve asset import + const generatorOptions = isUserSetPublicPath + ? originalGeneratorOptions + : { + ...originalGeneratorOptions, + importMode: 'preserve', + }; + + const rule = config.module.rule(CHAIN_ID.RULE.SVG); + + rule.oneOf(CHAIN_ID.ONE_OF.SVG_ASSET).generator(generatorOptions).issuer({ + not: CSS_EXTENSIONS_PATTERN, + }); + + rule + .oneOf(`${CHAIN_ID.ONE_OF.SVG_ASSET}-for-css`) + .type(originalTypeOptions) + .parser(originalParserOptions) + .generator(originalGeneratorOptions) + .issuer(CSS_EXTENSIONS_PATTERN); + + // 2. modify other assets rules + const ruleIds = [ + CHAIN_ID.RULE.FONT, + CHAIN_ID.RULE.MEDIA, + CHAIN_ID.RULE.IMAGE, + CHAIN_ID.RULE.ADDITIONAL_ASSETS, + ]; + for (const ruleId of ruleIds) { + const oneOfId = `${ruleId}-asset`; + const assetRule = config.module.rules.get(ruleId); + if (!assetRule) { + continue; + } + const assetRuleOneOf = assetRule.oneOfs.get(oneOfId); + + const originalTypeOptions = assetRuleOneOf.get('type'); + const originalParserOptions = assetRuleOneOf.get('parser'); + const originalGeneratorOptions = assetRuleOneOf.get('generator'); + + const generatorOptions = isUserSetPublicPath + ? originalGeneratorOptions + : { + ...originalGeneratorOptions, + importMode: 'preserve', + }; + + const rule = config.module.rule(ruleId); + rule.oneOf(oneOfId).generator(generatorOptions).issuer({ + not: CSS_EXTENSIONS_PATTERN, + }); + + rule + .oneOf(`${oneOfId}-for-css`) + .type(originalTypeOptions) + .parser(originalParserOptions) + .generator(originalGeneratorOptions) + .issuer(CSS_EXTENSIONS_PATTERN); + } + + // for svgr + // 1. remove __webpack_require__.p in svgr url-loader and file-loader + const isUsingSvgr = Boolean( + config.module + .rule(CHAIN_ID.RULE.SVG) + .oneOf(CHAIN_ID.RULE.SVG) + .uses.has(CHAIN_ID.USE.SVGR), + ); + if (isUsingSvgr) { + const urlLoaderRule = config.module + .rule(CHAIN_ID.RULE.SVG) + .oneOf(CHAIN_ID.ONE_OF.SVG) + .use(CHAIN_ID.USE.URL); + + const originalOptions = urlLoaderRule.get('options'); + + urlLoaderRule.options({ + ...originalOptions, + publicPath: (url: string) => `${PUBLIC_PATH_PLACEHOLDER}${url}`, + }); + config.plugin(LibSvgrPatchPlugin.name).use(LibSvgrPatchPlugin, []); + } + // 2. in bundleless, only support transform the svg asset to mixedImport svgr file + // remove issuer to make every svg asset is transformed + if (!bundle) { + if (isUsingSvgr) { + const rule = config.module + .rule(CHAIN_ID.RULE.SVG) + .oneOf(CHAIN_ID.ONE_OF.SVG); + rule.issuer([]); + } + } + + // css-asset + // preserve './' in css url + // in bundleless, we set this in libCssExtractLoader + // in bundle, we set this by https://github.com/web-infra-dev/rspack/pull/8946 + if (bundle) { + config.plugins.get(CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT)?.tap((options) => { + return [ + { + ...options[0], + enforceRelative: true, + }, + ]; + }); + } + }); + }, +}); // TODO: asset config document export const composeAssetConfig = ( @@ -11,16 +152,17 @@ export const composeAssetConfig = ( return { output: { dataUriLimit: 0, // default: no inline asset - // assetPrefix: 'auto', // TODO: will turn on this with js support together in the future + assetPrefix: 'auto', }, + plugins: [pluginLibAsset({ bundle: true })], }; } - return { output: { dataUriLimit: 0, // default: no inline asset - // assetPrefix: 'auto', // TODO: will turn on this with js support together in the future + assetPrefix: 'auto', }, + plugins: [pluginLibAsset({ bundle: false })], }; } diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 2fe97babd..9e4282e7a 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -17,7 +17,7 @@ import { composeAssetConfig } from './asset/assetConfig'; import { DEFAULT_CONFIG_EXTENSIONS, DEFAULT_CONFIG_NAME, - ENTRY_EXTENSIONS_PATTERN, + DTS_EXTENSIONS_PATTERN, JS_EXTENSIONS_PATTERN, RSLIB_ENTRY_QUERY, SWC_HELPERS, @@ -990,9 +990,9 @@ const composeEntryConfig = async ( }); // Filter the glob resolved entry files based on the allowed extensions - const resolvedEntryFiles = globEntryFiles.filter((file) => - ENTRY_EXTENSIONS_PATTERN.test(file), - ); + const resolvedEntryFiles = globEntryFiles.filter((i) => { + return !DTS_EXTENSIONS_PATTERN.test(i); + }); if (resolvedEntryFiles.length === 0) { throw new Error(`Cannot find ${resolvedEntryFiles}`); @@ -1070,6 +1070,7 @@ 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; let resolver: RspackResolver | undefined; @@ -1143,6 +1144,7 @@ const composeBundlelessExternalConfig = ( styleRedirectPath, styleRedirectExtension, redirectPath, + issuer, ); if (cssExternal !== false) { @@ -1163,20 +1165,33 @@ const composeBundlelessExternalConfig = ( // If data.request already have an extension, we replace it with new extension // This may result in a change in semantics, // user should use copy to keep origin file or use another separate entry to deal this - if (jsRedirectExtension && resolvedRequest.startsWith('.')) { + if (resolvedRequest.startsWith('.')) { const ext = extname(resolvedRequest); + if (ext) { + // 1. js files hit JS_EXTENSIONS_PATTERN, ./foo.ts -> ./foo.mjs if (JS_EXTENSIONS_PATTERN.test(resolvedRequest)) { - resolvedRequest = resolvedRequest.replace( - /\.[^.]+$/, - jsExtension, - ); + if (jsRedirectExtension) { + resolvedRequest = resolvedRequest.replace( + /\.[^.]+$/, + jsExtension, + ); + } } else { - // If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png - return callback(); + // 2. asset files, does not match jsExtensionsPattern, eg: ./foo.png -> ./foo.mjs + // non-js && non-css files + if (assetRedirect) { + resolvedRequest = resolvedRequest.replace( + /\.[^.]+$/, + jsExtension, + ); + } } } else { - resolvedRequest = `${resolvedRequest}${jsExtension}`; + // 1. js files hit JS_EXTENSIONS_PATTERN,./foo ->./foo.mjs + if (jsRedirectExtension) { + resolvedRequest = `${resolvedRequest}${jsExtension}`; + } } } diff --git a/packages/core/src/constant.ts b/packages/core/src/constant.ts index 85913bd83..a05f947bf 100644 --- a/packages/core/src/constant.ts +++ b/packages/core/src/constant.ts @@ -16,6 +16,8 @@ export const SHEBANG_REGEX: RegExp = /#!.*[\s\n\r]*$/; export const REACT_DIRECTIVE_REGEX: RegExp = /^['"]use (client|server)['"](;?)[\s\n\r]*$/; +const DTS_EXTENSIONS: string[] = ['d.ts', 'd.mts', 'd.cts']; + const JS_EXTENSIONS: string[] = [ 'js', 'mjs', @@ -33,11 +35,6 @@ const JS_EXTENSIONS: string[] = [ const CSS_EXTENSIONS: string[] = ['css', 'sass', 'scss', 'less'] as const; -const ENTRY_EXTENSIONS: string[] = [ - ...JS_EXTENSIONS, - ...CSS_EXTENSIONS, -] as const; - export const JS_EXTENSIONS_PATTERN: RegExp = new RegExp( `\\.(${JS_EXTENSIONS.join('|')})$`, ); @@ -46,6 +43,6 @@ export const CSS_EXTENSIONS_PATTERN: RegExp = new RegExp( `\\.(${CSS_EXTENSIONS.join('|')})$`, ); -export const ENTRY_EXTENSIONS_PATTERN: RegExp = new RegExp( - `\\.(${ENTRY_EXTENSIONS.join('|')})$`, +export const DTS_EXTENSIONS_PATTERN: RegExp = new RegExp( + `\\.(${DTS_EXTENSIONS.join('|')})$`, ); diff --git a/packages/core/src/css/LibCssExtractPlugin.ts b/packages/core/src/css/LibCssExtractPlugin.ts index d4588c63b..077d06db5 100644 --- a/packages/core/src/css/LibCssExtractPlugin.ts +++ b/packages/core/src/css/LibCssExtractPlugin.ts @@ -3,6 +3,7 @@ import { RSLIB_CSS_ENTRY_FLAG } from './cssConfig'; import { ABSOLUTE_PUBLIC_PATH, AUTO_PUBLIC_PATH, + BASE_URI, SINGLE_DOT_PATH_SEGMENT, } from './libCssExtractLoader'; import { getUndoPath } from './utils'; @@ -62,14 +63,17 @@ class LibCssExtractPlugin implements Rspack.RspackPluginInstance { } } - replace(ABSOLUTE_PUBLIC_PATH, ''); replace(SINGLE_DOT_PATH_SEGMENT, '.'); const undoPath = getUndoPath( name, compilation.outputOptions.path!, - false, + // preserve './' in css url + // https://github.com/web-infra-dev/rspack/pull/8946 + true, ); - replace(AUTO_PUBLIC_PATH, undoPath); + replace(`${ABSOLUTE_PUBLIC_PATH}${AUTO_PUBLIC_PATH}`, undoPath); + replace(ABSOLUTE_PUBLIC_PATH, ''); + replace(`${BASE_URI}/`, ''); return replaceSource; }); diff --git a/packages/core/src/css/cssConfig.ts b/packages/core/src/css/cssConfig.ts index f5a93aab7..7f057d7dd 100644 --- a/packages/core/src/css/cssConfig.ts +++ b/packages/core/src/css/cssConfig.ts @@ -87,9 +87,10 @@ export async function cssExternalHandler( styleRedirectPath: boolean, styleRedirectExtension: boolean, redirectPath: (request: string) => Promise, + issuer: string, ): Promise { - // cssExtract would execute the file handled by css-loader, so we cannot external the "helper import" from css-loader - // do not external @rsbuild/core/compiled/css-loader/noSourceMaps.js, sourceMaps.js, api.mjs etc. + // cssExtract: do not external @rsbuild/core/compiled/css-loader/noSourceMaps.js, sourceMaps.js, api.mjs etc. + // cssExtract would execute the result handled by css-loader with importModule, so we cannot external the "helper import" from css-loader if (/compiled\/css-loader\//.test(request)) { return callback(); } @@ -104,6 +105,10 @@ export async function cssExternalHandler( } if (!isCssFile(resolvedRequest)) { + // cssExtract: do not external assets module import + if (isCssFile(issuer)) { + return callback(); + } return false; } diff --git a/packages/core/src/types/config.ts b/packages/core/src/types/config.ts index 96e14f4d6..f183458d8 100644 --- a/packages/core/src/types/config.ts +++ b/packages/core/src/types/config.ts @@ -188,8 +188,8 @@ export type Redirect = { js?: JsRedirect; /** Controls the redirect of the import paths of output style files. */ style?: StyleRedirect; - // TODO: support other redirects - // asset?: boolean; + /** Controls the redirect of the import paths of output asset files. */ + asset?: boolean; // dts?: DtsRedirect; }; diff --git a/packages/core/tests/__snapshots__/config.test.ts.snap b/packages/core/tests/__snapshots__/config.test.ts.snap index 19dae61ba..5b9221534 100644 --- a/packages/core/tests/__snapshots__/config.test.ts.snap +++ b/packages/core/tests/__snapshots__/config.test.ts.snap @@ -8,6 +8,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "progressBar": false, }, "output": { + "assetPrefix": "auto", "dataUriLimit": 0, "distPath": { "css": "./", @@ -113,6 +114,13 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "name": "rsbuild:format", "setup": [Function], }, + { + "name": "rsbuild:lib-asset", + "pre": [ + "rsbuild:svgr", + ], + "setup": [Function], + }, { "name": "rsbuild:lib-entry-chunk", "setup": [Function], @@ -252,6 +260,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "progressBar": false, }, "output": { + "assetPrefix": "auto", "dataUriLimit": 0, "distPath": { "css": "./", @@ -356,6 +365,13 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "name": "rsbuild:cjs-import-meta-url-shim", "setup": [Function], }, + { + "name": "rsbuild:lib-asset", + "pre": [ + "rsbuild:svgr", + ], + "setup": [Function], + }, { "name": "rsbuild:lib-entry-chunk", "setup": [Function], diff --git a/packages/core/tests/constant.test.ts b/packages/core/tests/constant.test.ts index 5522c2ce6..d9fdf4ee7 100644 --- a/packages/core/tests/constant.test.ts +++ b/packages/core/tests/constant.test.ts @@ -1,7 +1,7 @@ import { expect, test } from 'vitest'; import { CSS_EXTENSIONS_PATTERN, - ENTRY_EXTENSIONS_PATTERN, + DTS_EXTENSIONS_PATTERN, JS_EXTENSIONS_PATTERN, } from '../src/constant'; @@ -27,6 +27,17 @@ const cssTestStrings = [ { str: '/Users/path/index.sass', expected: true }, ]; +const dtsTestStrings = [ + { str: 'index.js', expected: false }, + { str: './index.ts', expected: false }, + { str: './index.d.ts', expected: true }, + { str: '/Users/path/index.ts', expected: false }, + { str: '/Users/path/index.d.ts', expected: true }, + { str: '/Users/path/index.d.mts', expected: true }, + { str: '/Users/path/index.d.cts', expected: true }, + { str: '/Users/path/index.tsx', expected: false }, +]; + test('JS_EXTENSIONS_PATTERN', () => { for (const { str, expected } of jsTestStrings) { expect(JS_EXTENSIONS_PATTERN.test(str)).toBe(expected); @@ -39,8 +50,8 @@ test('CSS_EXTENSIONS_PATTERN', () => { } }); -test('ENTRY_EXTENSIONS_PATTERN', () => { - for (const { str, expected } of [...jsTestStrings, ...cssTestStrings]) { - expect(ENTRY_EXTENSIONS_PATTERN.test(str)).toBe(expected); +test('DTS_EXTENSIONS_PATTERN', () => { + for (const { str, expected } of dtsTestStrings) { + expect(DTS_EXTENSIONS_PATTERN.test(str)).toBe(expected); } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3206fb6be..f4e26527a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -459,9 +459,9 @@ importers: tests/integration/alias: {} - tests/integration/asset/limit: {} + tests/integration/asset/assets-include: {} - tests/integration/asset/name: + tests/integration/asset/hash: dependencies: react: specifier: ^19.0.0 @@ -471,10 +471,16 @@ importers: specifier: ^1.1.0 version: 1.1.0(@rsbuild/core@1.2.0) + tests/integration/asset/json: {} + + tests/integration/asset/limit: {} + tests/integration/asset/path: {} tests/integration/asset/public-path: {} + tests/integration/asset/source: {} + tests/integration/asset/svgr: devDependencies: '@rsbuild/plugin-react': @@ -757,6 +763,8 @@ importers: specifier: ^0.11.0 version: 0.11.0(@babel/core@7.26.0) + tests/integration/redirect/asset: {} + tests/integration/redirect/js: devDependencies: '@types/lodash': @@ -808,6 +816,8 @@ importers: tests/integration/sourcemap/inline: {} + tests/integration/style/asset: {} + tests/integration/style/css-modules/bundle: {} tests/integration/style/css-modules/bundle-false: {} diff --git a/tests/e2e/react-component/index.pw.test.ts b/tests/e2e/react-component/index.pw.test.ts index ff618c545..ad9f8ad05 100644 --- a/tests/e2e/react-component/index.pw.test.ts +++ b/tests/e2e/react-component/index.pw.test.ts @@ -40,7 +40,15 @@ async function assetShouldWork(page: Page) { assert(h1El); expect(h1El).toHaveCSS('background', /static\/svg\/logo/); - // TODO: asset in js + // asset by import url from './assets/logo.svg' + const imgEls = await page.$$('.counter-button>img'); + expect(imgEls).toHaveLength(2); + const srcList = await Promise.all( + imgEls.map((imgEl) => imgEl.getAttribute('src')), + ); + for (const src of srcList) { + expect(src).toMatch(/static\/svg\/logo/); + } } test('should render example "react-component-bundle" successfully', async ({ diff --git a/tests/integration/asset/__snapshots__/index.test.ts.snap b/tests/integration/asset/__snapshots__/index.test.ts.snap index 50129058c..4193dd100 100644 --- a/tests/integration/asset/__snapshots__/index.test.ts.snap +++ b/tests/integration/asset/__snapshots__/index.test.ts.snap @@ -1,7 +1,45 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`use asset/source 1`] = ` +"const draft_namespaceObject = "this is a txt file\\nthis is a txt file\\n"; +console.log(draft_namespaceObject); +" +`; + +exports[`use asset/source 2`] = ` +"const draft_rslib_entry_namespaceObject = "this is a txt file\\nthis is a txt file\\n"; +export { draft_rslib_entry_namespaceObject as default }; +" +`; + +exports[`use json 1`] = ` +"JSON.parse('{"foo":1,"bar":["2"]}'); +" +`; + +exports[`use json 2`] = ` +"var data_rslib_entry_namespaceObject = JSON.parse('{"foo":1,"bar":["2"]}'); +var __webpack_exports__bar = data_rslib_entry_namespaceObject.bar; +var __webpack_exports__foo = data_rslib_entry_namespaceObject.foo; +export { __webpack_exports__bar as bar, __webpack_exports__foo as foo }; +" +`; + +exports[`use source.assetInclude 1`] = ` +"import draft_namespaceObject from "./static/assets/draft.txt"; +console.log(draft_namespaceObject); +" +`; + +exports[`use source.assetInclude 2`] = ` +"import draft_rslib_entry_namespaceObject from "../static/assets/draft.txt"; +export { draft_rslib_entry_namespaceObject as default }; +" +`; + exports[`use svgr 1`] = ` -"import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; +"import __rslib_svgr_url__0__ from "./static/svg/logo.svg"; +import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; import "react"; const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", @@ -25,14 +63,8 @@ const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_r }) }); const logoreact = SvgLogo; -console.log(logoreact); -" -`; - -exports[`use svgr 2`] = ` -"import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; -import "react"; -const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("svg", { +console.log('queryImport', 'ReactComponent', logoreact); +const logo_SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 841.9 595.3", ...props, @@ -53,63 +85,106 @@ const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_r ] }) }); -const logoreact = SvgLogo; -console.log(logoreact); +const logo = __rslib_svgr_url__0__; +console.log('namedImport', 'ReactComponent', logo_SvgLogo); +console.log('default Import', 'reexport', logo); " `; -exports[`use svgr 3`] = ` -"import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; -import "react"; -const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("svg", { +exports[`use svgr 2`] = ` +""use strict"; +var __webpack_exports__ = {}; +const jsx_runtime_namespaceObject = require("react/jsx-runtime"); +require("react"); +const SvgLogo = (props)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 841.9 595.3", ...props, - children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("g", { + children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("g", { fill: "#61DAFB", children: [ - /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("path", { + /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("path", { d: "M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3m-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9m-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9m32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1M421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32m-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24q7.05 12 14.4 23.4M420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32m-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9m-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6s22.9-35.6 58.3-50.6c8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2M310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7m237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1m38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6M320.8 78.4" }), - /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("circle", { + /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("circle", { cx: 420.9, cy: 296.5, r: 45.7 }), - /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("path", { + /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("path", { d: "M520.5 78.1" }) ] }) }); -console.log(SvgLogo); -" -`; - -exports[`use svgr 4`] = ` -"import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; -import "react"; -const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("svg", { +const logoreact = SvgLogo; +console.log('queryImport', 'ReactComponent', logoreact); +const logo_SvgLogo = (props)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 841.9 595.3", ...props, - children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("g", { + children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("g", { fill: "#61DAFB", children: [ - /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("path", { + /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("path", { d: "M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3m-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9m-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9m32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1M421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32m-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24q7.05 12 14.4 23.4M420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32m-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9m-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6s22.9-35.6 58.3-50.6c8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2M310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7m237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1m38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6M320.8 78.4" }), - /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("circle", { + /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("circle", { cx: 420.9, cy: 296.5, r: 45.7 }), - /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("path", { + /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("path", { d: "M520.5 78.1" }) ] }) }); -console.log(SvgLogo); +const logo = require("./static/svg/logo.svg"); +console.log('namedImport', 'ReactComponent', logo_SvgLogo); +console.log('default Import', 'reexport', logo); +var __webpack_export_target__ = exports; +for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; +if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true +}); +" +`; + +exports[`use svgr 3`] = ` +"import * as __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__ from "./assets/logo.js"; +console.log('namedImport', 'ReactComponent', __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__.ReactComponent); +" +`; + +exports[`use svgr 4`] = ` +"import * as __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__ from "./assets/logo.js"; +console.log('namedImport', 'ReactComponent', __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__.ReactComponent); +" +`; + +exports[`use svgr 5`] = ` +""use strict"; +var __webpack_exports__ = {}; +const logo_cjs_namespaceObject = require("./assets/logo.cjs"); +console.log('namedImport', 'ReactComponent', logo_cjs_namespaceObject.ReactComponent); +var __webpack_export_target__ = exports; +for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; +if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true +}); +" +`; + +exports[`use svgr 6`] = ` +""use strict"; +var __webpack_exports__ = {}; +const logo_cjs_namespaceObject = require("./assets/logo.cjs"); +console.log('namedImport', 'ReactComponent', logo_cjs_namespaceObject.ReactComponent); +var __webpack_export_target__ = exports; +for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; +if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true +}); " `; diff --git a/tests/integration/asset/assets-include/package.json b/tests/integration/asset/assets-include/package.json new file mode 100644 index 000000000..2fab8c41c --- /dev/null +++ b/tests/integration/asset/assets-include/package.json @@ -0,0 +1,6 @@ +{ + "name": "assets-include-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/asset/assets-include/rslib.config.ts b/tests/integration/asset/assets-include/rslib.config.ts new file mode 100644 index 000000000..4589547e3 --- /dev/null +++ b/tests/integration/asset/assets-include/rslib.config.ts @@ -0,0 +1,35 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + // 0. bundle + // esm + generateBundleEsmConfig({ + source: { + assetsInclude: /\.txt/, + }, + output: { + distPath: { + root: './dist/esm/bundle', + }, + }, + }), + // 1. bundleless + // esm + generateBundleEsmConfig({ + bundle: false, + source: { + assetsInclude: /\.txt/, + }, + output: { + distPath: { + root: './dist/esm/bundleless', + }, + }, + }), + ], + output: { + target: 'web', + }, +}); diff --git a/tests/integration/asset/assets-include/src/assets/draft.txt b/tests/integration/asset/assets-include/src/assets/draft.txt new file mode 100644 index 000000000..2b740b0ec --- /dev/null +++ b/tests/integration/asset/assets-include/src/assets/draft.txt @@ -0,0 +1,2 @@ +this is a txt file +this is a txt file diff --git a/tests/integration/asset/assets-include/src/index.js b/tests/integration/asset/assets-include/src/index.js new file mode 100644 index 000000000..2c066948e --- /dev/null +++ b/tests/integration/asset/assets-include/src/index.js @@ -0,0 +1,3 @@ +import draft from './assets/draft.txt'; + +console.log(draft); diff --git a/tests/integration/asset/name/package.json b/tests/integration/asset/hash/package.json similarity index 100% rename from tests/integration/asset/name/package.json rename to tests/integration/asset/hash/package.json diff --git a/tests/integration/asset/hash/rslib.config.ts b/tests/integration/asset/hash/rslib.config.ts new file mode 100644 index 000000000..eaeee0e26 --- /dev/null +++ b/tests/integration/asset/hash/rslib.config.ts @@ -0,0 +1,60 @@ +import { pluginReact } from '@rsbuild/plugin-react'; +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + // 0. bundle default + // esm + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/bundle', + }, + filename: { + image: '[name].[contenthash:8][ext]', + }, + }, + }), + // cjs + generateBundleCjsConfig({ + output: { + distPath: { + root: './dist/cjs/bundle', + }, + filename: { + image: '[name].[contenthash:8][ext]', + }, + }, + }), + // 1. bundleless default + // esm + generateBundleEsmConfig({ + bundle: false, + output: { + distPath: { + root: './dist/esm/bundleless', + }, + filename: { + image: '[name].[contenthash:8][ext]', + }, + }, + }), + // cjs + generateBundleCjsConfig({ + bundle: false, + output: { + distPath: { + root: './dist/cjs/bundleless', + }, + filename: { + image: '[name].[contenthash:8][ext]', + }, + }, + }), + ], + output: { + target: 'web', + }, + plugins: [pluginReact()], +}); diff --git a/tests/integration/asset/name/assets/image.png b/tests/integration/asset/hash/src/assets/image.png similarity index 100% rename from tests/integration/asset/name/assets/image.png rename to tests/integration/asset/hash/src/assets/image.png diff --git a/tests/integration/asset/name/src/index.jsx b/tests/integration/asset/hash/src/index.jsx similarity index 61% rename from tests/integration/asset/name/src/index.jsx rename to tests/integration/asset/hash/src/index.jsx index d78a7a874..7caafe9ab 100644 --- a/tests/integration/asset/name/src/index.jsx +++ b/tests/integration/asset/hash/src/index.jsx @@ -1,4 +1,4 @@ -import a from '../assets/image.png'; +import a from './assets/image.png'; export default () => { return ; diff --git a/tests/integration/asset/index.test.ts b/tests/integration/asset/index.test.ts index 6bc313b2b..0f0b87298 100644 --- a/tests/integration/asset/index.test.ts +++ b/tests/integration/asset/index.test.ts @@ -1,90 +1,338 @@ import { join } from 'node:path'; -import { buildAndGetResults } from 'test-helper'; +import { buildAndGetResults, queryContent } from 'test-helper'; import { expect, test } from 'vitest'; test('set the size threshold to inline static assets', async () => { const fixturePath = join(__dirname, 'limit'); const { contents } = await buildAndGetResults({ fixturePath }); - // inline when bundle - expect(Object.values(contents.esm0!)[0]).toContain( - 'const logo_namespaceObject = "data:image/svg+xml;base64', + // 0. bundle default + // esm + const { content: indexJs0 } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs0).toContain( + 'import logo_namespaceObject from "./static/svg/logo.svg";', ); - - // external when bundle - expect(Object.values(contents.esm1!)[0]).toContain( - 'const logo_namespaceObject = __webpack_require__.p + "static/svg/logo.svg";', + // cjs + const { content: indexCjs0 } = queryContent(contents.cjs0!, /index\.cjs/); + expect(indexCjs0).toContain( + 'const logo_namespaceObject = require("./static/svg/logo.svg");', ); - // inline bundleless - expect(Object.values(contents.esm2!)[0]).toContain( + // 1. bundle inline + // esm + const { content: indexJs1 } = queryContent(contents.esm1!, /index\.js/); + expect(indexJs1).toContain( + 'const logo_namespaceObject = "data:image/svg+xml;base64', + ); + // cjs + const { content: indexCjs1 } = queryContent(contents.cjs1!, /index\.cjs/); + expect(indexCjs1).toContain( 'const logo_namespaceObject = "data:image/svg+xml;base64', ); - // external bundleless - expect(Object.values(contents.esm3!)[0]).toContain( - 'const logo_namespaceObject = __webpack_require__.p + "static/svg/logo.svg";', + // 2. bundleless esm default + // esm + const { content: indexJs2 } = queryContent(contents.esm2!, /index\.js/); + const { content: logoJs2 } = queryContent(contents.esm2!, /assets\/logo\.js/); + expect(indexJs2).toMatchInlineSnapshot(` + "import * as __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__ from "./assets/logo.js"; + const src_rslib_entry_ = __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__["default"]; + export { src_rslib_entry_ as default }; + " + `); + expect(logoJs2).toMatchInlineSnapshot(` + "import logo_rslib_entry_namespaceObject from "../static/svg/logo.svg"; + export { logo_rslib_entry_namespaceObject as default }; + " + `); + // cjs + const { content: indexCjs2 } = queryContent(contents.cjs2!, /index\.cjs/); + const { content: logoCjs2 } = queryContent( + contents.cjs2!, + /assets\/logo\.cjs/, ); + expect(indexCjs2).toContain( + 'const logo_cjs_namespaceObject = require("./assets/logo.cjs");', + ); + expect(logoCjs2).toMatchInlineSnapshot(` + ""use strict"; + var __webpack_modules__ = { + "./src/assets/logo.svg?__rslib_entry__": function(module) { + module.exports = require("../static/svg/logo.svg"); + } + }; + var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + var __webpack_exports__ = __webpack_require__("./src/assets/logo.svg?__rslib_entry__"); + var __webpack_export_target__ = exports; + for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; + if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true + }); + " + `); }); -test('set the assets name', async () => { - const fixturePath = join(__dirname, 'name'); +test('set the assets filename with hash', async () => { + const fixturePath = join(__dirname, 'hash'); const { contents } = await buildAndGetResults({ fixturePath }); - - // bundle - expect(Object.values(contents.esm0!)[0]).toContain( - 'const image_namespaceObject = __webpack_require__.p + "static/image/image.c74653c1.png";', + // 0. bundle default + // esm + const { content: indexJs0 } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs0).toContain( + 'import image_namespaceObject from "./static/image/image.c74653c1.png";', + ); + // cjs + const { content: indexCjs0 } = queryContent(contents.cjs0!, /index\.cjs/); + expect(indexCjs0).toContain( + 'const image_namespaceObject = require("./static/image/image.c74653c1.png");', ); - // bundleless - expect(Object.values(contents.esm1!)[0]).toContain( - 'const image_namespaceObject = __webpack_require__.p + "static/image/image.c74653c1712618b1.png";', + // 1. bundleless default + // esm + const { content: imageJs1 } = queryContent( + contents.esm1!, + /assets\/image\.js/, ); + expect(imageJs1).toMatchInlineSnapshot(` + "import image_rslib_entry_namespaceObject from "../static/image/image.c74653c1.png"; + export { image_rslib_entry_namespaceObject as default }; + " + `); + // cjs + const { content: imageCjs1 } = queryContent( + contents.cjs1!, + /assets\/image\.cjs/, + ); + expect(imageCjs1).toMatchInlineSnapshot(` + ""use strict"; + var __webpack_modules__ = { + "./src/assets/image.png?__rslib_entry__": function(module) { + module.exports = require("../static/image/image.c74653c1.png"); + } + }; + var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + var __webpack_exports__ = __webpack_require__("./src/assets/image.png?__rslib_entry__"); + var __webpack_export_target__ = exports; + for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; + if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true + }); + " + `); }); test('set the assets output path', async () => { const fixturePath = join(__dirname, 'path'); const { contents } = await buildAndGetResults({ fixturePath }); - - // bundle - expect(Object.values(contents.esm0!)[0]).toContain( - 'const image_namespaceObject = __webpack_require__.p + "assets/bundle/image.png";', + // 0. bundle default + // esm + const { content: indexJs0 } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs0).toContain( + 'import image_namespaceObject from "./assets/bundle/image.png";', + ); + // cjs + const { content: indexCjs0 } = queryContent(contents.cjs0!, /index\.cjs/); + expect(indexCjs0).toContain( + 'const image_namespaceObject = require("./assets/bundle/image.png");', ); - // bundleless - expect(Object.values(contents.esm1!)[0]).toContain( - 'const image_namespaceObject = __webpack_require__.p + "assets/bundleless/image.png";', + // 1. bundleless default + // esm + const { content: imageJs1 } = queryContent( + contents.esm1!, + /assets\/image\.js/, + ); + expect(imageJs1).toMatchInlineSnapshot(` + "import image_rslib_entry_namespaceObject from "../assets/bundleless/image.png"; + export { image_rslib_entry_namespaceObject as default }; + " + `); + // cjs + const { content: imageCjs1 } = queryContent( + contents.cjs1!, + /assets\/image\.cjs/, ); + expect(imageCjs1).toMatchInlineSnapshot(` + ""use strict"; + var __webpack_modules__ = { + "./src/assets/image.png?__rslib_entry__": function(module) { + module.exports = require("../assets/bundleless/image.png"); + } + }; + var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + var __webpack_exports__ = __webpack_require__("./src/assets/image.png?__rslib_entry__"); + var __webpack_export_target__ = exports; + for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; + if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true + }); + " + `); }); test('set the assets public path', async () => { const fixturePath = join(__dirname, 'public-path'); const { contents } = await buildAndGetResults({ fixturePath }); - // bundle - expect(Object.values(contents.esm0!)[0]).toContain( - '__webpack_require__.p = "/public/path/bundle/";', + // 1. umd should preserve '__webpack_require__.p' + const { content: indexUmdJs } = queryContent(contents.umd!, /index\.js/); + + expect(indexUmdJs).toContain('__webpack_require__.p = "/public/path/";'); + expect(indexUmdJs).toContain( + 'const image_namespaceObject = __webpack_require__.p + "static/image/image.png";', ); - // bundleless - expect(Object.values(contents.esm1!)[0]).toContain( - '__webpack_require__.p = "/public/path/bundleless/";', + // 2. bundle + // esm + const { content: indexJs } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs).toMatchInlineSnapshot(` + "var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + (()=>{ + __webpack_require__.p = "/public/path/"; + })(); + const image_namespaceObject = __webpack_require__.p + "static/image/image.png"; + const src = image_namespaceObject; + export { src as default }; + " + `); + + // 3. bundleless + // esm + const { content: imageJs } = queryContent( + contents.esm1!, + /assets\/image\.js/, ); + expect(imageJs).toMatchInlineSnapshot(` + "var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + (()=>{ + __webpack_require__.p = "/public/path/"; + })(); + const image_rslib_entry_namespaceObject = __webpack_require__.p + "static/image/image.png"; + export { image_rslib_entry_namespaceObject as default }; + " + `); +}); + +test('use json', async () => { + const fixturePath = join(__dirname, 'json'); + const { contents } = await buildAndGetResults({ fixturePath }); + + // 0. bundle + // esm + const { content: indexJs } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs).matchSnapshot(); + // 1. bundleless + // esm + const { content: dataJs } = queryContent(contents.esm1!, /assets\/data\.js/); + expect(dataJs).matchSnapshot(); }); test('use svgr', async () => { const fixturePath = join(__dirname, 'svgr'); const { contents } = await buildAndGetResults({ fixturePath }); - // bundle -- default export with react query - expect(Object.values(contents.esm0!)[0]).toMatchSnapshot(); + // 0. bundle + // esm + const { content: indexJs } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs).matchSnapshot(); + // cjs + const { content: indexCjs } = queryContent(contents.cjs0!, /index\.cjs/); + expect(indexCjs).matchSnapshot(); - // bundleless -- default export with react query - expect(Object.values(contents.esm1!)[0]).toMatchSnapshot(); + // 1. bundleless + // esm + const { content: namedImportJs } = queryContent( + contents.esm1!, + /namedImport\.js/, + ); + expect(namedImportJs).toMatchSnapshot(); + const { content: defaultImportJs } = queryContent( + contents.esm1!, + /namedImport\.js/, + ); + expect(defaultImportJs).toMatchSnapshot(); + // cjs + const { content: namedImportCjs } = queryContent( + contents.cjs1!, + /namedImport\.cjs/, + ); + expect(namedImportCjs).toMatchSnapshot(); + const { content: defaultImportCjs } = queryContent( + contents.cjs1!, + /namedImport\.cjs/, + ); + expect(defaultImportCjs).toMatchSnapshot(); +}); + +test('use asset/source', async () => { + const fixturePath = join(__dirname, 'source'); + const { contents } = await buildAndGetResults({ fixturePath }); - // bundle -- named export - expect(Object.values(contents.esm2!)[0]).toMatchSnapshot(); + // 0. bundle + // esm + const { content: indexJs } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs).matchSnapshot(); + // 1. bundleless + // esm + const { content: dataJs } = queryContent(contents.esm1!, /assets\/draft\.js/); + expect(dataJs).matchSnapshot(); +}); + +test('use source.assetInclude', async () => { + const fixturePath = join(__dirname, 'assets-include'); + const { contents } = await buildAndGetResults({ fixturePath }); - // bundleless -- named export - expect(Object.values(contents.esm3!)[0]).toMatchSnapshot(); + // 0. bundle + // esm + const { content: indexJs } = queryContent(contents.esm0!, /index\.js/); + expect(indexJs).matchSnapshot(); + // 1. bundleless + // esm + const { content: dataJs } = queryContent(contents.esm1!, /assets\/draft\.js/); + expect(dataJs).matchSnapshot(); }); diff --git a/tests/integration/asset/json/package.json b/tests/integration/asset/json/package.json new file mode 100644 index 000000000..55a5fb838 --- /dev/null +++ b/tests/integration/asset/json/package.json @@ -0,0 +1,6 @@ +{ + "name": "asset-types-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/asset/name/rslib.config.ts b/tests/integration/asset/json/rslib.config.ts similarity index 80% rename from tests/integration/asset/name/rslib.config.ts rename to tests/integration/asset/json/rslib.config.ts index 0a70ba02e..37cc90927 100644 --- a/tests/integration/asset/name/rslib.config.ts +++ b/tests/integration/asset/json/rslib.config.ts @@ -1,9 +1,10 @@ -import { pluginReact } from '@rsbuild/plugin-react'; import { defineConfig } from '@rslib/core'; import { generateBundleEsmConfig } from 'test-helper'; export default defineConfig({ lib: [ + // 0. bundle default + // esm generateBundleEsmConfig({ output: { distPath: { @@ -14,6 +15,9 @@ export default defineConfig({ }, }, }), + + // 1. bundleless default + // esm generateBundleEsmConfig({ bundle: false, output: { @@ -26,13 +30,7 @@ export default defineConfig({ }, }), ], - source: { - entry: { - index: './src/index.jsx', - }, - }, output: { target: 'web', }, - plugins: [pluginReact()], }); diff --git a/tests/integration/asset/json/src/assets/data.json b/tests/integration/asset/json/src/assets/data.json new file mode 100644 index 000000000..5743be16e --- /dev/null +++ b/tests/integration/asset/json/src/assets/data.json @@ -0,0 +1,4 @@ +{ + "foo": 1, + "bar": ["2"] +} diff --git a/tests/integration/asset/json/src/index.js b/tests/integration/asset/json/src/index.js new file mode 100644 index 000000000..c14c58d2a --- /dev/null +++ b/tests/integration/asset/json/src/index.js @@ -0,0 +1,3 @@ +import a from './assets/data.json'; + +a; diff --git a/tests/integration/asset/limit/rslib.config.ts b/tests/integration/asset/limit/rslib.config.ts index f472330cf..12aa98e32 100644 --- a/tests/integration/asset/limit/rslib.config.ts +++ b/tests/integration/asset/limit/rslib.config.ts @@ -1,50 +1,92 @@ import { defineConfig } from '@rslib/core'; -import { generateBundleEsmConfig } from 'test-helper'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; export default defineConfig({ lib: [ + // 0. bundle default + // esm generateBundleEsmConfig({ output: { distPath: { - root: './dist/esm/inline', + root: './dist/esm/bundle-default', + }, + }, + }), + // cjs + generateBundleCjsConfig({ + output: { + distPath: { + root: './dist/cjs/bundle-default', + }, + }, + }), + // 1. bundle inline + // esm + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/bundle-inline', + }, + dataUriLimit: { + svg: 4096, + }, + }, + }), + // cjs + generateBundleCjsConfig({ + output: { + distPath: { + root: './dist/cjs/bundle-inline', }, dataUriLimit: { svg: 4096, }, }, }), + // 2. bundleless default + // esm generateBundleEsmConfig({ + bundle: false, + output: { + distPath: { + root: './dist/esm/bundleless-default', + }, + }, + }), + // cjs + generateBundleCjsConfig({ + bundle: false, output: { distPath: { - root: './dist/esm/external', + root: './dist/cjs/bundleless-default', }, }, }), + + // 3. bundleless esm inline generateBundleEsmConfig({ bundle: false, output: { distPath: { - root: './dist/esm/inline-bundleless', + root: './dist/esm/bundleless-inline', }, dataUriLimit: { svg: 4096, }, }, }), - generateBundleEsmConfig({ + generateBundleCjsConfig({ bundle: false, output: { distPath: { - root: './dist/esm/external-bundleless', + root: './dist/cjs/bundleless-inline', + }, + dataUriLimit: { + svg: 4096, }, }, }), ], - source: { - entry: { - index: './src/index.js', - }, - }, output: { target: 'web', }, diff --git a/tests/integration/asset/public-path/assets/logo.svg b/tests/integration/asset/limit/src/assets/logo.svg similarity index 100% rename from tests/integration/asset/public-path/assets/logo.svg rename to tests/integration/asset/limit/src/assets/logo.svg diff --git a/tests/integration/asset/limit/src/index.js b/tests/integration/asset/limit/src/index.js index 76ad10053..3ec3a7c2a 100644 --- a/tests/integration/asset/limit/src/index.js +++ b/tests/integration/asset/limit/src/index.js @@ -1,3 +1,3 @@ -import logo from '../assets/logo.svg'; +import logo from './assets/logo.svg'; export default logo; diff --git a/tests/integration/asset/path/rslib.config.ts b/tests/integration/asset/path/rslib.config.ts index 5881be935..58585904c 100644 --- a/tests/integration/asset/path/rslib.config.ts +++ b/tests/integration/asset/path/rslib.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from '@rslib/core'; -import { generateBundleEsmConfig } from 'test-helper'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; export default defineConfig({ lib: [ + // 0. bundle + // esm generateBundleEsmConfig({ output: { distPath: { @@ -11,6 +13,16 @@ export default defineConfig({ }, }, }), + generateBundleCjsConfig({ + output: { + distPath: { + root: './dist/cjs/bundle', + image: 'assets/bundle', + }, + }, + }), + // 1. bundleless + // esm generateBundleEsmConfig({ bundle: false, output: { @@ -20,12 +32,16 @@ export default defineConfig({ }, }, }), + generateBundleCjsConfig({ + bundle: false, + output: { + distPath: { + root: './dist/cjs/bundleless', + image: 'assets/bundleless', + }, + }, + }), ], - source: { - entry: { - index: './src/index.js', - }, - }, output: { target: 'web', }, diff --git a/tests/integration/asset/path/assets/image.png b/tests/integration/asset/path/src/assets/image.png similarity index 100% rename from tests/integration/asset/path/assets/image.png rename to tests/integration/asset/path/src/assets/image.png diff --git a/tests/integration/asset/path/src/index.js b/tests/integration/asset/path/src/index.js index 206c64540..b6e29ee81 100644 --- a/tests/integration/asset/path/src/index.js +++ b/tests/integration/asset/path/src/index.js @@ -1,3 +1,3 @@ -import a from '../assets/image.png'; +import a from './assets/image.png'; export default a; diff --git a/tests/integration/asset/public-path/rslib.config.ts b/tests/integration/asset/public-path/rslib.config.ts index cb025d05a..41c16ccad 100644 --- a/tests/integration/asset/public-path/rslib.config.ts +++ b/tests/integration/asset/public-path/rslib.config.ts @@ -1,37 +1,31 @@ import { defineConfig } from '@rslib/core'; -import { generateBundleEsmConfig } from 'test-helper'; +import { generateBundleEsmConfig, generateBundleUmdConfig } from 'test-helper'; export default defineConfig({ lib: [ + generateBundleUmdConfig({ + output: { + assetPrefix: '/public/path', + }, + }), generateBundleEsmConfig({ output: { distPath: { root: './dist/esm/bundle', }, - assetPrefix: '/public/path/bundle', - dataUriLimit: { - svg: 0, - }, + assetPrefix: '/public/path', }, }), generateBundleEsmConfig({ bundle: false, output: { distPath: { - root: './dist/esm/bundleless', - }, - assetPrefix: '/public/path/bundleless', - dataUriLimit: { - svg: 0, + root: './dist/esm/bundle-false', }, + assetPrefix: '/public/path', }, }), ], - source: { - entry: { - index: './src/index.js', - }, - }, output: { target: 'web', }, diff --git a/tests/integration/bundle-false/asset/assets/image.png b/tests/integration/asset/public-path/src/assets/image.png similarity index 100% rename from tests/integration/bundle-false/asset/assets/image.png rename to tests/integration/asset/public-path/src/assets/image.png diff --git a/tests/integration/asset/public-path/src/index.js b/tests/integration/asset/public-path/src/index.js index 76ad10053..b17f08316 100644 --- a/tests/integration/asset/public-path/src/index.js +++ b/tests/integration/asset/public-path/src/index.js @@ -1,3 +1,3 @@ -import logo from '../assets/logo.svg'; +import logo from './assets/image.png'; export default logo; diff --git a/tests/integration/asset/source/package.json b/tests/integration/asset/source/package.json new file mode 100644 index 000000000..d141816b6 --- /dev/null +++ b/tests/integration/asset/source/package.json @@ -0,0 +1,6 @@ +{ + "name": "assets-source-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/asset/source/rslib.config.ts b/tests/integration/asset/source/rslib.config.ts new file mode 100644 index 000000000..67338f56c --- /dev/null +++ b/tests/integration/asset/source/rslib.config.ts @@ -0,0 +1,46 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +const assetSourceRules = [ + { + test: /\.txt$/i, + type: 'asset/source', + }, +]; + +export default defineConfig({ + lib: [ + // 0. bundle + // esm + generateBundleEsmConfig({ + tools: { + rspack(_config, { addRules }) { + addRules(assetSourceRules); + }, + }, + output: { + distPath: { + root: './dist/esm/bundle', + }, + }, + }), + // 1. bundleless + // esm + generateBundleEsmConfig({ + bundle: false, + tools: { + rspack(_config, { addRules }) { + addRules(assetSourceRules); + }, + }, + output: { + distPath: { + root: './dist/esm/bundleless', + }, + }, + }), + ], + output: { + target: 'web', + }, +}); diff --git a/tests/integration/asset/source/src/assets/draft.txt b/tests/integration/asset/source/src/assets/draft.txt new file mode 100644 index 000000000..2b740b0ec --- /dev/null +++ b/tests/integration/asset/source/src/assets/draft.txt @@ -0,0 +1,2 @@ +this is a txt file +this is a txt file diff --git a/tests/integration/asset/source/src/index.js b/tests/integration/asset/source/src/index.js new file mode 100644 index 000000000..2c066948e --- /dev/null +++ b/tests/integration/asset/source/src/index.js @@ -0,0 +1,3 @@ +import draft from './assets/draft.txt'; + +console.log(draft); diff --git a/tests/integration/asset/svgr/rslib.config.ts b/tests/integration/asset/svgr/rslib.config.ts index 6318ab07a..987972fca 100644 --- a/tests/integration/asset/svgr/rslib.config.ts +++ b/tests/integration/asset/svgr/rslib.config.ts @@ -1,73 +1,63 @@ import { pluginReact } from '@rsbuild/plugin-react'; import { pluginSvgr } from '@rsbuild/plugin-svgr'; import { defineConfig } from '@rslib/core'; -import { generateBundleEsmConfig } from 'test-helper'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; export default defineConfig({ lib: [ + // 0. bundle + // esm generateBundleEsmConfig({ - source: { - entry: { - index: './src/index.js', - }, - }, output: { distPath: { root: './dist/esm/bundle-default', }, }, - plugins: [pluginSvgr()], + plugins: [ + pluginSvgr({ + mixedImport: true, + }), + ], }), - generateBundleEsmConfig({ - source: { - entry: { - index: './src/index.js', - }, - }, - bundle: false, + // cjs + generateBundleCjsConfig({ output: { distPath: { - root: './dist/esm/bundleless-default', + root: './dist/cjs/bundle-default', }, }, - plugins: [pluginSvgr()], + plugins: [ + pluginSvgr({ + mixedImport: true, + }), + ], }), + // 1. bundleless + // esm generateBundleEsmConfig({ - source: { - entry: { - index: './src/namedExport.js', - }, - }, + bundle: false, output: { distPath: { - root: './dist/esm/bundle-named', + root: './dist/esm/bundleless-default', }, }, plugins: [ pluginSvgr({ - svgrOptions: { - exportType: 'named', - }, + mixedImport: true, }), ], }), - generateBundleEsmConfig({ - source: { - entry: { - index: './src/namedExport.js', - }, - }, + // cjs + generateBundleCjsConfig({ bundle: false, output: { distPath: { - root: './dist/esm/bundleless-named', + root: './dist/cjs/bundleless-default', }, }, plugins: [ pluginSvgr({ - svgrOptions: { - exportType: 'named', - }, + mixedImport: true, }), ], }), diff --git a/tests/integration/asset/svgr/assets/logo.svg b/tests/integration/asset/svgr/src/assets/logo.svg similarity index 100% rename from tests/integration/asset/svgr/assets/logo.svg rename to tests/integration/asset/svgr/src/assets/logo.svg diff --git a/tests/integration/asset/svgr/src/defaultImport.js b/tests/integration/asset/svgr/src/defaultImport.js new file mode 100644 index 000000000..57504f1b6 --- /dev/null +++ b/tests/integration/asset/svgr/src/defaultImport.js @@ -0,0 +1,3 @@ +import svgUrl from './assets/logo.svg'; + +console.log('default Import', 'reexport', svgUrl); diff --git a/tests/integration/asset/svgr/src/index.js b/tests/integration/asset/svgr/src/index.js index b6aaa208b..080d304bc 100644 --- a/tests/integration/asset/svgr/src/index.js +++ b/tests/integration/asset/svgr/src/index.js @@ -1,3 +1,3 @@ -import logo from '../assets/logo.svg?react'; - -console.log(logo); +import './queryImport'; +import './namedImport'; +import './defaultImport'; diff --git a/tests/integration/asset/svgr/src/namedExport.js b/tests/integration/asset/svgr/src/namedExport.js deleted file mode 100644 index 0241abbd6..000000000 --- a/tests/integration/asset/svgr/src/namedExport.js +++ /dev/null @@ -1,3 +0,0 @@ -import { ReactComponent } from '../assets/logo.svg'; - -console.log(ReactComponent); diff --git a/tests/integration/asset/svgr/src/namedImport.js b/tests/integration/asset/svgr/src/namedImport.js new file mode 100644 index 000000000..54540a206 --- /dev/null +++ b/tests/integration/asset/svgr/src/namedImport.js @@ -0,0 +1,3 @@ +import { ReactComponent } from './assets/logo.svg'; + +console.log('namedImport', 'ReactComponent', ReactComponent); diff --git a/tests/integration/asset/svgr/src/queryImport.js b/tests/integration/asset/svgr/src/queryImport.js new file mode 100644 index 000000000..3cdc85828 --- /dev/null +++ b/tests/integration/asset/svgr/src/queryImport.js @@ -0,0 +1,3 @@ +import ReactComponent from './assets/logo.svg?react'; + +console.log('queryImport', 'ReactComponent', ReactComponent); diff --git a/tests/integration/bundle-false/__snapshots__/index.test.ts.snap b/tests/integration/bundle-false/__snapshots__/index.test.ts.snap index b9b52178a..45b75db6e 100644 --- a/tests/integration/bundle-false/__snapshots__/index.test.ts.snap +++ b/tests/integration/bundle-false/__snapshots__/index.test.ts.snap @@ -1,7 +1,8 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`svgr in bundleless 1`] = ` -"import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; +"import __rslib_svgr_url__0__ from "../static/svg/logo.svg"; +import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime"; import "react"; const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", @@ -24,9 +25,8 @@ const SvgLogo = (props)=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_r ] }) }); -const logoreact = SvgLogo; -const src_rslib_entry_ = logoreact; -export { src_rslib_entry_ as default }; +const logo_rslib_entry_ = __rslib_svgr_url__0__; +export { SvgLogo as ReactComponent, logo_rslib_entry_ as default }; " `; @@ -59,7 +59,8 @@ var __webpack_require__ = {}; var __webpack_exports__ = {}; __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, { - default: ()=>src_rslib_entry_ + ReactComponent: ()=>SvgLogo, + default: ()=>logo_rslib_entry_ }); const jsx_runtime_namespaceObject = require("react/jsx-runtime"); require("react"); @@ -84,8 +85,7 @@ const SvgLogo = (props)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("svg ] }) }); -const logoreact = SvgLogo; -const src_rslib_entry_ = logoreact; +const logo_rslib_entry_ = require("../static/svg/logo.svg"); var __webpack_export_target__ = exports; for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { diff --git a/tests/integration/bundle-false/asset/rslib.config.ts b/tests/integration/bundle-false/asset/rslib.config.ts index 68e3b9d61..e9a72ab2e 100644 --- a/tests/integration/bundle-false/asset/rslib.config.ts +++ b/tests/integration/bundle-false/asset/rslib.config.ts @@ -10,15 +10,4 @@ export default defineConfig({ bundle: false, }), ], - // do not inline svg - output: { - dataUriLimit: { - svg: 0, - }, - }, - source: { - entry: { - index: ['./src/**'], - }, - }, }); diff --git a/tests/integration/bundle-false/asset/src/assets/image.png b/tests/integration/bundle-false/asset/src/assets/image.png new file mode 100644 index 000000000..a53ecc092 Binary files /dev/null and b/tests/integration/bundle-false/asset/src/assets/image.png differ diff --git a/tests/integration/bundle-false/asset/assets/logo.svg b/tests/integration/bundle-false/asset/src/assets/logo.svg similarity index 100% rename from tests/integration/bundle-false/asset/assets/logo.svg rename to tests/integration/bundle-false/asset/src/assets/logo.svg diff --git a/tests/integration/bundle-false/asset/src/index.js b/tests/integration/bundle-false/asset/src/index.js index 2e3671336..fcd03c098 100644 --- a/tests/integration/bundle-false/asset/src/index.js +++ b/tests/integration/bundle-false/asset/src/index.js @@ -1,5 +1,5 @@ -import image from '../assets/image.png'; -import logoURL from '../assets/logo.svg'; +import image from './assets/image.png'; +import logoURL from './assets/logo.svg'; console.log(logoURL); console.log(image); diff --git a/tests/integration/bundle-false/index.test.ts b/tests/integration/bundle-false/index.test.ts index c46d47115..0408da2de 100644 --- a/tests/integration/bundle-false/index.test.ts +++ b/tests/integration/bundle-false/index.test.ts @@ -147,15 +147,66 @@ test('asset in bundleless', async () => { const fixturePath = join(__dirname, 'asset'); const { contents } = await buildAndGetResults({ fixturePath }); - const assets = [ - 'const image_namespaceObject = __webpack_require__.p + "static/image/image.png";', - 'const logo_namespaceObject = __webpack_require__.p + "static/svg/logo.svg";', - ]; - - for (const asset of assets) { - expect(Object.values(contents.esm)[0]).toContain(asset); - expect(Object.values(contents.cjs)[0]).toContain(asset); - } + expect(Object.values(contents.esm)[0]).toMatchInlineSnapshot(` + "import image_rslib_entry_namespaceObject from "../static/image/image.png"; + export { image_rslib_entry_namespaceObject as default }; + " + `); + expect(Object.values(contents.esm)[1]).toMatchInlineSnapshot(` + "import logo_rslib_entry_namespaceObject from "../static/svg/logo.svg"; + export { logo_rslib_entry_namespaceObject as default }; + " + `); + expect(Object.values(contents.cjs)[0]).toMatchInlineSnapshot(` + ""use strict"; + var __webpack_modules__ = { + "./src/assets/image.png?__rslib_entry__": function(module) { + module.exports = require("../static/image/image.png"); + } + }; + var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + var __webpack_exports__ = __webpack_require__("./src/assets/image.png?__rslib_entry__"); + var __webpack_export_target__ = exports; + for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; + if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true + }); + " + `); + expect(Object.values(contents.cjs)[1]).toMatchInlineSnapshot(` + ""use strict"; + var __webpack_modules__ = { + "./src/assets/logo.svg?__rslib_entry__": function(module) { + module.exports = require("../static/svg/logo.svg"); + } + }; + var __webpack_module_cache__ = {}; + function __webpack_require__(moduleId) { + var cachedModule = __webpack_module_cache__[moduleId]; + if (void 0 !== cachedModule) return cachedModule.exports; + var module = __webpack_module_cache__[moduleId] = { + exports: {} + }; + __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + return module.exports; + } + var __webpack_exports__ = __webpack_require__("./src/assets/logo.svg?__rslib_entry__"); + var __webpack_export_target__ = exports; + for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__]; + if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { + value: true + }); + " + `); }); test('svgr in bundleless', async () => { diff --git a/tests/integration/bundle-false/svgr/rslib.config.ts b/tests/integration/bundle-false/svgr/rslib.config.ts index 17aa8a6e4..73f59c3f5 100644 --- a/tests/integration/bundle-false/svgr/rslib.config.ts +++ b/tests/integration/bundle-false/svgr/rslib.config.ts @@ -20,5 +20,5 @@ export default defineConfig({ output: { target: 'web', }, - plugins: [pluginReact(), pluginSvgr()], + plugins: [pluginReact(), pluginSvgr({ mixedImport: true })], }); diff --git a/tests/integration/bundle-false/svgr/assets/logo.svg b/tests/integration/bundle-false/svgr/src/assets/logo.svg similarity index 100% rename from tests/integration/bundle-false/svgr/assets/logo.svg rename to tests/integration/bundle-false/svgr/src/assets/logo.svg diff --git a/tests/integration/bundle-false/svgr/src/index.jsx b/tests/integration/bundle-false/svgr/src/index.jsx index bbb3d6502..6622ccb4f 100644 --- a/tests/integration/bundle-false/svgr/src/index.jsx +++ b/tests/integration/bundle-false/svgr/src/index.jsx @@ -1,3 +1,3 @@ -import Logo from '../assets/logo.svg?react'; +import Logo from './assets/logo.svg'; export default Logo; diff --git a/tests/integration/redirect/asset.test.ts b/tests/integration/redirect/asset.test.ts new file mode 100644 index 000000000..1a0974b54 --- /dev/null +++ b/tests/integration/redirect/asset.test.ts @@ -0,0 +1,32 @@ +import path from 'node:path'; +import { buildAndGetResults, queryContent } from 'test-helper'; +import { beforeAll, expect, test } from 'vitest'; + +let contents: Awaited>['contents']; + +beforeAll(async () => { + const fixturePath = path.resolve(__dirname, './asset'); + contents = (await buildAndGetResults({ fixturePath })).contents; +}); + +test('0. default', async () => { + const { content: indexJs } = queryContent(contents.esm0!, /index\.js/); + const { content: indexCjs } = queryContent(contents.cjs0!, /index\.cjs/); + expect(indexJs).toMatchInlineSnapshot(` + "import * as __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__ from "./assets/logo.js"; + __WEBPACK_EXTERNAL_MODULE__assets_logo_js_450929b7__["default"]; + " + `); + expect(indexCjs).toContain('require("./assets/logo.cjs")'); +}); + +test('1. redirect.asset = false', async () => { + const { content: indexJs } = queryContent(contents.esm1!, /index\.js/); + const { content: indexCjs } = queryContent(contents.cjs1!, /index\.cjs/); + expect(indexJs).toMatchInlineSnapshot(` + "import * as __WEBPACK_EXTERNAL_MODULE__assets_logo_svg_cfb3a4d9__ from "./assets/logo.svg"; + __WEBPACK_EXTERNAL_MODULE__assets_logo_svg_cfb3a4d9__["default"]; + " + `); + expect(indexCjs).toContain('require("./assets/logo.svg")'); +}); diff --git a/tests/integration/redirect/asset/package.json b/tests/integration/redirect/asset/package.json new file mode 100644 index 000000000..7557e719c --- /dev/null +++ b/tests/integration/redirect/asset/package.json @@ -0,0 +1,6 @@ +{ + "name": "asset-redirect-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/redirect/asset/rslib.config.ts b/tests/integration/redirect/asset/rslib.config.ts new file mode 100644 index 000000000..697e61607 --- /dev/null +++ b/tests/integration/redirect/asset/rslib.config.ts @@ -0,0 +1,46 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + // 0. default + generateBundleEsmConfig({ + bundle: false, + output: { + distPath: { + root: 'dist/default/esm', + }, + }, + }), + generateBundleCjsConfig({ + bundle: false, + output: { + distPath: { + root: 'dist/default/cjs', + }, + }, + }), + // 1. redirect.asset: false + generateBundleEsmConfig({ + bundle: false, + redirect: { asset: false }, + output: { + distPath: { + root: 'dist/asset-false/esm', + }, + }, + }), + generateBundleCjsConfig({ + bundle: false, + redirect: { asset: false }, + output: { + distPath: { + root: 'dist/asset-false/cjs', + }, + }, + }), + ], + output: { + target: 'web', + }, +}); diff --git a/tests/integration/redirect/asset/src/assets/logo.svg b/tests/integration/redirect/asset/src/assets/logo.svg new file mode 100644 index 000000000..6b60c1042 --- /dev/null +++ b/tests/integration/redirect/asset/src/assets/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/integration/redirect/asset/src/index.js b/tests/integration/redirect/asset/src/index.js new file mode 100644 index 000000000..96bf07c52 --- /dev/null +++ b/tests/integration/redirect/asset/src/index.js @@ -0,0 +1,3 @@ +import svg from './assets/logo.svg'; + +svg; diff --git a/tests/integration/style/asset/index.test.ts b/tests/integration/style/asset/index.test.ts new file mode 100644 index 000000000..9772c164e --- /dev/null +++ b/tests/integration/style/asset/index.test.ts @@ -0,0 +1,31 @@ +import { buildAndGetResults } from 'test-helper'; +import { expectFileContainContent } from 'test-helper/vitest'; +import { expect, test } from 'vitest'; + +test('should output css asset', async () => { + const fixturePath = __dirname; + const { contents } = await buildAndGetResults({ fixturePath, type: 'css' }); + const esmFiles = Object.keys(contents.esm); + expect(esmFiles).toMatchInlineSnapshot(` + [ + "/tests/integration/style/asset/dist/esm/index.css", + ] + `); + expectFileContainContent( + contents.esm, + 'index.css', + 'background: url(./static/svg/logo.svg)', + ); + + const cjsFiles = Object.keys(contents.cjs); + expect(cjsFiles).toMatchInlineSnapshot(` + [ + "/tests/integration/style/asset/dist/cjs/index.css", + ] + `); + expectFileContainContent( + contents.cjs, + 'index.css', + 'background: url(./static/svg/logo.svg)', + ); +}); diff --git a/tests/integration/style/asset/package.json b/tests/integration/style/asset/package.json new file mode 100644 index 000000000..0d291b36a --- /dev/null +++ b/tests/integration/style/asset/package.json @@ -0,0 +1,6 @@ +{ + "name": "style-asset-bundle-false-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/style/asset/rslib.config.ts b/tests/integration/style/asset/rslib.config.ts new file mode 100644 index 000000000..5cc8fdf27 --- /dev/null +++ b/tests/integration/style/asset/rslib.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ bundle: false }), + generateBundleCjsConfig({ bundle: false }), + ], + output: { + target: 'web', + }, +}); diff --git a/tests/integration/style/asset/src/assets/logo.svg b/tests/integration/style/asset/src/assets/logo.svg new file mode 100644 index 000000000..6b60c1042 --- /dev/null +++ b/tests/integration/style/asset/src/assets/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/integration/style/asset/src/index.css b/tests/integration/style/asset/src/index.css new file mode 100644 index 000000000..12d47743d --- /dev/null +++ b/tests/integration/style/asset/src/index.css @@ -0,0 +1,3 @@ +.foo { + background: url('./assets/logo.svg'); +} diff --git a/website/docs/en/config/lib/redirect.mdx b/website/docs/en/config/lib/redirect.mdx index ba18898e0..9f33aee3f 100644 --- a/website/docs/en/config/lib/redirect.mdx +++ b/website/docs/en/config/lib/redirect.mdx @@ -28,6 +28,7 @@ type StyleRedirect = { type Redirect = { js?: JsRedirect; style?: StyleRedirect; + asset?: boolean; }; ``` @@ -144,9 +145,9 @@ Whether to automatically redirect the file extension to import paths based on th - **Type:** `boolean` - **Default:** `true` -When set to `true`, When importing a normal style file, the path will be rewritten to the `.css` file. When importing [CSS Modules](/config/rsbuild/output#outputcssmodules), the path will be rewritten to the corresponding JavaScript output file. +When set to `true`, the file extension of importing a normal style file will be rewritten to `.css`. When importing [CSS Modules](/config/rsbuild/output#outputcssmodules), the path will be rewritten to the corresponding JavaScript output file. -When set to `false`, The file extension will remain unchanged from the original import path. +When set to `false`, the file extension will remain unchanged from the original import path. - **Example:** @@ -159,3 +160,14 @@ import './index.css'; // expected output import styles from './index.module.less'; // source code ↓ import styles from './index.module.mjs'; // expected output ``` + +## redirect.asset + +When set to `true`, the paths of imported resource files will be rewritten to the corresponding JavaScript product file. + +When set to `false`, the file extension will remain unchanged from the original import path. + +```ts +import url from './assets/logo.svg'; // source code ↓ +import url from './assets/logo.mjs'; // expected output +``` diff --git a/website/docs/zh/config/lib/redirect.mdx b/website/docs/zh/config/lib/redirect.mdx index b75d6d0eb..3df8ae680 100644 --- a/website/docs/zh/config/lib/redirect.mdx +++ b/website/docs/zh/config/lib/redirect.mdx @@ -43,6 +43,7 @@ const defaultRedirect = { path: true, extension: true, }, + asset: true, }; ``` @@ -102,7 +103,7 @@ const defaultRedirect = { 当设置为 `true` 时,无论原始扩展名或导入路径中是否指定,文件扩展名都将自动添加到产物文件的重写导入路径中。 -当设置为 `false` 时,文件扩展名将保持原始导入路径中的不变(无论是否指定或指定为任意值)。 +当设置为 `false` 时,文件扩展名将保持原始导入路径(无论是否指定或指定为任意值)。 ## redirect.style @@ -144,9 +145,9 @@ import styles from '../foo.css'; // './dist/utils/index.js' 预期生成的代 - **类型:** `boolean` - **默认值:** `true` -当设置为 `true` 时,导入普通样式文件时,路径将被重写为 `.css` 文件,导入 [CSS Modules](/config/rsbuild/output#outputcssmodules) 时,路径将被重写为到对应的 JavaScript 产物文件。 +当设置为 `true` 时,导入普通样式文件的文件扩展名将被重写为 `.css`,导入 [CSS Modules](/config/rsbuild/output#outputcssmodules) 时,路径将被重写为到对应的 JavaScript 产物文件。 -当设置为 `false` 时,文件扩展名将保持原始导入路径中的不变。 +当设置为 `false` 时,文件扩展名将保持原始导入路径。 - **示例:** @@ -159,3 +160,14 @@ import './index.css'; // 预期生成的代码 import styles from './index.module.less'; // 源码 ↓ import styles from './index.module.mjs'; // 预期生成的代码 ``` + +## redirect.asset + +当设置为 `true` 时,导入资源文件的路径将被重写到对应的 JavaScript 产物文件。 + +当设置为 `false` 时,文件扩展名将保持原始导入路径。 + +```ts +import url from './assets/logo.svg'; // 源码 ↓ +import url from './assets/logo.mjs'; // 预期生成的代码 +```