shakapacker 8.4.0 → 9.0.0

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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintignore +1 -0
  3. data/.eslintrc.fast.js +40 -0
  4. data/.eslintrc.js +48 -0
  5. data/.github/STATUS.md +1 -0
  6. data/.github/workflows/claude-code-review.yml +54 -0
  7. data/.github/workflows/claude.yml +50 -0
  8. data/.github/workflows/dummy.yml +8 -4
  9. data/.github/workflows/generator.yml +17 -14
  10. data/.github/workflows/node.yml +23 -1
  11. data/.github/workflows/ruby.yml +11 -0
  12. data/.github/workflows/test-bundlers.yml +170 -0
  13. data/.gitignore +17 -0
  14. data/.husky/pre-commit +2 -0
  15. data/.npmignore +56 -0
  16. data/.prettierignore +3 -0
  17. data/.rubocop.yml +1 -0
  18. data/.yalcignore +26 -0
  19. data/CHANGELOG.md +156 -18
  20. data/CLAUDE.md +29 -0
  21. data/CONTRIBUTING.md +138 -20
  22. data/Gemfile.lock +3 -3
  23. data/README.md +130 -5
  24. data/Rakefile +39 -4
  25. data/TODO.md +50 -0
  26. data/TODO_v9.md +87 -0
  27. data/conductor-setup.sh +70 -0
  28. data/conductor.json +7 -0
  29. data/docs/cdn_setup.md +379 -0
  30. data/docs/css-modules-export-mode.md +512 -0
  31. data/docs/deployment.md +10 -1
  32. data/docs/optional-peer-dependencies.md +198 -0
  33. data/docs/peer-dependencies.md +60 -0
  34. data/docs/rspack.md +190 -0
  35. data/docs/rspack_migration_guide.md +202 -0
  36. data/docs/transpiler-migration.md +188 -0
  37. data/docs/transpiler-performance.md +179 -0
  38. data/docs/troubleshooting.md +5 -0
  39. data/docs/typescript-migration.md +378 -0
  40. data/docs/typescript.md +99 -0
  41. data/docs/using_esbuild_loader.md +3 -3
  42. data/docs/using_swc_loader.md +5 -3
  43. data/docs/v6_upgrade.md +10 -0
  44. data/docs/v9_upgrade.md +413 -0
  45. data/lib/install/bin/shakapacker +3 -5
  46. data/lib/install/config/rspack/rspack.config.js +6 -0
  47. data/lib/install/config/rspack/rspack.config.ts +7 -0
  48. data/lib/install/config/shakapacker.yml +12 -2
  49. data/lib/install/config/webpack/webpack.config.ts +7 -0
  50. data/lib/install/package.json +38 -0
  51. data/lib/install/template.rb +194 -44
  52. data/lib/shakapacker/configuration.rb +141 -0
  53. data/lib/shakapacker/dev_server_runner.rb +25 -5
  54. data/lib/shakapacker/doctor.rb +844 -0
  55. data/lib/shakapacker/manifest.rb +4 -2
  56. data/lib/shakapacker/rspack_runner.rb +19 -0
  57. data/lib/shakapacker/runner.rb +144 -4
  58. data/lib/shakapacker/swc_migrator.rb +376 -0
  59. data/lib/shakapacker/utils/manager.rb +2 -0
  60. data/lib/shakapacker/version.rb +1 -1
  61. data/lib/shakapacker/version_checker.rb +1 -1
  62. data/lib/shakapacker/webpack_runner.rb +4 -42
  63. data/lib/shakapacker.rb +2 -1
  64. data/lib/tasks/shakapacker/doctor.rake +8 -0
  65. data/lib/tasks/shakapacker/install.rake +12 -2
  66. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  67. data/lib/tasks/shakapacker.rake +1 -0
  68. data/package/.npmignore +4 -0
  69. data/package/babel/preset.ts +56 -0
  70. data/package/config.ts +175 -0
  71. data/package/{dev_server.js → dev_server.ts} +8 -5
  72. data/package/env.ts +92 -0
  73. data/package/environments/base.ts +138 -0
  74. data/package/environments/development.ts +90 -0
  75. data/package/environments/production.ts +80 -0
  76. data/package/environments/test.ts +53 -0
  77. data/package/environments/types.ts +90 -0
  78. data/package/esbuild/index.ts +42 -0
  79. data/package/index.d.ts +3 -97
  80. data/package/index.ts +52 -0
  81. data/package/loaders.d.ts +28 -0
  82. data/package/optimization/rspack.ts +36 -0
  83. data/package/optimization/webpack.ts +57 -0
  84. data/package/plugins/rspack.ts +103 -0
  85. data/package/plugins/webpack.ts +62 -0
  86. data/package/rspack/index.ts +64 -0
  87. data/package/rules/{babel.js → babel.ts} +2 -2
  88. data/package/rules/{coffee.js → coffee.ts} +1 -1
  89. data/package/rules/css.ts +3 -0
  90. data/package/rules/{erb.js → erb.ts} +1 -1
  91. data/package/rules/esbuild.ts +10 -0
  92. data/package/rules/file.ts +40 -0
  93. data/package/rules/{jscommon.js → jscommon.ts} +4 -4
  94. data/package/rules/{less.js → less.ts} +4 -4
  95. data/package/rules/raw.ts +25 -0
  96. data/package/rules/rspack.ts +176 -0
  97. data/package/rules/{sass.js → sass.ts} +7 -3
  98. data/package/rules/{stylus.js → stylus.ts} +4 -8
  99. data/package/rules/swc.ts +10 -0
  100. data/package/rules/{index.js → webpack.ts} +1 -1
  101. data/package/swc/index.ts +54 -0
  102. data/package/types/README.md +87 -0
  103. data/package/types/index.ts +60 -0
  104. data/package/types.ts +108 -0
  105. data/package/utils/configPath.ts +6 -0
  106. data/package/utils/debug.ts +49 -0
  107. data/package/utils/defaultConfigPath.ts +4 -0
  108. data/package/utils/errorCodes.ts +219 -0
  109. data/package/utils/errorHelpers.ts +143 -0
  110. data/package/utils/getStyleRule.ts +64 -0
  111. data/package/utils/helpers.ts +85 -0
  112. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  113. data/package/utils/pathValidation.ts +139 -0
  114. data/package/utils/requireOrError.ts +15 -0
  115. data/package/utils/snakeToCamelCase.ts +5 -0
  116. data/package/utils/typeGuards.ts +342 -0
  117. data/package/utils/validateDependencies.ts +61 -0
  118. data/package/webpack-types.d.ts +33 -0
  119. data/package/webpackDevServerConfig.ts +117 -0
  120. data/package.json +134 -9
  121. data/scripts/remove-use-strict.js +45 -0
  122. data/scripts/type-check-no-emit.js +27 -0
  123. data/test/package/config.test.js +3 -0
  124. data/test/package/env.test.js +42 -7
  125. data/test/package/environments/base.test.js +5 -1
  126. data/test/package/rules/babel.test.js +16 -0
  127. data/test/package/rules/esbuild.test.js +1 -1
  128. data/test/package/rules/raw.test.js +40 -7
  129. data/test/package/rules/swc.test.js +1 -1
  130. data/test/package/rules/webpack.test.js +35 -0
  131. data/test/package/staging.test.js +4 -3
  132. data/test/package/transpiler-defaults.test.js +127 -0
  133. data/test/peer-dependencies.sh +85 -0
  134. data/test/scripts/remove-use-strict.test.js +125 -0
  135. data/test/typescript/build.test.js +118 -0
  136. data/test/typescript/environments.test.js +107 -0
  137. data/test/typescript/pathValidation.test.js +142 -0
  138. data/test/typescript/securityValidation.test.js +182 -0
  139. data/tools/README.md +124 -0
  140. data/tools/css-modules-v9-codemod.js +179 -0
  141. data/tsconfig.eslint.json +16 -0
  142. data/tsconfig.json +38 -0
  143. data/yarn.lock +2704 -767
  144. metadata +111 -41
  145. data/package/babel/preset.js +0 -48
  146. data/package/config.js +0 -56
  147. data/package/env.js +0 -48
  148. data/package/environments/base.js +0 -171
  149. data/package/environments/development.js +0 -13
  150. data/package/environments/production.js +0 -88
  151. data/package/environments/test.js +0 -3
  152. data/package/esbuild/index.js +0 -40
  153. data/package/index.js +0 -40
  154. data/package/rules/css.js +0 -3
  155. data/package/rules/esbuild.js +0 -10
  156. data/package/rules/file.js +0 -29
  157. data/package/rules/raw.js +0 -5
  158. data/package/rules/swc.js +0 -10
  159. data/package/swc/index.js +0 -50
  160. data/package/utils/configPath.js +0 -4
  161. data/package/utils/defaultConfigPath.js +0 -2
  162. data/package/utils/getStyleRule.js +0 -40
  163. data/package/utils/helpers.js +0 -62
  164. data/package/utils/snakeToCamelCase.js +0 -5
  165. data/package/webpackDevServerConfig.js +0 -71
  166. data/test/package/rules/index.test.js +0 -16
@@ -0,0 +1,512 @@
1
+ # CSS Modules Export Mode
2
+
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
+ ### Important: exportLocalsConvention with namedExport
36
+
37
+ When `namedExport: true` is enabled (v9 default), css-loader requires `exportLocalsConvention` to be either `'camelCaseOnly'` or `'dashesOnly'`.
38
+
39
+ **The following will cause a build error:**
40
+ ```js
41
+ modules: {
42
+ namedExport: true,
43
+ exportLocalsConvention: 'camelCase' // ❌ ERROR: incompatible with namedExport: true
44
+ }
45
+ ```
46
+
47
+ **Error message:**
48
+ ```
49
+ "exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
50
+ ```
51
+
52
+ **Correct v9 configuration:**
53
+ ```js
54
+ modules: {
55
+ namedExport: true,
56
+ exportLocalsConvention: 'camelCaseOnly' // ✅ Correct - only camelCase exported
57
+ }
58
+ ```
59
+
60
+ **exportLocalsConvention options with namedExport:**
61
+
62
+ When `namedExport: true`, you can use:
63
+ - `'camelCaseOnly'` (v9 default): Exports ONLY the camelCase version (e.g., only `myButton`)
64
+ - `'dashesOnly'`: Exports ONLY the original kebab-case version (e.g., only `my-button`)
65
+
66
+ **Not compatible with namedExport: true:**
67
+ - `'camelCase'`: Exports both versions (both `my-button` and `myButton`) - only works with `namedExport: false` (v8 behavior)
68
+
69
+ **Configuration Quick Reference:**
70
+
71
+ | namedExport | exportLocalsConvention | `.my-button` exports | Use Case | Compatible? |
72
+ |-------------|------------------------|---------------------|----------|-------------|
73
+ | `true` | `'camelCaseOnly'` | `myButton` | JavaScript conventions | ✅ Valid |
74
+ | `true` | `'dashesOnly'` | `'my-button'` | Preserve CSS naming | ✅ Valid |
75
+ | `false` | `'camelCase'` | Both `myButton` AND `'my-button'` | v8 compatibility | ✅ Valid |
76
+ | `false` | `'asIs'` | `'my-button'` | No transformation | ✅ Valid |
77
+ | `true` | `'camelCase'` | - | - | ❌ Build Error |
78
+
79
+ **When to use each option:**
80
+ - Use `'camelCaseOnly'` if you prefer standard JavaScript naming conventions
81
+ - Use `'dashesOnly'` if you want to preserve your CSS class names exactly as written
82
+ - Use `'camelCase'` (with `namedExport: false`) only if you need both versions available
83
+
84
+ ## Version 8.x and Earlier Behavior
85
+
86
+ In Shakapacker v8 and earlier, the default behavior was to use a **default export object**:
87
+
88
+ ```js
89
+ // v8 and earlier default
90
+ import styles from './Foo.module.css';
91
+ <button className={styles.bright} />
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Migrating from v8 to v9
97
+
98
+ When upgrading to Shakapacker v9, you'll need to update your CSS Module imports from default exports to named exports.
99
+
100
+ ### Migration Options
101
+
102
+ #### Option 1: Update Your Code (Recommended)
103
+
104
+ **For JavaScript projects:**
105
+ ```js
106
+ // Before (v8)
107
+ import styles from './Component.module.css';
108
+ <div className={styles.container}>
109
+ <button className={styles.button}>Click me</button>
110
+ </div>
111
+
112
+ // After (v9) - JavaScript
113
+ import { container, button } from './Component.module.css';
114
+ <div className={container}>
115
+ <button className={button}>Click me</button>
116
+ </div>
117
+ ```
118
+
119
+ **For TypeScript projects:**
120
+ ```typescript
121
+ // Before (v8)
122
+ import styles from './Component.module.css';
123
+ <div className={styles.container}>
124
+ <button className={styles.button}>Click me</button>
125
+ </div>
126
+
127
+ // After (v9) - TypeScript
128
+ import * as styles from './Component.module.css';
129
+ <div className={styles.container}>
130
+ <button className={styles.button}>Click me</button>
131
+ </div>
132
+ ```
133
+
134
+ Note: TypeScript projects only need to change from default import to namespace import (`* as styles`), the property access remains the same.
135
+
136
+ #### Option 2: Keep v8 Behavior
137
+
138
+ If you prefer to keep the v8 default export behavior during migration, you can override the configuration (see below).
139
+
140
+ ---
141
+
142
+ ## Reverting to Default Exports (v8 Behavior)
143
+
144
+ To use the v8-style default exports instead of v9's named exports:
145
+
146
+ ### Option 1: Update `config/webpack/commonWebpackConfig.js` (Recommended)
147
+
148
+ This approach modifies the common webpack configuration that applies to all environments:
149
+
150
+ ```js
151
+ // config/webpack/commonWebpackConfig.js
152
+ const { generateWebpackConfig, merge } = require('shakapacker');
153
+
154
+ const baseClientWebpackConfig = generateWebpackConfig();
155
+
156
+ // Override CSS Modules configuration to use v8-style default exports
157
+ const overrideCssModulesConfig = (config) => {
158
+ // Find the CSS rule in the module rules
159
+ const cssRule = config.module.rules.find(rule =>
160
+ rule.test && rule.test.toString().includes('css')
161
+ );
162
+
163
+ if (cssRule && cssRule.use) {
164
+ const cssLoaderUse = cssRule.use.find(use =>
165
+ use.loader && use.loader.includes('css-loader')
166
+ );
167
+
168
+ if (cssLoaderUse && cssLoaderUse.options && cssLoaderUse.options.modules) {
169
+ // Override v9 default to use v8-style default exports
170
+ cssLoaderUse.options.modules.namedExport = false;
171
+ cssLoaderUse.options.modules.exportLocalsConvention = 'asIs';
172
+ }
173
+ }
174
+
175
+ return config;
176
+ };
177
+
178
+ const commonOptions = {
179
+ resolve: {
180
+ extensions: ['.css', '.ts', '.tsx'],
181
+ },
182
+ };
183
+
184
+ const commonWebpackConfig = () => {
185
+ const config = merge({}, baseClientWebpackConfig, commonOptions);
186
+ return overrideCssModulesConfig(config);
187
+ };
188
+
189
+ module.exports = commonWebpackConfig;
190
+ ```
191
+
192
+ ### Option 2: Create `config/webpack/environment.js` (Alternative)
193
+
194
+ If you prefer using a separate environment file:
195
+
196
+ ```js
197
+ // config/webpack/environment.js
198
+ const { environment } = require('@shakacode/shakapacker');
199
+ const getStyleRule = require('@shakacode/shakapacker/package/utils/getStyleRule');
200
+
201
+ // CSS Modules rule for *.module.css with v8-style default export
202
+ const cssModulesRule = getStyleRule(/\.module\.css$/i, [], {
203
+ sourceMap: true,
204
+ importLoaders: 2,
205
+ modules: {
206
+ auto: true,
207
+ namedExport: false, // <-- override v9 default
208
+ exportLocalsConvention: 'asIs' // keep class names as-is instead of camelCase
209
+ }
210
+ });
211
+
212
+ // Ensure this rule wins for *.module.css
213
+ if (cssModulesRule) {
214
+ environment.loaders.prepend('css-modules', cssModulesRule);
215
+ }
216
+
217
+ // Plain CSS rule for non-modules
218
+ const plainCssRule = getStyleRule(/(?<!\.module)\.css$/i, [], {
219
+ sourceMap: true,
220
+ importLoaders: 2,
221
+ modules: false
222
+ });
223
+
224
+ if (plainCssRule) {
225
+ environment.loaders.append('css', plainCssRule);
226
+ }
227
+
228
+ module.exports = environment;
229
+ ```
230
+
231
+ Then reference this in your environment-specific configs (development.js, production.js, etc.).
232
+
233
+ ### Option 3: (Optional) Sass Modules
234
+
235
+ If you also use Sass modules, add similar configuration for SCSS files:
236
+
237
+ ```js
238
+ // For Option 1 approach, extend the overrideCssModulesConfig function:
239
+ const overrideCssModulesConfig = (config) => {
240
+ // Handle both CSS and SCSS rules
241
+ const styleRules = config.module.rules.filter(rule =>
242
+ rule.test && (rule.test.toString().includes('css') || rule.test.toString().includes('scss'))
243
+ );
244
+
245
+ styleRules.forEach(rule => {
246
+ if (rule.use) {
247
+ const cssLoaderUse = rule.use.find(use =>
248
+ use.loader && use.loader.includes('css-loader')
249
+ );
250
+
251
+ if (cssLoaderUse && cssLoaderUse.options && cssLoaderUse.options.modules) {
252
+ cssLoaderUse.options.modules.namedExport = false;
253
+ cssLoaderUse.options.modules.exportLocalsConvention = 'asIs';
254
+ }
255
+ }
256
+ });
257
+
258
+ return config;
259
+ };
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Detailed Migration Guide
265
+
266
+ ### Migrating from v8 (Default Exports) to v9 (Named Exports)
267
+
268
+ #### 1. Update Import Statements
269
+
270
+ ```js
271
+ // Old (v8 - default export)
272
+ import styles from './Component.module.css';
273
+
274
+ // New (v9 - named exports)
275
+ import { bright, container, button } from './Component.module.css';
276
+ ```
277
+
278
+ #### 2. Update Class References
279
+
280
+ ```js
281
+ // Old (v8)
282
+ <div className={styles.container}>
283
+ <button className={styles.button}>Click me</button>
284
+ <span className={styles.bright}>Highlighted text</span>
285
+ </div>
286
+
287
+ // New (v9)
288
+ <div className={container}>
289
+ <button className={button}>Click me</button>
290
+ <span className={bright}>Highlighted text</span>
291
+ </div>
292
+ ```
293
+
294
+ #### 3. Handle Kebab-Case Class Names
295
+
296
+ **Option A: Use camelCase (v9 default)**
297
+
298
+ With `exportLocalsConvention: 'camelCaseOnly'`, kebab-case class names are automatically converted:
299
+
300
+ ```css
301
+ /* styles.module.css */
302
+ .my-button { ... }
303
+ .primary-color { ... }
304
+ ```
305
+
306
+ ```js
307
+ // v9 default - camelCase conversion
308
+ import { myButton, primaryColor } from './styles.module.css';
309
+ <button className={myButton} />
310
+ ```
311
+
312
+ **Option B: Keep kebab-case with 'dashesOnly'**
313
+
314
+ If you prefer to preserve the original kebab-case names, configure your webpack to use `'dashesOnly'`:
315
+
316
+ ```js
317
+ // config/webpack/commonWebpackConfig.js
318
+ modules: {
319
+ namedExport: true,
320
+ exportLocalsConvention: 'dashesOnly'
321
+ }
322
+ ```
323
+
324
+ ```js
325
+ // With dashesOnly - preserve kebab-case
326
+ import * as styles from './styles.module.css';
327
+ <button className={styles['my-button']} />
328
+
329
+ // Or with aliasing:
330
+ import { 'my-button': myButton } from './styles.module.css';
331
+ <button className={myButton} />
332
+ ```
333
+
334
+ **Note:** With both `'camelCaseOnly'` and `'dashesOnly'`, only one version of each class name is exported. The original kebab-case name is NOT available with `'camelCaseOnly'`, and the camelCase version is NOT available with `'dashesOnly'`.
335
+
336
+ #### 4. Using a Codemod for Large Codebases
337
+
338
+ For large codebases, you can create a codemod to automate the migration:
339
+
340
+ ```js
341
+ // css-modules-v9-migration.js
342
+ module.exports = function(fileInfo, api) {
343
+ const j = api.jscodeshift;
344
+ const root = j(fileInfo.source);
345
+
346
+ // Find CSS module imports
347
+ root.find(j.ImportDeclaration, {
348
+ source: { value: value => value.endsWith('.module.css') }
349
+ }).forEach(path => {
350
+ const defaultSpecifier = path.node.specifiers.find(
351
+ spec => spec.type === 'ImportDefaultSpecifier'
352
+ );
353
+
354
+ if (defaultSpecifier) {
355
+ // Convert default import to namespace import for analysis
356
+ // Then extract used properties and convert to named imports
357
+ // ... codemod implementation
358
+ }
359
+ });
360
+
361
+ return root.toSource();
362
+ };
363
+ ```
364
+
365
+ Run with:
366
+ ```bash
367
+ npx jscodeshift -t css-modules-v9-migration.js src/
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Version Comparison
373
+
374
+ | Feature | v8 (and earlier) | v9 |
375
+ |---------|-----------------|----|
376
+ | Default behavior | Default export object | Named exports |
377
+ | Import syntax | `import styles from '...'` | `import { className } from '...'` |
378
+ | Class reference | `styles.className` | `className` |
379
+ | Export convention | `asIs` (no transformation) | `camelCaseOnly` |
380
+ | TypeScript warnings | May show warnings | No warnings |
381
+ | Tree-shaking | Limited | Optimized |
382
+
383
+ ---
384
+
385
+ ## Benefits of Named Exports (v9 Default)
386
+
387
+ 1. **No Build Warnings**: Eliminates webpack/TypeScript warnings about missing exports
388
+ 2. **Better Tree-Shaking**: Unused CSS class exports can be eliminated
389
+ 3. **Explicit Dependencies**: Clear about which CSS classes are being used
390
+ 4. **Modern Standards**: Aligns with ES modules and modern tooling
391
+ 5. **Type Safety**: TypeScript can validate individual class imports
392
+
393
+ ## Benefits of Default Exports (v8 Behavior)
394
+
395
+ 1. **Familiar Pattern**: Matches most existing React tutorials
396
+ 2. **Namespace Import**: All classes available under one import
397
+ 3. **Less Verbose**: Single import for all classes
398
+ 4. **Legacy Compatibility**: Works with existing codebases
399
+
400
+ ---
401
+
402
+ ## Verifying the Configuration
403
+
404
+ ### 1. Rebuild Your Packs
405
+
406
+ After making any configuration changes, rebuild your webpack bundles:
407
+
408
+ ```bash
409
+ # For development
410
+ NODE_ENV=development bin/shakapacker
411
+
412
+ # Or with the dev server
413
+ bin/shakapacker-dev-server
414
+ ```
415
+
416
+ ### 2. Test in Your React Component
417
+
418
+ Verify your imports work correctly:
419
+
420
+ ```js
421
+ // v9 default (named exports)
422
+ import { bright } from './Foo.module.css';
423
+ console.log(bright); // 'Foo_bright__hash'
424
+
425
+ // Or if using v8 configuration (default export)
426
+ import styles from './Foo.module.css';
427
+ console.log(styles); // { bright: 'Foo_bright__hash' }
428
+ ```
429
+
430
+ ### 3. Debug Webpack Configuration (Optional)
431
+
432
+ To inspect the final webpack configuration:
433
+
434
+ ```bash
435
+ NODE_ENV=development bin/shakapacker --profile --json > /tmp/webpack-stats.json
436
+ ```
437
+
438
+ Then search for `css-loader` options in the generated JSON file.
439
+
440
+ ---
441
+
442
+ ## Troubleshooting
443
+
444
+ ### Build Error: exportLocalsConvention Incompatible with namedExport
445
+
446
+ If you see this error during build:
447
+ ```
448
+ "exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
449
+ ```
450
+
451
+ **Cause:** Your webpack configuration has `namedExport: true` with `exportLocalsConvention: 'camelCase'`.
452
+
453
+ **Solution:** Change `exportLocalsConvention` to `'camelCaseOnly'` or `'dashesOnly'`:
454
+
455
+ ```js
456
+ // config/webpack/commonWebpackConfig.js or similar
457
+ modules: {
458
+ namedExport: true,
459
+ exportLocalsConvention: 'camelCaseOnly' // or 'dashesOnly'
460
+ }
461
+ ```
462
+
463
+ Alternatively, if you need the `'camelCase'` option (both original and camelCase exports), you must revert to v8 behavior by setting `namedExport: false` as shown in the "Reverting to Default Exports" section above.
464
+
465
+ ### CSS Classes Not Applying
466
+
467
+ If your CSS classes aren't applying after the upgrade:
468
+
469
+ 1. **Check import syntax**: Ensure you're using the correct import style for your configuration
470
+ 2. **Verify class names**: Use `console.log` to see available classes
471
+ 3. **Check camelCase conversion**: Kebab-case names are converted to camelCase in v9 with `'camelCaseOnly'`
472
+ 4. **Rebuild webpack**: Clear cache and rebuild: `rm -rf tmp/cache && bin/shakapacker`
473
+
474
+ ### TypeScript Support
475
+
476
+ #### For v9 (Named Exports)
477
+
478
+ ```typescript
479
+ // src/types/css-modules.d.ts
480
+ declare module '*.module.css' {
481
+ const classes: { [key: string]: string };
482
+ export = classes;
483
+ }
484
+ ```
485
+
486
+ #### For v8 Behavior (Default Export)
487
+
488
+ ```typescript
489
+ // src/types/css-modules.d.ts
490
+ declare module '*.module.css' {
491
+ const classes: { [key: string]: string };
492
+ export default classes;
493
+ }
494
+ ```
495
+
496
+ ### Build Performance
497
+
498
+ The configuration changes should not impact build performance significantly. If you experience issues:
499
+
500
+ 1. Check webpack stats: `bin/shakapacker --profile`
501
+ 2. Verify only necessary rules are being modified
502
+ 3. Consider using webpack bundle analyzer for deeper insights
503
+
504
+ ---
505
+
506
+ ## Summary
507
+
508
+ - **v9 default**: Named exports with camelCase conversion
509
+ - **v8 default**: Default export object with no conversion
510
+ - **Migration path**: Update imports or override configuration
511
+ - **Benefits of v9**: No warnings, better tree-shaking, explicit dependencies
512
+ - **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