feat(rspack): replace css by postcss and bump/remove dependencies (#10724)

This commit is contained in:
Juan de Dios Martínez Vallejo
2025-03-23 18:07:47 +01:00
committed by GitHub
parent 490c168571
commit d18fb092ad
4 changed files with 422 additions and 2107 deletions

File diff suppressed because it is too large Load Diff

View File

@ -21,10 +21,7 @@
"dependencies": { "dependencies": {
"@babel/core": "^7.26.9", "@babel/core": "^7.26.9",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@rsbuild/plugin-node-polyfill": "^1.3.0",
"@rspack/cli": "^1.2.7",
"@rspack/core": "^1.2.8", "@rspack/core": "^1.2.8",
"@swc/helpers": "^0.5.15",
"@vue/compiler-sfc": "^3.5.13", "@vue/compiler-sfc": "^3.5.13",
"acorn": "^8.14.0", "acorn": "^8.14.0",
"acorn-stage3": "^4.0.0", "acorn-stage3": "^4.0.0",
@ -32,21 +29,19 @@
"babel-loader": "^8.0.0", "babel-loader": "^8.0.0",
"cli-highlight": "^2.1.11", "cli-highlight": "^2.1.11",
"commander": "^8.3.0", "commander": "^8.3.0",
"css": "^3.0.0", "css-loader": "^7.1.2",
"css-loader": "^6.11.0",
"dotenv-webpack": "^8.1.0", "dotenv-webpack": "^8.1.0",
"loader-utils": "^2.0.0 || ^3.0.0", "loader-utils": "^3.3.1",
"postcss": "^8.5.3", "postcss": "^8.5.3",
"postcss-import": "^14.0.0", "postcss-import": "^16.1.0",
"postcss-loader": "^8.1.1", "postcss-loader": "^8.1.1",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"react-refresh": "^0.14.2", "react-refresh": "^0.14.2",
"rspack-plugin-virtual-module": "^0.1.13", "rspack-plugin-virtual-module": "^0.1.13",
"sass": "^1.0.0", "sass": "^1.86.0",
"sass-embedded": "^1.85.1", "sass-embedded": "^1.86.0",
"sass-loader": "^16.0.5", "sass-loader": "^16.0.5",
"sax": "^1.4.1", "sax": "^1.4.1",
"source-map": "^0.7.4",
"swc-loader": "^0.2.6", "swc-loader": "^0.2.6",
"ts-checker-rspack-plugin": "^1.1.1", "ts-checker-rspack-plugin": "^1.1.1",
"ts-dedent": "^2.2.0", "ts-dedent": "^2.2.0",

View File

@ -15,6 +15,10 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
// we need to patch VueLoader if we want to enable hmr // we need to patch VueLoader if we want to enable hmr
if (env.hmr) { if (env.hmr) {
patchVueLoaderForHMR(); patchVueLoaderForHMR();
// TODO: fix hmr in child components
// https://github.com/web-infra-dev/rsbuild/issues/3217#issuecomment-2290946740
// config.devtool(false);
} }
// resolve .vue files // resolve .vue files

View File

@ -1,4 +1,4 @@
import { parse, Import, Stylesheet } from 'css'; import postcss, { Root, Rule, Declaration, AtRule } from 'postcss';
import { urlToRequest } from 'loader-utils'; import { urlToRequest } from 'loader-utils';
import { dedent } from 'ts-dedent'; import { dedent } from 'ts-dedent';
@ -11,21 +11,22 @@ export default function loader(content: string, map: any) {
const inline = !!options.useForImports; const inline = !!options.useForImports;
const requirePrefix = inline ? inlineLoader : ''; const requirePrefix = inline ? inlineLoader : '';
const ast = parse(content); const ast = postcss.parse(content);
// todo: revise if this is necessary // todo: revise if this is necessary
// todo: perhaps use postCSS and just build imports into a single file? // todo: perhaps use postCSS and just build imports into a single file?
let dependencies = []; let dependencies = [];
getAndRemoveImportRules(ast) getAndRemoveImportRules(ast)
.map(extractUrlFromRule) .map(extractUrlFromRule)
.map(createRequireUri) .map(createRequireUri)
.forEach(({ uri, requireURI }) => { .forEach(({ requireURI }) => {
dependencies.push(`require("${requirePrefix}${requireURI}")`); dependencies.push(`require("${requirePrefix}${requireURI}")`);
}); });
const str = JSON.stringify(ast, (k, v) => (k === 'position' ? undefined : v)); const cssCompatibleAST = transformPostcssASTtoCSS(ast);
// map.mappings = map.mappings.replace(/;{2,}/, '') const str = JSON.stringify(cssCompatibleAST);
const code = dedent` const code = dedent`
/* CSS2JSON */ /* CSS2JSON */
@ -33,41 +34,29 @@ export default function loader(content: string, map: any) {
const ___CSS2JSON_LOADER_EXPORT___ = ${str} const ___CSS2JSON_LOADER_EXPORT___ = ${str}
export default ___CSS2JSON_LOADER_EXPORT___ export default ___CSS2JSON_LOADER_EXPORT___
`; `;
this.callback(
null, this.callback(null, code, map);
code, //`${dependencies.join('\n')}module.exports = ${str};`,
map,
);
} }
function getImportRules(ast: Stylesheet): Import[] { /**
if (!ast || (<any>ast).type !== 'stylesheet' || !ast.stylesheet) { * Extract @import and remove them from the AST
return []; */
} function getAndRemoveImportRules(ast: Root): AtRule[] {
return <Import[]>( const imports = ast.nodes.filter(
ast.stylesheet.rules.filter( (node) => node.type === 'atrule' && node.name === 'import',
(rule) => rule.type === 'import' && (<any>rule).import, ) as AtRule[];
) imports.forEach((rule) => rule.remove());
);
}
function getAndRemoveImportRules(ast: Stylesheet): Import[] {
const imports = getImportRules(ast);
ast.stylesheet.rules = ast.stylesheet.rules.filter(
(rule) => rule.type !== 'import',
);
return imports; return imports;
} }
/** /**
* Extracts the url from import rule (ex. `url("./platform.css")`) * Extracts the url from import rule (ex. `url("./platform.css")`)
*/ */
function extractUrlFromRule(importRule: Import): string { function extractUrlFromRule(importRule: AtRule): string {
const urlValue = importRule.import; const params = importRule.params;
const unpackedUrlMatch = urlValue.match(unpackUrlPattern); const unpackedUrlMatch = params.match(unpackUrlPattern);
const unpackedValue = unpackedUrlMatch ? unpackedUrlMatch[1] : urlValue; const unpackedValue = unpackedUrlMatch ? unpackedUrlMatch[1] : params;
const quotesMatch = unpackedValue.match(betweenQuotesPattern); const quotesMatch = unpackedValue.match(betweenQuotesPattern);
return quotesMatch ? quotesMatch[2] : unpackedValue; return quotesMatch ? quotesMatch[2] : unpackedValue;
@ -75,7 +64,40 @@ function extractUrlFromRule(importRule: Import): string {
function createRequireUri(uri): { uri: string; requireURI: string } { function createRequireUri(uri): { uri: string; requireURI: string } {
return { return {
uri: uri, uri,
requireURI: urlToRequest(uri), requireURI: urlToRequest(uri),
}; };
} }
/**
* TRANSFORMING THE POSTCSS AST TO THE ORIGINAL CSS AST
*/
function transformPostcssASTtoCSS(ast: Root) {
return {
type: 'stylesheet',
stylesheet: {
rules: ast.nodes
.filter((node) => node.type === 'rule') // Solo reglas CSS, no comentarios ni otros
.map(transformRule),
parsingErrors: [],
},
};
}
function transformRule(rule: Rule) {
return {
type: 'rule',
selectors: rule.selectors,
declarations: rule.nodes
.filter((node) => node.type === 'decl')
.map(transformDeclaration),
};
}
function transformDeclaration(decl: Declaration) {
return {
type: 'declaration',
property: decl.prop,
value: decl.value,
};
}