Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions examples/preact-component-bundle-false/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FunctionComponent } from 'preact';
import logo from '../../assets/logo.svg';
import styles from './index.module.scss';

interface CounterButtonProps {
Expand All @@ -10,7 +11,12 @@ export const CounterButton: FunctionComponent<CounterButtonProps> = ({
onClick,
label,
}) => (
<button type="button" className={styles.button} onClick={onClick}>
<button
type="button"
className={`${styles.button} counter-button`}
onClick={onClick}
>
<img src={logo} alt="react" />
{label}
</button>
);
5 changes: 5 additions & 0 deletions examples/preact-component-bundle-false/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
7 changes: 7 additions & 0 deletions examples/preact-component-bundle-false/src/index.scss
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 0 additions & 6 deletions examples/react-component-bundle-false/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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()],
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type React from 'react';
import logo from '../../assets/logo.svg';
import styles from './index.module.scss';

interface CounterButtonProps {
Expand All @@ -10,7 +11,12 @@ export const CounterButton: React.FC<CounterButtonProps> = ({
onClick,
label,
}) => (
<button type="button" className={styles.button} onClick={onClick}>
<button
type="button"
className={`${styles.button} counter-button`}
onClick={onClick}
>
<img src={logo} alt="react" />
{label}
</button>
);
5 changes: 5 additions & 0 deletions examples/react-component-bundle-false/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 0 additions & 1 deletion examples/react-component-bundle/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()],
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type React from 'react';
import logo from '../../assets/logo.svg';
import styles from './index.module.scss';

interface CounterButtonProps {
Expand All @@ -10,7 +11,12 @@ export const CounterButton: React.FC<CounterButtonProps> = ({
onClick,
label,
}) => (
<button type="button" className={styles.button} onClick={onClick}>
<button
type="button"
className={`${styles.button} counter-button`}
onClick={onClick}
>
<img src={logo} alt="react" />
{label}
</button>
);
5 changes: 5 additions & 0 deletions examples/react-component-bundle/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
68 changes: 68 additions & 0 deletions packages/core/src/asset/LibSvgrPatchPlugin.ts
Original file line number Diff line number Diff line change
@@ -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;
});
}
});
});
}
}
150 changes: 146 additions & 4 deletions packages/core/src/asset/assetConfig.ts
Original file line number Diff line number Diff line change
@@ -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 = (
Expand All @@ -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 })],
};
}

Expand Down
Loading
Loading