From a7de790d1476b2e1dd4eb53e21874e2d63bdccf3 Mon Sep 17 00:00:00 2001 From: neverland Date: Sat, 22 Mar 2025 16:01:43 +0800 Subject: [PATCH] feat(css): add support for raw CSS imports --- e2e/cases/css/css-raw/index.test.ts | 40 +++++++++++++++++++ e2e/cases/css/css-raw/src/a.css | 3 ++ e2e/cases/css/css-raw/src/b.module.css | 3 ++ e2e/cases/css/css-raw/src/index.js | 5 +++ examples/react/src/App.tsx | 2 +- .../tests/__snapshots__/default.test.ts.snap | 32 +++++++++++++++ packages/core/src/configChain.ts | 2 + packages/core/src/plugins/css.ts | 8 +++- .../tests/__snapshots__/builder.test.ts.snap | 8 ++++ .../core/tests/__snapshots__/css.test.ts.snap | 40 +++++++++++++++++++ .../tests/__snapshots__/default.test.ts.snap | 32 +++++++++++++++ .../__snapshots__/environments.test.ts.snap | 16 ++++++++ packages/core/types.d.ts | 5 +++ 13 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 e2e/cases/css/css-raw/index.test.ts create mode 100644 e2e/cases/css/css-raw/src/a.css create mode 100644 e2e/cases/css/css-raw/src/b.module.css create mode 100644 e2e/cases/css/css-raw/src/index.js diff --git a/e2e/cases/css/css-raw/index.test.ts b/e2e/cases/css/css-raw/index.test.ts new file mode 100644 index 0000000000..aef9dff7a9 --- /dev/null +++ b/e2e/cases/css/css-raw/index.test.ts @@ -0,0 +1,40 @@ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { build, dev } from '@e2e/helper'; +import { expect, test } from '@playwright/test'; + +test('should allow to import raw CSS files in development mode', async ({ + page, +}) => { + const rsbuild = await dev({ + cwd: __dirname, + page, + }); + + expect(await page.evaluate('window.aRaw')).toBe( + readFileSync(path.join(__dirname, 'src/a.css'), 'utf-8'), + ); + expect(await page.evaluate('window.bRaw')).toBe( + readFileSync(path.join(__dirname, 'src/b.module.css'), 'utf-8'), + ); + + await rsbuild.close(); +}); + +test('should allow to import raw CSS files in production mode', async ({ + page, +}) => { + const rsbuild = await build({ + cwd: __dirname, + page, + }); + + expect(await page.evaluate('window.aRaw')).toBe( + readFileSync(path.join(__dirname, 'src/a.css'), 'utf-8'), + ); + expect(await page.evaluate('window.bRaw')).toBe( + readFileSync(path.join(__dirname, 'src/b.module.css'), 'utf-8'), + ); + + await rsbuild.close(); +}); diff --git a/e2e/cases/css/css-raw/src/a.css b/e2e/cases/css/css-raw/src/a.css new file mode 100644 index 0000000000..9d6dfc4988 --- /dev/null +++ b/e2e/cases/css/css-raw/src/a.css @@ -0,0 +1,3 @@ +.header-class { + color: red; +} diff --git a/e2e/cases/css/css-raw/src/b.module.css b/e2e/cases/css/css-raw/src/b.module.css new file mode 100644 index 0000000000..47206f9644 --- /dev/null +++ b/e2e/cases/css/css-raw/src/b.module.css @@ -0,0 +1,3 @@ +.title-class { + font-size: 14px; +} diff --git a/e2e/cases/css/css-raw/src/index.js b/e2e/cases/css/css-raw/src/index.js new file mode 100644 index 0000000000..432dc44722 --- /dev/null +++ b/e2e/cases/css/css-raw/src/index.js @@ -0,0 +1,5 @@ +import aRaw from './a.css?raw'; +import bRaw from './b.module.css?raw'; + +window.aRaw = aRaw; +window.bRaw = bRaw; diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx index dff1751231..3fc6ba217d 100644 --- a/examples/react/src/App.tsx +++ b/examples/react/src/App.tsx @@ -1,4 +1,4 @@ -import './App.css'; +import rawCss from './App.css?raw'; const App = () => { return ( diff --git a/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap b/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap index b744491812..97f2048a4d 100644 --- a/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap +++ b/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap @@ -49,6 +49,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = ` "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -72,6 +75,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = ` }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -463,6 +471,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when produ "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -486,6 +497,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when produ }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -872,6 +888,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -896,6 +915,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -1208,6 +1232,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -1232,6 +1259,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", diff --git a/packages/core/src/configChain.ts b/packages/core/src/configChain.ts index e51f55e2cc..eede8336c2 100644 --- a/packages/core/src/configChain.ts +++ b/packages/core/src/configChain.ts @@ -49,6 +49,8 @@ export const CHAIN_ID = { TS: 'ts', /** Rule for CSS */ CSS: 'css', + /** Rule for raw CSS */ + CSS_RAW: 'css-raw', /** Rule for less */ LESS: 'less', /** Rule for sass */ diff --git a/packages/core/src/plugins/css.ts b/packages/core/src/plugins/css.ts index 24bf726363..bedf34b734 100644 --- a/packages/core/src/plugins/css.ts +++ b/packages/core/src/plugins/css.ts @@ -265,6 +265,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 rawRule = chain.module.rule(CHAIN_ID.RULE.CSS_RAW); const { config } = environment; rule @@ -273,7 +274,12 @@ export const pluginCss = (): RsbuildPlugin => ({ .type('javascript/auto') // 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' }); + .dependency({ not: 'url' }) + // exclude `import './foo.css?raw'` + .resourceQuery({ not: /raw/ }); + + // Support for importing raw CSS files + rawRule.test(CSS_REGEX).type('asset/source').resourceQuery(/raw/); const emitCss = config.output.emitCss ?? target === 'web'; diff --git a/packages/core/tests/__snapshots__/builder.test.ts.snap b/packages/core/tests/__snapshots__/builder.test.ts.snap index 9d7f048247..4bf47a8068 100644 --- a/packages/core/tests/__snapshots__/builder.test.ts.snap +++ b/packages/core/tests/__snapshots__/builder.test.ts.snap @@ -36,6 +36,9 @@ exports[`should use Rspack as the default bundler > apply Rspack correctly 1`] = "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -70,6 +73,11 @@ exports[`should use Rspack as the default bundler > apply Rspack correctly 1`] = }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", diff --git a/packages/core/tests/__snapshots__/css.test.ts.snap b/packages/core/tests/__snapshots__/css.test.ts.snap index c88f6ec032..780ec00978 100644 --- a/packages/core/tests/__snapshots__/css.test.ts.snap +++ b/packages/core/tests/__snapshots__/css.test.ts.snap @@ -11,6 +11,9 @@ exports[`plugin-css > should use custom cssModules rule when using output.cssMod "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -45,6 +48,11 @@ exports[`plugin-css > should use custom cssModules rule when using output.cssMod }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, ], }, "plugins": [ @@ -73,6 +81,9 @@ exports[`plugin-css injectStyles > should apply ignoreCssLoader when injectStyle "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -97,6 +108,11 @@ exports[`plugin-css injectStyles > should apply ignoreCssLoader when injectStyle }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, ], }, "plugins": [ @@ -118,6 +134,9 @@ exports[`plugin-css injectStyles > should use css-loader + style-loader when inj "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -152,6 +171,11 @@ exports[`plugin-css injectStyles > should use css-loader + style-loader when inj }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, ], }, "plugins": [ @@ -171,6 +195,9 @@ exports[`should ensure isolation of PostCSS config objects between different bui "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -220,6 +247,11 @@ exports[`should ensure isolation of PostCSS config objects between different bui }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, ] `; @@ -232,6 +264,9 @@ exports[`should ensure isolation of PostCSS config objects between different bui "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -281,5 +316,10 @@ exports[`should ensure isolation of PostCSS config objects between different bui }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, ] `; diff --git a/packages/core/tests/__snapshots__/default.test.ts.snap b/packages/core/tests/__snapshots__/default.test.ts.snap index 0564c84adc..54e2da9a22 100644 --- a/packages/core/tests/__snapshots__/default.test.ts.snap +++ b/packages/core/tests/__snapshots__/default.test.ts.snap @@ -36,6 +36,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = ` "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -70,6 +73,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = ` }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -459,6 +467,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when prod "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -493,6 +504,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when prod }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -918,6 +934,9 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -942,6 +961,11 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -1280,6 +1304,9 @@ exports[`tools.rspack > should match snapshot 1`] = ` "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -1314,6 +1341,11 @@ exports[`tools.rspack > should match snapshot 1`] = ` }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", diff --git a/packages/core/tests/__snapshots__/environments.test.ts.snap b/packages/core/tests/__snapshots__/environments.test.ts.snap index 106affbf22..743d34e388 100644 --- a/packages/core/tests/__snapshots__/environments.test.ts.snap +++ b/packages/core/tests/__snapshots__/environments.test.ts.snap @@ -1313,6 +1313,9 @@ exports[`environment config > tools.rspack / bundlerChain can be configured in e "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -1347,6 +1350,11 @@ exports[`environment config > tools.rspack / bundlerChain can be configured in e }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", @@ -1671,6 +1679,9 @@ exports[`environment config > tools.rspack / bundlerChain can be configured in e "resolve": { "preferRelative": true, }, + "resourceQuery": { + "not": /raw/, + }, "sideEffects": true, "test": /\\\\\\.css\\$/, "type": "javascript/auto", @@ -1695,6 +1706,11 @@ exports[`environment config > tools.rspack / bundlerChain can be configured in e }, ], }, + { + "resourceQuery": /raw/, + "test": /\\\\\\.css\\$/, + "type": "asset/source", + }, { "dependency": { "not": "url", diff --git a/packages/core/types.d.ts b/packages/core/types.d.ts index 2719753a7e..c6449c7941 100644 --- a/packages/core/types.d.ts +++ b/packages/core/types.d.ts @@ -184,6 +184,11 @@ declare module '*?inline' { export default content; } +declare module '*.css?raw' { + const content: string; + export default content; +} + /** * CSS Modules */