diff --git a/CHANGELOG.md b/CHANGELOG.md index 31329d5a7..4987d97dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,10 +93,11 @@ Changes since the last non-beta release. ``` 2. **CSS Modules now use named exports by default** - - Configured with `namedExport: true` and `exportLocalsConvention: 'camelCase'` + - Configured with `namedExport: true` and `exportLocalsConvention: 'camelCaseOnly'` - **JavaScript:** Use named imports: `import { className } from './styles.module.css'` - **TypeScript:** Use namespace imports: `import * as styles from './styles.module.css'` - Default imports (`import styles from '...'`) no longer work + - **Note:** css-loader requires `'camelCaseOnly'` or `'dashesOnly'` when `namedExport: true`. Using `'camelCase'` causes a build error. - See [CSS Modules Export Mode documentation](./docs/css-modules-export-mode.md) for migration details 3. **Configuration option renamed from `webpack_loader` to `javascript_transpiler`** diff --git a/TODO_v9.md b/TODO_v9.md index 067471529..79bd8a278 100644 --- a/TODO_v9.md +++ b/TODO_v9.md @@ -20,12 +20,15 @@ Align with Next.js and modern tooling by using named exports: options: { modules: { namedExport: true, - exportLocalsConvention: 'camelCase' + exportLocalsConvention: 'camelCaseOnly' // Must be 'camelCaseOnly' or 'dashesOnly' with namedExport: true } } } ``` +**Note:** Using `exportLocalsConvention: 'camelCase'` with `namedExport: true` will cause a build error. +css-loader only allows `'camelCaseOnly'` or `'dashesOnly'` when named exports are enabled. + 2. **Update TypeScript types:** - Ensure proper typing for CSS modules with named exports - May need to update or generate `.d.ts` files for CSS modules @@ -81,4 +84,4 @@ Align with Next.js and modern tooling by using named exports: ### Test Infrastructure - Successfully implemented dual bundler support (webpack/rspack) - test-bundler script working well with status command -- Consider adding more comprehensive tests for both bundlers \ No newline at end of file +- Consider adding more comprehensive tests for both bundlers diff --git a/docs/css-modules-export-mode.md b/docs/css-modules-export-mode.md index 6e613256f..002b558b9 100644 --- a/docs/css-modules-export-mode.md +++ b/docs/css-modules-export-mode.md @@ -32,6 +32,55 @@ import * as styles from './Foo.module.css'; - Aligns with modern JavaScript module standards - Automatically converts kebab-case to camelCase (`my-button` → `myButton`) +### Important: exportLocalsConvention with namedExport + +When `namedExport: true` is enabled (v9 default), css-loader requires `exportLocalsConvention` to be either `'camelCaseOnly'` or `'dashesOnly'`. + +**The following will cause a build error:** +```js +modules: { + namedExport: true, + exportLocalsConvention: 'camelCase' // ❌ ERROR: incompatible with namedExport: true +} +``` + +**Error message:** +``` +"exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option +``` + +**Correct v9 configuration:** +```js +modules: { + namedExport: true, + exportLocalsConvention: 'camelCaseOnly' // ✅ Correct - only camelCase exported +} +``` + +**exportLocalsConvention options with namedExport:** + +When `namedExport: true`, you can use: +- `'camelCaseOnly'` (v9 default): Exports ONLY the camelCase version (e.g., only `myButton`) +- `'dashesOnly'`: Exports ONLY the original kebab-case version (e.g., only `my-button`) + +**Not compatible with namedExport: true:** +- `'camelCase'`: Exports both versions (both `my-button` and `myButton`) - only works with `namedExport: false` (v8 behavior) + +**Configuration Quick Reference:** + +| namedExport | exportLocalsConvention | `.my-button` exports | Use Case | Compatible? | +|-------------|------------------------|---------------------|----------|-------------| +| `true` | `'camelCaseOnly'` | `myButton` | JavaScript conventions | ✅ Valid | +| `true` | `'dashesOnly'` | `'my-button'` | Preserve CSS naming | ✅ Valid | +| `false` | `'camelCase'` | Both `myButton` AND `'my-button'` | v8 compatibility | ✅ Valid | +| `false` | `'asIs'` | `'my-button'` | No transformation | ✅ Valid | +| `true` | `'camelCase'` | - | - | ❌ Build Error | + +**When to use each option:** +- Use `'camelCaseOnly'` if you prefer standard JavaScript naming conventions +- Use `'dashesOnly'` if you want to preserve your CSS class names exactly as written +- Use `'camelCase'` (with `namedExport: false`) only if you need both versions available + ## Version 8.x and Earlier Behavior In Shakapacker v8 and earlier, the default behavior was to use a **default export object**: @@ -244,7 +293,9 @@ import { bright, container, button } from './Component.module.css'; #### 3. Handle Kebab-Case Class Names -With v9's `exportLocalsConvention: 'camelCase'`, kebab-case class names are automatically converted: +**Option A: Use camelCase (v9 default)** + +With `exportLocalsConvention: 'camelCaseOnly'`, kebab-case class names are automatically converted: ```css /* styles.module.css */ @@ -253,13 +304,35 @@ With v9's `exportLocalsConvention: 'camelCase'`, kebab-case class names are auto ``` ```js -// v9 imports (camelCase conversion) +// v9 default - camelCase conversion import { myButton, primaryColor } from './styles.module.css'; +; + JS + File.write(js_file, js_content) + end + + it "warns about v8-style imports" do + doctor.send(:check_css_modules_configuration) + expect(doctor.warnings).to include(match(/Potential v8-style CSS module imports detected/)) + expect(doctor.warnings).to include(match(/v9 uses named exports/)) + expect(doctor.warnings).to include(match(/See docs\/v9_upgrade.md for migration guide/)) + end + end + + context "with v9-style import patterns" do + before do + js_file = source_path.join("component.jsx") + js_content = <<~JS + import { button } from './styles.module.css'; + export const Button = () => ; + JS + File.write(js_file, js_content) + end + + it "does not warn about imports" do + doctor.send(:check_css_modules_configuration) + expect(doctor.warnings).not_to include(match(/v8-style CSS module imports/)) + end + end + end + end end diff --git a/tools/README.md b/tools/README.md index 2dcd1b653..e92ab8ddb 100644 --- a/tools/README.md +++ b/tools/README.md @@ -104,7 +104,7 @@ const Button: React.FC = () => { ### Notes -1. **Kebab-case conversion**: CSS classes with kebab-case (e.g., `my-button`) are automatically converted to camelCase (`myButton`) for JavaScript files, matching css-loader's `exportLocalsConvention: 'camelCase'` setting. +1. **Kebab-case conversion**: CSS classes with kebab-case (e.g., `my-button`) are automatically converted to camelCase (`myButton`) for JavaScript files, matching css-loader's `exportLocalsConvention: 'camelCaseOnly'` setting. 2. **Unused imports**: The codemod only imports CSS classes that are actually used in JavaScript files. If you pass the entire styles object to a component, it will convert to namespace import for safety. @@ -121,4 +121,4 @@ const Button: React.FC = () => { **Solution**: Ensure your TypeScript definitions are updated as shown in the [v9 Upgrade Guide](../docs/v9_upgrade.md). **Issue**: Runtime errors about missing CSS classes -**Solution**: Check if you have kebab-case class names that need camelCase conversion. \ No newline at end of file +**Solution**: Check if you have kebab-case class names that need camelCase conversion.