shakapacker 9.0.0.beta.0 → 9.0.0.beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +54 -0
  3. data/.github/workflows/claude.yml +50 -0
  4. data/.github/workflows/dummy.yml +3 -3
  5. data/.github/workflows/test-bundlers.yml +152 -0
  6. data/.rubocop.yml +1 -0
  7. data/CHANGELOG.md +15 -3
  8. data/CLAUDE.md +29 -0
  9. data/Gemfile.lock +1 -1
  10. data/README.md +42 -1
  11. data/Rakefile +39 -4
  12. data/TODO.md +51 -0
  13. data/TODO_v9.md +84 -0
  14. data/conductor-setup.sh +58 -0
  15. data/conductor.json +7 -0
  16. data/docs/cdn_setup.md +379 -0
  17. data/docs/css-modules-export-mode.md +216 -86
  18. data/docs/deployment.md +10 -1
  19. data/docs/rspack.md +7 -7
  20. data/docs/rspack_migration_guide.md +202 -0
  21. data/docs/using_esbuild_loader.md +3 -3
  22. data/docs/using_swc_loader.md +5 -3
  23. data/docs/v6_upgrade.md +10 -0
  24. data/docs/v9_upgrade.md +185 -0
  25. data/lib/install/bin/shakapacker +3 -17
  26. data/lib/install/config/shakapacker.yml +9 -4
  27. data/lib/shakapacker/configuration.rb +75 -3
  28. data/lib/shakapacker/dev_server_runner.rb +19 -9
  29. data/lib/shakapacker/manifest.rb +4 -3
  30. data/lib/shakapacker/rspack_runner.rb +4 -42
  31. data/lib/shakapacker/runner.rb +105 -11
  32. data/lib/shakapacker/utils/manager.rb +2 -0
  33. data/lib/shakapacker/version.rb +1 -1
  34. data/lib/shakapacker/version_checker.rb +1 -1
  35. data/lib/shakapacker/webpack_runner.rb +4 -42
  36. data/lib/tasks/shakapacker/install.rake +6 -2
  37. data/package/config.js +24 -0
  38. data/package/environments/base.js +12 -2
  39. data/package/environments/development.js +52 -12
  40. data/package/environments/production.js +8 -3
  41. data/package/environments/test.js +5 -3
  42. data/package/index.d.ts +69 -30
  43. data/package/index.js +1 -1
  44. data/package/optimization/rspack.js +9 -5
  45. data/package/plugins/rspack.js +12 -28
  46. data/package/rspack/index.js +57 -0
  47. data/package/rules/babel.js +2 -2
  48. data/package/rules/esbuild.js +2 -2
  49. data/package/rules/raw.js +5 -5
  50. data/package/rules/rspack.js +77 -7
  51. data/package/rules/swc.js +2 -2
  52. data/package/utils/debug.js +49 -0
  53. data/package/utils/getStyleRule.js +19 -10
  54. data/package/utils/requireOrError.js +1 -1
  55. data/package/utils/validateCssModulesConfig.js +91 -0
  56. data/package/utils/validateDependencies.js +61 -0
  57. data/package/webpackDevServerConfig.js +2 -0
  58. data/package-lock.json +11966 -0
  59. data/package.json +9 -2
  60. data/test/package/rules/esbuild.test.js +1 -1
  61. data/test/package/rules/swc.test.js +1 -1
  62. data/tools/README.md +124 -0
  63. data/tools/css-modules-v9-codemod.js +179 -0
  64. data/yarn.lock +199 -81
  65. metadata +20 -3
  66. data/lib/install/bin/shakapacker-rspack +0 -13
@@ -1,26 +1,98 @@
1
1
  # CSS Modules Export Mode
2
2
 
3
- Most React guides and tutorials expect to import CSS Modules using a **default export object**:
3
+ ## Version 9.x (Current Default Behavior)
4
+
5
+ Starting with Shakapacker v9, CSS Modules are configured with **named exports** (`namedExport: true`) by default to align with Next.js and modern tooling standards.
6
+
7
+ ### JavaScript Usage
8
+
9
+ In pure JavaScript projects, you can use true named imports:
10
+
11
+ ```js
12
+ // v9 - named exports in JavaScript
13
+ import { bright, container } from './Foo.module.css';
14
+ <button className={bright} />
15
+ ```
16
+
17
+ ### TypeScript Usage
18
+
19
+ TypeScript cannot statically analyze CSS files to determine the exact export names at compile time. When css-loader generates individual named exports dynamically from your CSS classes, TypeScript doesn't know what those exports will be. Therefore, you must use namespace imports:
20
+
21
+ ```typescript
22
+ // v9 - namespace import required for TypeScript
23
+ import * as styles from './Foo.module.css';
24
+ <button className={styles.bright} />
25
+ ```
26
+
27
+ **Why namespace imports?** While webpack's css-loader generates true named exports at runtime (with `namedExport: true`), TypeScript's type system cannot determine these dynamic exports during compilation. The namespace import pattern allows TypeScript to treat the import as an object with string keys, bypassing the need for static export validation while still benefiting from the runtime optimizations of named exports.
28
+
29
+ ### Benefits of v9 Configuration
30
+ - Eliminates certain webpack warnings
31
+ - Provides better tree-shaking potential
32
+ - Aligns with modern JavaScript module standards
33
+ - Automatically converts kebab-case to camelCase (`my-button` → `myButton`)
34
+
35
+ ## Version 8.x and Earlier Behavior
36
+
37
+ In Shakapacker v8 and earlier, the default behavior was to use a **default export object**:
4
38
 
5
39
  ```js
40
+ // v8 and earlier default
6
41
  import styles from './Foo.module.css';
7
42
  <button className={styles.bright} />
8
43
  ```
9
44
 
10
- However, depending on configuration, `css-loader` may instead emit **named exports**:
45
+ ---
46
+
47
+ ## Migrating from v8 to v9
48
+
49
+ When upgrading to Shakapacker v9, you'll need to update your CSS Module imports from default exports to named exports.
50
+
51
+ ### Migration Options
11
52
 
53
+ #### Option 1: Update Your Code (Recommended)
54
+
55
+ **For JavaScript projects:**
12
56
  ```js
13
- import { bright } from './Foo.module.css';
14
- <button className={bright} />
57
+ // Before (v8)
58
+ import styles from './Component.module.css';
59
+ <div className={styles.container}>
60
+ <button className={styles.button}>Click me</button>
61
+ </div>
62
+
63
+ // After (v9) - JavaScript
64
+ import { container, button } from './Component.module.css';
65
+ <div className={container}>
66
+ <button className={button}>Click me</button>
67
+ </div>
68
+ ```
69
+
70
+ **For TypeScript projects:**
71
+ ```typescript
72
+ // Before (v8)
73
+ import styles from './Component.module.css';
74
+ <div className={styles.container}>
75
+ <button className={styles.button}>Click me</button>
76
+ </div>
77
+
78
+ // After (v9) - TypeScript
79
+ import * as styles from './Component.module.css';
80
+ <div className={styles.container}>
81
+ <button className={styles.button}>Click me</button>
82
+ </div>
15
83
  ```
16
84
 
17
- By default, Shakapacker currently leaves `css-loader`'s `modules.namedExport` option unset, which leads to **named exports** being used in many cases. This can surprise developers expecting the `import styles ...` pattern.
85
+ Note: TypeScript projects only need to change from default import to namespace import (`* as styles`), the property access remains the same.
86
+
87
+ #### Option 2: Keep v8 Behavior
88
+
89
+ If you prefer to keep the v8 default export behavior during migration, you can override the configuration (see below).
18
90
 
19
91
  ---
20
92
 
21
- ## How to Configure Shakapacker for Default Exports
93
+ ## Reverting to Default Exports (v8 Behavior)
22
94
 
23
- To force the more familiar `import styles ...` behavior (i.e. `namedExport: false`), update your webpack configuration as follows.
95
+ To use the v8-style default exports instead of v9's named exports:
24
96
 
25
97
  ### Option 1: Update `config/webpack/commonWebpackConfig.js` (Recommended)
26
98
 
@@ -32,7 +104,7 @@ const { generateWebpackConfig, merge } = require('shakapacker');
32
104
 
33
105
  const baseClientWebpackConfig = generateWebpackConfig();
34
106
 
35
- // Override CSS Modules configuration to use default exports instead of named exports
107
+ // Override CSS Modules configuration to use v8-style default exports
36
108
  const overrideCssModulesConfig = (config) => {
37
109
  // Find the CSS rule in the module rules
38
110
  const cssRule = config.module.rules.find(rule =>
@@ -45,7 +117,7 @@ const overrideCssModulesConfig = (config) => {
45
117
  );
46
118
 
47
119
  if (cssLoaderUse && cssLoaderUse.options && cssLoaderUse.options.modules) {
48
- // Set namedExport to false for default export behavior
120
+ // Override v9 default to use v8-style default exports
49
121
  cssLoaderUse.options.modules.namedExport = false;
50
122
  cssLoaderUse.options.modules.exportLocalsConvention = 'asIs';
51
123
  }
@@ -77,14 +149,14 @@ If you prefer using a separate environment file:
77
149
  const { environment } = require('@shakacode/shakapacker');
78
150
  const getStyleRule = require('@shakacode/shakapacker/package/utils/getStyleRule');
79
151
 
80
- // CSS Modules rule for *.module.css with default export enabled
152
+ // CSS Modules rule for *.module.css with v8-style default export
81
153
  const cssModulesRule = getStyleRule(/\.module\.css$/i, [], {
82
154
  sourceMap: true,
83
155
  importLoaders: 2,
84
156
  modules: {
85
157
  auto: true,
86
- namedExport: false, // <-- key: enable default export object
87
- exportLocalsConvention: 'asIs' // keep your class names as-is
158
+ namedExport: false, // <-- override v9 default
159
+ exportLocalsConvention: 'asIs' // keep class names as-is instead of camelCase
88
160
  }
89
161
  });
90
162
 
@@ -140,110 +212,157 @@ const overrideCssModulesConfig = (config) => {
140
212
 
141
213
  ---
142
214
 
143
- ## Verifying the Configuration
215
+ ## Detailed Migration Guide
144
216
 
145
- ### 1. Rebuild Your Packs
217
+ ### Migrating from v8 (Default Exports) to v9 (Named Exports)
146
218
 
147
- After making the configuration changes, rebuild your webpack bundles:
219
+ #### 1. Update Import Statements
148
220
 
149
- ```bash
150
- # For development
151
- NODE_ENV=development bin/shakapacker
221
+ ```js
222
+ // Old (v8 - default export)
223
+ import styles from './Component.module.css';
152
224
 
153
- # Or with the dev server
154
- bin/shakapacker-dev-server
225
+ // New (v9 - named exports)
226
+ import { bright, container, button } from './Component.module.css';
155
227
  ```
156
228
 
157
- ### 2. Test in Your React Component
229
+ #### 2. Update Class References
158
230
 
159
- Update your component to use default imports:
231
+ ```js
232
+ // Old (v8)
233
+ <div className={styles.container}>
234
+ <button className={styles.button}>Click me</button>
235
+ <span className={styles.bright}>Highlighted text</span>
236
+ </div>
237
+
238
+ // New (v9)
239
+ <div className={container}>
240
+ <button className={button}>Click me</button>
241
+ <span className={bright}>Highlighted text</span>
242
+ </div>
243
+ ```
244
+
245
+ #### 3. Handle Kebab-Case Class Names
246
+
247
+ With v9's `exportLocalsConvention: 'camelCase'`, kebab-case class names are automatically converted:
248
+
249
+ ```css
250
+ /* styles.module.css */
251
+ .my-button { ... }
252
+ .primary-color { ... }
253
+ ```
160
254
 
161
255
  ```js
162
- // Before (named exports)
163
- import { bright } from './Foo.module.css';
256
+ // v9 imports (camelCase conversion)
257
+ import { myButton, primaryColor } from './styles.module.css';
164
258
 
165
- // After (default export)
166
- import styles from './Foo.module.css';
167
- console.log(styles); // { bright: 'Foo_bright__hash' }
259
+ // Use the camelCase versions in your components
260
+ <button className={myButton} />
168
261
  ```
169
262
 
170
- ### 3. Debug Webpack Configuration (Optional)
263
+ #### 4. Using a Codemod for Large Codebases
171
264
 
172
- To inspect the final webpack configuration:
265
+ For large codebases, you can create a codemod to automate the migration:
173
266
 
174
- ```bash
175
- NODE_ENV=development bin/shakapacker --profile --json > /tmp/webpack-stats.json
267
+ ```js
268
+ // css-modules-v9-migration.js
269
+ module.exports = function(fileInfo, api) {
270
+ const j = api.jscodeshift;
271
+ const root = j(fileInfo.source);
272
+
273
+ // Find CSS module imports
274
+ root.find(j.ImportDeclaration, {
275
+ source: { value: value => value.endsWith('.module.css') }
276
+ }).forEach(path => {
277
+ const defaultSpecifier = path.node.specifiers.find(
278
+ spec => spec.type === 'ImportDefaultSpecifier'
279
+ );
280
+
281
+ if (defaultSpecifier) {
282
+ // Convert default import to namespace import for analysis
283
+ // Then extract used properties and convert to named imports
284
+ // ... codemod implementation
285
+ }
286
+ });
287
+
288
+ return root.toSource();
289
+ };
176
290
  ```
177
291
 
178
- Then search for `css-loader` options in the generated JSON file.
292
+ Run with:
293
+ ```bash
294
+ npx jscodeshift -t css-modules-v9-migration.js src/
295
+ ```
179
296
 
180
297
  ---
181
298
 
182
- ## Benefits of Default Export Approach
299
+ ## Version Comparison
183
300
 
184
- 1. **Better Developer Experience**: Matches most React tutorials and documentation
185
- 2. **IDE Support**: Better autocomplete and IntelliSense for CSS class names
186
- 3. **Type Safety**: Easier to add TypeScript definitions for CSS modules
187
- 4. **Consistency**: Aligns with common React ecosystem practices
301
+ | Feature | v8 (and earlier) | v9 |
302
+ |---------|-----------------|----|
303
+ | Default behavior | Default export object | Named exports |
304
+ | Import syntax | `import styles from '...'` | `import { className } from '...'` |
305
+ | Class reference | `styles.className` | `className` |
306
+ | Export convention | `asIs` (no transformation) | `camelCase` |
307
+ | TypeScript warnings | May show warnings | No warnings |
308
+ | Tree-shaking | Limited | Optimized |
188
309
 
189
310
  ---
190
311
 
191
- ## Migration Guide
312
+ ## Benefits of Named Exports (v9 Default)
192
313
 
193
- If you're migrating from named exports to default exports:
314
+ 1. **No Build Warnings**: Eliminates webpack/TypeScript warnings about missing exports
315
+ 2. **Better Tree-Shaking**: Unused CSS class exports can be eliminated
316
+ 3. **Explicit Dependencies**: Clear about which CSS classes are being used
317
+ 4. **Modern Standards**: Aligns with ES modules and modern tooling
318
+ 5. **Type Safety**: TypeScript can validate individual class imports
194
319
 
195
- ### 1. Update Import Statements
320
+ ## Benefits of Default Exports (v8 Behavior)
196
321
 
197
- ```js
198
- // Old (named exports)
199
- import { bright, container, button } from './Component.module.css';
322
+ 1. **Familiar Pattern**: Matches most existing React tutorials
323
+ 2. **Namespace Import**: All classes available under one import
324
+ 3. **Less Verbose**: Single import for all classes
325
+ 4. **Legacy Compatibility**: Works with existing codebases
200
326
 
201
- // New (default export)
202
- import styles from './Component.module.css';
203
- ```
327
+ ---
204
328
 
205
- ### 2. Update Class References
329
+ ## Verifying the Configuration
206
330
 
207
- ```js
208
- // Old
209
- <div className={container}>
210
- <button className={button}>Click me</button>
211
- <span className={bright}>Highlighted text</span>
212
- </div>
331
+ ### 1. Rebuild Your Packs
213
332
 
214
- // New
215
- <div className={styles.container}>
216
- <button className={styles.button}>Click me</button>
217
- <span className={styles.bright}>Highlighted text</span>
218
- </div>
333
+ After making any configuration changes, rebuild your webpack bundles:
334
+
335
+ ```bash
336
+ # For development
337
+ NODE_ENV=development bin/shakapacker
338
+
339
+ # Or with the dev server
340
+ bin/shakapacker-dev-server
219
341
  ```
220
342
 
221
- ### 3. Consider Using a Codemod
343
+ ### 2. Test in Your React Component
222
344
 
223
- For large codebases, consider writing a codemod to automate the migration:
345
+ Verify your imports work correctly:
224
346
 
225
- ```bash
226
- # Example using jscodeshift (pseudocode)
227
- npx jscodeshift -t css-modules-migration.js src/
228
- ```
347
+ ```js
348
+ // v9 default (named exports)
349
+ import { bright } from './Foo.module.css';
350
+ console.log(bright); // 'Foo_bright__hash'
229
351
 
230
- ---
352
+ // Or if using v8 configuration (default export)
353
+ import styles from './Foo.module.css';
354
+ console.log(styles); // { bright: 'Foo_bright__hash' }
355
+ ```
231
356
 
232
- ## Future Shakapacker Configuration
357
+ ### 3. Debug Webpack Configuration (Optional)
233
358
 
234
- In future versions of Shakapacker, this configuration may be exposed via `config/shakapacker.yml`:
359
+ To inspect the final webpack configuration:
235
360
 
236
- ```yml
237
- # Future configuration (not yet implemented)
238
- css_modules:
239
- # true -> named exports (import { bright } ...)
240
- # false -> default export (import styles ...)
241
- named_export: false
361
+ ```bash
362
+ NODE_ENV=development bin/shakapacker --profile --json > /tmp/webpack-stats.json
242
363
  ```
243
364
 
244
- - **Current behavior:** Uses named exports when unset
245
- - **Future behavior:** New app templates will default to `false`
246
- - **Next major release:** The default will change to `false` when unset
365
+ Then search for `css-loader` options in the generated JSON file.
247
366
 
248
367
  ---
249
368
 
@@ -251,15 +370,26 @@ css_modules:
251
370
 
252
371
  ### CSS Classes Not Applying
253
372
 
254
- If your CSS classes aren't applying after the change:
373
+ If your CSS classes aren't applying after the upgrade:
255
374
 
256
- 1. **Check import syntax**: Ensure you're using `import styles from ...`
257
- 2. **Verify class names**: Use `console.log(styles)` to see available classes
258
- 3. **Rebuild webpack**: Clear cache and rebuild: `rm -rf tmp/cache && bin/shakapacker`
375
+ 1. **Check import syntax**: Ensure you're using the correct import style for your configuration
376
+ 2. **Verify class names**: Use `console.log` to see available classes
377
+ 3. **Check camelCase conversion**: Kebab-case names are converted to camelCase in v9
378
+ 4. **Rebuild webpack**: Clear cache and rebuild: `rm -rf tmp/cache && bin/shakapacker`
259
379
 
260
380
  ### TypeScript Support
261
381
 
262
- For TypeScript projects, create type definitions for your CSS modules:
382
+ #### For v9 (Named Exports)
383
+
384
+ ```typescript
385
+ // src/types/css-modules.d.ts
386
+ declare module '*.module.css' {
387
+ const classes: { [key: string]: string };
388
+ export = classes;
389
+ }
390
+ ```
391
+
392
+ #### For v8 Behavior (Default Export)
263
393
 
264
394
  ```typescript
265
395
  // src/types/css-modules.d.ts
@@ -281,8 +411,8 @@ The configuration changes should not impact build performance significantly. If
281
411
 
282
412
  ## Summary
283
413
 
284
- - **Current default**: Named exports (`import { bright } ...`)
285
- - **Recommended for DX**: Default export (`import styles ...`)
286
- - **Implementation**: Override CSS loader configuration in `commonWebpackConfig.js`
287
- - **Migration**: Update imports and class references systematically
288
- - **Future**: Shakapacker will provide native configuration options
414
+ - **v9 default**: Named exports with camelCase conversion
415
+ - **v8 default**: Default export object with no conversion
416
+ - **Migration path**: Update imports or override configuration
417
+ - **Benefits of v9**: No warnings, better tree-shaking, explicit dependencies
418
+ - **Keeping v8 behavior**: Override css-loader configuration as shown above
data/docs/deployment.md CHANGED
@@ -88,7 +88,16 @@ Now, you can set `brotli_static on;` in your nginx site config, as per the confi
88
88
 
89
89
  ## CDN
90
90
 
91
- If you are using a CDN setup, Shakapacker does NOT use the `ASSET_HOST` environment variable to prefix URLs for assets during bundle compilation. You must use the `SHAKAPACKER_ASSET_HOST` environment variable instead (`WEBPACKER_ASSET_HOST` if you're using any version of Webpacker or Shakapacker before Shakapacker v7).
91
+ Shakapacker supports serving JavaScript bundles and assets from a CDN. For a comprehensive guide on setting up CDN with Shakapacker, including CloudFlare configuration, troubleshooting, and advanced setups, see the [CDN Setup Guide](cdn_setup.md).
92
+
93
+ **Quick Setup**: Set the `SHAKAPACKER_ASSET_HOST` environment variable before compiling assets:
94
+
95
+ ```bash
96
+ export SHAKAPACKER_ASSET_HOST=https://cdn.example.com
97
+ RAILS_ENV=production bundle exec rails assets:precompile
98
+ ```
99
+
100
+ Note: Shakapacker does NOT use the `ASSET_HOST` environment variable. You must use `SHAKAPACKER_ASSET_HOST` instead (`WEBPACKER_ASSET_HOST` if using Shakapacker before v7).
92
101
 
93
102
  ## Capistrano
94
103
 
data/docs/rspack.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rspack Integration
2
2
 
3
- Shakapacker supports [Rspack](https://rspack.rs) as an alternative bundler to Webpack. Rspack is a fast Rust-based web bundler with webpack-compatible API that can significantly speed up your build times.
3
+ Shakapacker supports [Rspack](https://rspack.rs) as an alternative assets bundler to Webpack. Rspack is a fast Rust-based web bundler with webpack-compatible API that can significantly speed up your build times.
4
4
 
5
5
  ## Installation
6
6
 
@@ -25,10 +25,10 @@ To enable Rspack, update your `config/shakapacker.yml`:
25
25
  ```yaml
26
26
  default: &default
27
27
  # ... other config options
28
- bundler: 'rspack' # Change from 'webpack' to 'rspack'
28
+ assets_bundler: 'rspack' # Change from 'webpack' to 'rspack'
29
29
  ```
30
30
 
31
- ## Configuration
31
+ ### Configuration Files
32
32
 
33
33
  Rspack uses its own configuration directory to keep things organized. Create your Rspack configuration file at `config/rspack/rspack.config.js`:
34
34
 
@@ -73,7 +73,7 @@ const { generateRspackConfig } = require('shakapacker/rspack')
73
73
  module.exports = generateRspackConfig()
74
74
  ```
75
75
 
76
- > **Note:** Shakapacker will show a deprecation warning if you use `config/webpack/webpack.config.js` with `bundler: 'rspack'`. Please migrate to `config/rspack/rspack.config.js`.
76
+ > **Note:** Shakapacker will show a deprecation warning if you use `config/webpack/webpack.config.js` with `assets_bundler: 'rspack'`. Please migrate to `config/rspack/rspack.config.js`.
77
77
 
78
78
  ## Key Differences from Webpack
79
79
 
@@ -119,10 +119,10 @@ optimization: {
119
119
  All existing Shakapacker commands work the same way and automatically use Rspack when configured:
120
120
 
121
121
  ```bash
122
- # Build (automatically uses rspack when bundler: 'rspack')
122
+ # Build (automatically uses rspack when assets_bundler: 'rspack')
123
123
  ./bin/shakapacker
124
124
 
125
- # Development server (automatically uses rspack when bundler: 'rspack')
125
+ # Development server (automatically uses rspack when assets_bundler: 'rspack')
126
126
  ./bin/shakapacker-dev-server
127
127
 
128
128
  # Watch mode
@@ -151,7 +151,7 @@ Rspack typically provides:
151
151
  ```yaml
152
152
  # config/shakapacker.yml
153
153
  default: &default
154
- bundler: 'rspack'
154
+ assets_bundler: 'rspack'
155
155
  ```
156
156
 
157
157
  3. **Create Rspack config:**