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
45 changes: 45 additions & 0 deletions e2e/cases/css/css-inline/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { build, dev, rspackOnlyTest } from '@e2e/helper';
import { expect } from '@playwright/test';

rspackOnlyTest(
'should allow to import inline CSS files in development mode',
async ({ page }) => {
const rsbuild = await dev({
cwd: __dirname,
page,
});

const aInline: string = await page.evaluate('window.aInline');
const bInline: string = await page.evaluate('window.bInline');

expect(
aInline.includes('.header-class') && aInline.includes('color: red'),
).toBe(true);
expect(
bInline.includes('.title-class') && bInline.includes('font-size: 14px'),
).toBe(true);

await rsbuild.close();
},
);

rspackOnlyTest(
'should allow to import inline CSS files in production mode',
async ({ page }) => {
const rsbuild = await build({
cwd: __dirname,
page,
});

expect(await page.evaluate('window.aInline')).toBe(
'.header-class{color:red}',
);
expect(await page.evaluate('window.bInline')).toBe(
'.title-class{font-size:14px}',
);

await rsbuild.close();
},
);
3 changes: 3 additions & 0 deletions e2e/cases/css/css-inline/src/a.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.header-class {
color: red;
}
3 changes: 3 additions & 0 deletions e2e/cases/css/css-inline/src/b.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.title-class {
font-size: 14px;
}
5 changes: 5 additions & 0 deletions e2e/cases/css/css-inline/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import aInline from './a.css?inline';
import bInline from './b.module.css?inline';

window.aInline = aInline;
window.bInline = bInline;
2 changes: 1 addition & 1 deletion examples/react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import rawCss from './App.css?raw';
import './App.css';

const App = () => {
return (
Expand Down
88 changes: 84 additions & 4 deletions packages/compat/webpack/tests/__snapshots__/default.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = `
"preferRelative": true,
},
"resourceQuery": {
"not": /raw/,
"not": /raw\\|inline/,
},
"sideEffects": true,
"test": /\\\\\\.css\\$/,
Expand All @@ -75,6 +75,26 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = `
},
],
},
{
"resolve": {
"preferRelative": true,
},
"resourceQuery": /inline/,
"sideEffects": true,
"test": /\\\\\\.css\\$/,
"type": "javascript/auto",
"use": [
{
"loader": "<ROOT>/packages/core/compiled/css-loader/index.js",
"options": {
"exportType": "string",
"importLoaders": 0,
"modules": false,
"sourceMap": false,
},
},
],
},
{
"resourceQuery": /raw/,
"test": /\\\\\\.css\\$/,
Expand Down Expand Up @@ -472,7 +492,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when produ
"preferRelative": true,
},
"resourceQuery": {
"not": /raw/,
"not": /raw\\|inline/,
},
"sideEffects": true,
"test": /\\\\\\.css\\$/,
Expand All @@ -497,6 +517,26 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when produ
},
],
},
{
"resolve": {
"preferRelative": true,
},
"resourceQuery": /inline/,
"sideEffects": true,
"test": /\\\\\\.css\\$/,
"type": "javascript/auto",
"use": [
{
"loader": "<ROOT>/packages/core/compiled/css-loader/index.js",
"options": {
"exportType": "string",
"importLoaders": 0,
"modules": false,
"sourceMap": false,
},
},
],
},
{
"resourceQuery": /raw/,
"test": /\\\\\\.css\\$/,
Expand Down Expand Up @@ -889,7 +929,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
"preferRelative": true,
},
"resourceQuery": {
"not": /raw/,
"not": /raw\\|inline/,
},
"sideEffects": true,
"test": /\\\\\\.css\\$/,
Expand All @@ -915,6 +955,26 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
},
],
},
{
"resolve": {
"preferRelative": true,
},
"resourceQuery": /inline/,
"sideEffects": true,
"test": /\\\\\\.css\\$/,
"type": "javascript/auto",
"use": [
{
"loader": "<ROOT>/packages/core/compiled/css-loader/index.js",
"options": {
"exportType": "string",
"importLoaders": 0,
"modules": false,
"sourceMap": false,
},
},
],
},
{
"resourceQuery": /raw/,
"test": /\\\\\\.css\\$/,
Expand Down Expand Up @@ -1233,7 +1293,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
"preferRelative": true,
},
"resourceQuery": {
"not": /raw/,
"not": /raw\\|inline/,
},
"sideEffects": true,
"test": /\\\\\\.css\\$/,
Expand All @@ -1259,6 +1319,26 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
},
],
},
{
"resolve": {
"preferRelative": true,
},
"resourceQuery": /inline/,
"sideEffects": true,
"test": /\\\\\\.css\\$/,
"type": "javascript/auto",
"use": [
{
"loader": "<ROOT>/packages/core/compiled/css-loader/index.js",
"options": {
"exportType": "string",
"importLoaders": 0,
"modules": false,
"sourceMap": false,
},
},
],
},
{
"resourceQuery": /raw/,
"test": /\\\\\\.css\\$/,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/configChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export const CHAIN_ID = {
CSS: 'css',
/** Rule for raw CSS */
CSS_RAW: 'css-raw',
/** Rule for inline CSS */
CSS_INLINE: 'css-inline',
/** Rule for Less */
LESS: 'less',
/** Rule for raw Less */
Expand Down
82 changes: 62 additions & 20 deletions packages/core/src/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
PostCSSOptions,
RsbuildPlugin,
Rspack,
RspackChain,
} from '../types';

const getCSSModulesLocalIdentName = (
Expand Down Expand Up @@ -265,6 +266,7 @@ export const pluginCss = (): RsbuildPlugin => ({
order: 'pre',
handler: async (chain, { target, isProd, CHAIN_ID, environment }) => {
const rule = chain.module.rule(CHAIN_ID.RULE.CSS);
const inlineRule = chain.module.rule(CHAIN_ID.RULE.CSS_INLINE);
const { config } = environment;

rule
Expand All @@ -274,10 +276,16 @@ export const pluginCss = (): RsbuildPlugin => ({
// When using `new URL('./path/to/foo.css', import.meta.url)`,
// the module should be treated as an asset module rather than a JS module.
.dependency({ not: 'url' })
// exclude `import './foo.css?raw'`
.resourceQuery({ not: /raw/ });
// exclude `import './foo.css?raw'` and `import './foo.css?inline'`
.resourceQuery({ not: /raw|inline/ });

// Support for importing raw CSS files
// Support for `import inlineCss from "a.css?inline"`
inlineRule
.test(CSS_REGEX)
.type('javascript/auto')
.resourceQuery(/inline/);

// Support for `import rawCss from "a.css?raw"`
chain.module
.rule(CHAIN_ID.RULE.CSS_RAW)
.test(CSS_REGEX)
Expand Down Expand Up @@ -316,7 +324,18 @@ export const pluginCss = (): RsbuildPlugin => ({
// Number of loaders applied before css-loader for `@import` at-rules
let importLoaders = 0;

rule.use(CHAIN_ID.USE.CSS).loader(getCompiledPath('css-loader'));
// Update the normal CSS rule and the inline CSS rule
const updateRules = (
callback: (rule: RspackChain.Rule, type: 'normal' | 'inline') => void,
) => {
callback(rule, 'normal');
callback(inlineRule, 'inline');
};

const cssLoaderPath = getCompiledPath('css-loader');
updateRules((rule) => {
rule.use(CHAIN_ID.USE.CSS).loader(cssLoaderPath);
});

if (emitCss) {
// `builtin:lightningcss-loader` is not supported when using webpack
Expand All @@ -331,10 +350,18 @@ export const pluginCss = (): RsbuildPlugin => ({
environment.browserslist,
);

rule
.use(CHAIN_ID.USE.LIGHTNINGCSS)
.loader('builtin:lightningcss-loader')
.options(lightningcssOptions);
updateRules((rule, type) => {
const enableMinify =
type === 'inline' && config.mode === 'production';
rule
.use(CHAIN_ID.USE.LIGHTNINGCSS)
.loader('builtin:lightningcss-loader')
.options(
enableMinify
? { ...lightningcssOptions, minify: true }
: lightningcssOptions,
);
});
}

const postcssLoaderOptions = await getPostcssLoaderOptions({
Expand All @@ -349,10 +376,14 @@ export const pluginCss = (): RsbuildPlugin => ({
postcssLoaderOptions.postcssOptions?.plugins?.length
) {
importLoaders++;
rule
.use(CHAIN_ID.USE.POSTCSS)
.loader(getCompiledPath('postcss-loader'))
.options(postcssLoaderOptions);
const postcssLoaderPath = getCompiledPath('postcss-loader');

updateRules((rule) => {
rule
.use(CHAIN_ID.USE.POSTCSS)
.loader(postcssLoaderPath)
.options(postcssLoaderOptions);
});
}
}

Expand All @@ -363,20 +394,31 @@ export const pluginCss = (): RsbuildPlugin => ({
localIdentName,
emitCss,
});
rule.use(CHAIN_ID.USE.CSS).options(cssLoaderOptions);

updateRules((rule, type) => {
rule.use(CHAIN_ID.USE.CSS).options(
type === 'inline'
? ({
...cssLoaderOptions,
exportType: 'string',
modules: false,
} satisfies CSSLoaderOptions)
: cssLoaderOptions,
);

// CSS imports should always be treated as sideEffects
rule.merge({ sideEffects: true });

// Enable preferRelative by default, which is consistent with the default behavior of css-loader
// see: https://github.com/webpack-contrib/css-loader/blob/579fc13/src/plugins/postcss-import-parser.js#L234
rule.resolve.preferRelative(true);
});

const isStringExport = cssLoaderOptions.exportType === 'string';
if (isStringExport && rule.uses.has(CHAIN_ID.USE.MINI_CSS_EXTRACT)) {
rule.uses.delete(CHAIN_ID.USE.MINI_CSS_EXTRACT);
}

// CSS imports should always be treated as sideEffects
rule.merge({ sideEffects: true });

// Enable preferRelative by default, which is consistent with the default behavior of css-loader
// see: https://github.com/webpack-contrib/css-loader/blob/579fc13/src/plugins/postcss-import-parser.js#L234
rule.resolve.preferRelative(true);

// Apply CSS extract plugin if not using style-loader and emitCss is true
if (emitCss && !config.output.injectStyles && !isStringExport) {
const extractPluginOptions = config.tools.cssExtract.pluginOptions;
Expand Down
Loading
Loading