packages/rspack/src/migrations/update-23-0-0/rewrite-experiments-css-to-module-rules.md
experiments.css: true for @rspack/core@2@rspack/core@2 removed experiments.css and promoted CSS handling to a
default-available feature — but it's now opt-in per file type via
module.rules. Workspaces that set experiments.css: true on v1 will
silently lose CSS processing on v2 unless module.rules declares a rule
matching their CSS file extensions with a type of 'css', 'css/module',
or 'css/auto'.
This change is workspace-specific (which test: regex, which type:,
whether existing rules already cover it), so the migration ships as an
AI-assisted instruction rather than a blind codemod.
For every rspack.config.{ts,js,mjs,cjs} in the workspace that has
experiments.css: true:
Delete the experiments.css: true line. If experiments becomes
empty after the deletion, delete the whole experiments: { ... }
property too.
Ensure module.rules has a CSS rule using type: 'css/auto' (or
'css' / 'css/module' if you have a stronger preference). The rspack
docs recommend 'css/auto' because it auto-detects CSS Modules
(filenames matching *.module.css).
If module.rules already has an entry that matches .css files —
for example via css-loader / style-loader / MiniCssExtractPlugin
— leave it alone. The user opted into a loader pipeline on v1 and
that pipeline still works on v2.
If module.rules has no CSS-related rule at all, append:
{
test: /\.css$/,
type: 'css/auto',
}
Do not touch experiments.css: false configs. Those are handled
automatically by the deterministic codemod that runs alongside this
prompt.
export default {
// ...
experiments: {
css: true,
},
};
export default {
// ...
module: {
rules: [{ test: /\.css$/, type: 'css/auto' }],
},
};
export default {
// ...
experiments: {
css: true,
},
module: {
rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }],
},
};
experiments.css removed, existing rule untouchedexport default {
// ...
module: {
rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }],
},
};