shakapacker 8.4.0 → 9.7.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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/address-review.md +206 -0
  3. data/.claude/commands/update-changelog.md +354 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
  6. data/.github/STATUS.md +1 -0
  7. data/.github/actionlint-matcher.json +17 -0
  8. data/.github/workflows/claude-code-review.yml +45 -0
  9. data/.github/workflows/claude.yml +55 -0
  10. data/.github/workflows/dummy.yml +18 -5
  11. data/.github/workflows/eslint-validation.yml +46 -0
  12. data/.github/workflows/generator.yml +38 -22
  13. data/.github/workflows/node.yml +116 -2
  14. data/.github/workflows/ruby.yml +57 -15
  15. data/.github/workflows/test-bundlers.yml +180 -0
  16. data/.gitignore +27 -0
  17. data/.husky/pre-commit +2 -0
  18. data/.npmignore +56 -0
  19. data/.prettierignore +7 -0
  20. data/.rubocop.yml +2 -0
  21. data/.yalcignore +26 -0
  22. data/CHANGELOG.md +487 -19
  23. data/CLAUDE.md +63 -0
  24. data/CONTRIBUTING.md +268 -21
  25. data/ESLINT_TECHNICAL_DEBT.md +165 -0
  26. data/README.md +497 -137
  27. data/Rakefile +44 -4
  28. data/TODO.md +58 -0
  29. data/TODO_v9.md +97 -0
  30. data/bin/conductor-exec +24 -0
  31. data/bin/shakapacker-config +11 -0
  32. data/conductor-setup.sh +147 -0
  33. data/conductor.json +9 -0
  34. data/docs/api-reference.md +519 -0
  35. data/docs/cdn_setup.md +384 -0
  36. data/docs/common-upgrades.md +695 -0
  37. data/docs/configuration.md +845 -0
  38. data/docs/css-modules-export-mode.md +566 -0
  39. data/docs/customizing_babel_config.md +16 -16
  40. data/docs/deployment.md +78 -7
  41. data/docs/developing_shakapacker.md +6 -0
  42. data/docs/early_hints.md +433 -0
  43. data/docs/early_hints_manual_api.md +454 -0
  44. data/docs/feature_testing.md +492 -0
  45. data/docs/node_package_api.md +70 -0
  46. data/docs/optional-peer-dependencies.md +203 -0
  47. data/docs/peer-dependencies.md +71 -0
  48. data/docs/precompile_hook.md +486 -0
  49. data/docs/preventing_fouc.md +132 -0
  50. data/docs/react.md +58 -48
  51. data/docs/releasing.md +288 -0
  52. data/docs/rspack.md +218 -0
  53. data/docs/rspack_migration_guide.md +862 -0
  54. data/docs/sprockets.md +1 -0
  55. data/docs/style_loader_vs_mini_css.md +12 -12
  56. data/docs/subresource_integrity.md +13 -7
  57. data/docs/transpiler-migration.md +212 -0
  58. data/docs/transpiler-performance.md +200 -0
  59. data/docs/troubleshooting.md +272 -24
  60. data/docs/typescript-migration.md +388 -0
  61. data/docs/typescript.md +103 -0
  62. data/docs/using_esbuild_loader.md +12 -12
  63. data/docs/using_swc_loader.md +121 -16
  64. data/docs/v6_upgrade.md +42 -19
  65. data/docs/v7_upgrade.md +8 -6
  66. data/docs/v8_upgrade.md +13 -12
  67. data/docs/v9_upgrade.md +616 -0
  68. data/eslint.config.fast.js +254 -0
  69. data/eslint.config.js +309 -0
  70. data/jest.config.js +8 -1
  71. data/knip.ts +61 -0
  72. data/lib/install/bin/shakapacker +4 -6
  73. data/lib/install/bin/shakapacker-config +11 -0
  74. data/lib/install/bin/shakapacker-dev-server +1 -1
  75. data/lib/install/binstubs.rb +6 -2
  76. data/lib/install/config/rspack/rspack.config.js +6 -0
  77. data/lib/install/config/rspack/rspack.config.ts +7 -0
  78. data/lib/install/config/shakapacker.yml +75 -12
  79. data/lib/install/config/webpack/webpack.config.ts +7 -0
  80. data/lib/install/package.json +38 -0
  81. data/lib/install/template.rb +207 -45
  82. data/lib/shakapacker/build_config_loader.rb +147 -0
  83. data/lib/shakapacker/bundler_switcher.rb +415 -0
  84. data/lib/shakapacker/compiler.rb +87 -0
  85. data/lib/shakapacker/configuration.rb +475 -6
  86. data/lib/shakapacker/dev_server.rb +88 -1
  87. data/lib/shakapacker/dev_server_runner.rb +240 -6
  88. data/lib/shakapacker/doctor.rb +1191 -0
  89. data/lib/shakapacker/env.rb +19 -3
  90. data/lib/shakapacker/helper.rb +411 -14
  91. data/lib/shakapacker/install/env.rb +33 -0
  92. data/lib/shakapacker/instance.rb +93 -4
  93. data/lib/shakapacker/manifest.rb +167 -30
  94. data/lib/shakapacker/railtie.rb +4 -0
  95. data/lib/shakapacker/rspack_runner.rb +19 -0
  96. data/lib/shakapacker/runner.rb +668 -9
  97. data/lib/shakapacker/swc_migrator.rb +384 -0
  98. data/lib/shakapacker/utils/manager.rb +2 -0
  99. data/lib/shakapacker/utils/version_syntax_converter.rb +1 -1
  100. data/lib/shakapacker/version.rb +1 -1
  101. data/lib/shakapacker/version_checker.rb +1 -1
  102. data/lib/shakapacker/webpack_runner.rb +4 -42
  103. data/lib/shakapacker.rb +159 -1
  104. data/lib/tasks/shakapacker/binstubs.rake +4 -2
  105. data/lib/tasks/shakapacker/check_binstubs.rake +2 -2
  106. data/lib/tasks/shakapacker/doctor.rake +48 -0
  107. data/lib/tasks/shakapacker/export_bundler_config.rake +68 -0
  108. data/lib/tasks/shakapacker/install.rake +16 -4
  109. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  110. data/lib/tasks/shakapacker/switch_bundler.rake +72 -0
  111. data/lib/tasks/shakapacker.rake +2 -0
  112. data/package/.npmignore +4 -0
  113. data/package/babel/preset.ts +59 -0
  114. data/package/config.ts +189 -0
  115. data/package/configExporter/buildValidator.ts +906 -0
  116. data/package/configExporter/cli.ts +1748 -0
  117. data/package/configExporter/configDocs.ts +102 -0
  118. data/package/configExporter/configFile.ts +663 -0
  119. data/package/configExporter/fileWriter.ts +112 -0
  120. data/package/configExporter/index.ts +15 -0
  121. data/package/configExporter/types.ts +159 -0
  122. data/package/configExporter/yamlSerializer.ts +391 -0
  123. data/package/dev_server.ts +27 -0
  124. data/package/env.ts +92 -0
  125. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +36 -0
  126. data/package/environments/base.ts +147 -0
  127. data/package/environments/development.ts +88 -0
  128. data/package/environments/production.ts +82 -0
  129. data/package/environments/test.ts +55 -0
  130. data/package/environments/types.ts +98 -0
  131. data/package/esbuild/index.ts +40 -0
  132. data/package/index.d.ts +68 -93
  133. data/package/index.d.ts.template +72 -0
  134. data/package/index.ts +104 -0
  135. data/package/loaders.d.ts +28 -0
  136. data/package/optimization/rspack.ts +36 -0
  137. data/package/optimization/webpack.ts +55 -0
  138. data/package/plugins/envFilter.ts +82 -0
  139. data/package/plugins/rspack.ts +119 -0
  140. data/package/plugins/webpack.ts +82 -0
  141. data/package/rspack/index.ts +91 -0
  142. data/package/rules/{babel.js → babel.ts} +2 -2
  143. data/package/rules/{coffee.js → coffee.ts} +1 -1
  144. data/package/rules/css.ts +3 -0
  145. data/package/rules/{erb.js → erb.ts} +1 -1
  146. data/package/rules/esbuild.ts +10 -0
  147. data/package/rules/file.ts +41 -0
  148. data/package/rules/{jscommon.js → jscommon.ts} +5 -4
  149. data/package/rules/{less.js → less.ts} +4 -4
  150. data/package/rules/raw.ts +28 -0
  151. data/package/rules/rspack.ts +174 -0
  152. data/package/rules/sass.ts +21 -0
  153. data/package/rules/{stylus.js → stylus.ts} +4 -8
  154. data/package/rules/swc.ts +10 -0
  155. data/package/rules/{index.js → webpack.ts} +1 -2
  156. data/package/swc/index.ts +54 -0
  157. data/package/types/README.md +90 -0
  158. data/package/types/index.ts +69 -0
  159. data/package/types.ts +105 -0
  160. data/package/utils/bundlerUtils.ts +232 -0
  161. data/package/utils/configPath.ts +6 -0
  162. data/package/utils/debug.ts +45 -0
  163. data/package/utils/defaultConfigPath.ts +7 -0
  164. data/package/utils/ensureManifestExists.ts +17 -0
  165. data/package/utils/errorCodes.ts +249 -0
  166. data/package/utils/errorHelpers.ts +152 -0
  167. data/package/utils/getStyleRule.ts +75 -0
  168. data/package/utils/helpers.ts +99 -0
  169. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  170. data/package/utils/pathValidation.ts +207 -0
  171. data/package/utils/requireOrError.ts +24 -0
  172. data/package/utils/snakeToCamelCase.ts +5 -0
  173. data/package/utils/typeGuards.ts +388 -0
  174. data/package/utils/validateDependencies.ts +61 -0
  175. data/package/webpack-types.d.ts +33 -0
  176. data/package/webpackDevServerConfig.ts +130 -0
  177. data/package.json +157 -18
  178. data/scripts/remove-use-strict.js +44 -0
  179. data/scripts/type-check-no-emit.js +27 -0
  180. data/shakapacker.gemspec +4 -2
  181. data/sig/shakapacker/commands.rbs +35 -0
  182. data/sig/shakapacker/compiler.rbs +65 -0
  183. data/sig/shakapacker/compiler_strategy.rbs +41 -0
  184. data/sig/shakapacker/configuration.rbs +140 -0
  185. data/sig/shakapacker/dev_server.rbs +56 -0
  186. data/sig/shakapacker/env.rbs +25 -0
  187. data/sig/shakapacker/helper.rbs +98 -0
  188. data/sig/shakapacker/instance.rbs +46 -0
  189. data/sig/shakapacker/manifest.rbs +69 -0
  190. data/sig/shakapacker/version.rbs +4 -0
  191. data/sig/shakapacker.rbs +66 -0
  192. data/test/configExporter/buildValidator.test.js +1295 -0
  193. data/test/configExporter/configFile.test.js +393 -0
  194. data/test/configExporter/integration.test.js +262 -0
  195. data/test/helpers.js +1 -1
  196. data/test/package/bundlerUtils.rspack.test.js +145 -0
  197. data/test/package/bundlerUtils.test.js +97 -0
  198. data/test/package/config.test.js +14 -0
  199. data/test/package/configExporter/cli.test.js +440 -0
  200. data/test/package/configExporter/types.test.js +163 -0
  201. data/test/package/configExporter.test.js +491 -0
  202. data/test/package/env.test.js +42 -7
  203. data/test/package/environments/base.test.js +14 -4
  204. data/test/package/helpers.test.js +2 -2
  205. data/test/package/plugins/envFiltering.test.js +453 -0
  206. data/test/package/plugins/webpackSubresourceIntegrity.test.js +89 -0
  207. data/test/package/rspack/index.test.js +293 -0
  208. data/test/package/rspack/optimization.test.js +86 -0
  209. data/test/package/rspack/plugins.test.js +185 -0
  210. data/test/package/rspack/rules.test.js +229 -0
  211. data/test/package/rules/babel.test.js +65 -38
  212. data/test/package/rules/esbuild.test.js +13 -4
  213. data/test/package/rules/file.test.js +7 -1
  214. data/test/package/rules/raw.test.js +40 -7
  215. data/test/package/rules/sass-version-parsing.test.js +71 -0
  216. data/test/package/rules/sass.test.js +11 -6
  217. data/test/package/rules/sass1.test.js +8 -5
  218. data/test/package/rules/sass16.test.js +24 -0
  219. data/test/package/rules/swc.test.js +50 -39
  220. data/test/package/rules/webpack.test.js +35 -0
  221. data/test/package/staging.test.js +4 -3
  222. data/test/package/transpiler-defaults.test.js +169 -0
  223. data/test/package/utils/ensureManifestExists.test.js +51 -0
  224. data/test/package/yamlSerializer.test.js +204 -0
  225. data/test/peer-dependencies.sh +85 -0
  226. data/test/resolver.js +34 -3
  227. data/test/scripts/remove-use-strict.test.js +125 -0
  228. data/test/typescript/build.test.js +118 -0
  229. data/test/typescript/environments.test.js +107 -0
  230. data/test/typescript/pathValidation.test.js +186 -0
  231. data/test/typescript/requireOrError.test.js +49 -0
  232. data/test/typescript/securityValidation.test.js +182 -0
  233. data/tools/README.md +134 -0
  234. data/tools/css-modules-v9-codemod.js +179 -0
  235. data/tsconfig.eslint.json +9 -0
  236. data/tsconfig.json +38 -0
  237. data/yarn.lock +3202 -1097
  238. metadata +212 -44
  239. data/.eslintignore +0 -4
  240. data/.eslintrc.js +0 -36
  241. data/Gemfile.lock +0 -251
  242. data/package/babel/preset.js +0 -48
  243. data/package/config.js +0 -56
  244. data/package/dev_server.js +0 -23
  245. data/package/env.js +0 -48
  246. data/package/environments/base.js +0 -171
  247. data/package/environments/development.js +0 -13
  248. data/package/environments/production.js +0 -88
  249. data/package/environments/test.js +0 -3
  250. data/package/esbuild/index.js +0 -40
  251. data/package/index.js +0 -40
  252. data/package/rules/css.js +0 -3
  253. data/package/rules/esbuild.js +0 -10
  254. data/package/rules/file.js +0 -29
  255. data/package/rules/raw.js +0 -5
  256. data/package/rules/sass.js +0 -18
  257. data/package/rules/swc.js +0 -10
  258. data/package/swc/index.js +0 -50
  259. data/package/utils/configPath.js +0 -4
  260. data/package/utils/defaultConfigPath.js +0 -2
  261. data/package/utils/getStyleRule.js +0 -40
  262. data/package/utils/helpers.js +0 -62
  263. data/package/utils/snakeToCamelCase.js +0 -5
  264. data/package/webpackDevServerConfig.js +0 -71
  265. data/test/package/rules/index.test.js +0 -16
@@ -0,0 +1,616 @@
1
+ # Shakapacker v9 Upgrade Guide
2
+
3
+ This guide outlines new features, breaking changes, and migration steps for upgrading from Shakapacker v8 to v9.
4
+
5
+ **📖 For detailed configuration options, see the [Configuration Guide](./configuration.md)**
6
+
7
+ > **⚠️ Important:** Shakapacker is both a Ruby gem AND an npm package. **You must update BOTH**:
8
+ >
9
+ > - Update the version in `Gemfile`
10
+ > - Update the version in `package.json`
11
+ > - Run `bundle update shakapacker`
12
+ > - Run your package manager install command (`yarn install`, `npm install`, or `pnpm install`)
13
+ >
14
+ > See [Migration Steps](#migration-steps) below for detailed instructions including version format differences and testing.
15
+
16
+ > **⚠️ Important for v9.1.0 Users:** If you're upgrading to v9.1.0 or later, please note the [SWC Configuration Breaking Change](#swc-loose-mode-breaking-change-v910) below. This affects users who previously configured SWC in v9.0.0.
17
+
18
+ ## New Features
19
+
20
+ ### TypeScript Support
21
+
22
+ Shakapacker v9 includes TypeScript definitions for better IDE support and type safety.
23
+
24
+ - **No breaking changes** - JavaScript configs continue to work
25
+ - **Optional** - Use TypeScript only if you want it
26
+ - **Type safety** - Catch configuration errors at compile-time
27
+ - **IDE support** - Full autocomplete for all options
28
+
29
+ See the [TypeScript Documentation](./typescript.md) for usage examples.
30
+
31
+ ### NODE_ENV Default Behavior Fixed
32
+
33
+ **What changed:** NODE_ENV now intelligently defaults based on RAILS_ENV instead of always defaulting to "production".
34
+
35
+ **New behavior:**
36
+
37
+ - When `RAILS_ENV=production` → `NODE_ENV` defaults to `"production"`
38
+ - When `RAILS_ENV=development` or unset → `NODE_ENV` defaults to `"development"`
39
+ - When `RAILS_ENV` is any other value (test, staging, etc.) → `NODE_ENV` defaults to `"development"`
40
+
41
+ **Benefits:**
42
+
43
+ - **Dev server "just works"** - No need to explicitly set NODE_ENV when running the development server
44
+ - **Correct configuration loaded** - Development server now properly loads the development configuration from shakapacker.yml
45
+ - **Fixes port issues** - Dev server uses the configured port (e.g., 3035) instead of defaulting to 8080
46
+ - **Fixes 404 errors** - Assets load correctly without requiring manual NODE_ENV configuration
47
+
48
+ **No action required** - This change improves the default behavior and requires no migration.
49
+
50
+ **If you previously worked around this bug**, you can now remove these workarounds:
51
+
52
+ - Remove `NODE_ENV=development` from your `.env`, `.env.development`, or `.env.local` files
53
+ - Remove `NODE_ENV=development` from your `docker-compose.yml` or Dockerfile
54
+ - Remove custom scripts that set NODE_ENV before running the dev server
55
+ - Remove `NODE_ENV=development` from your `bin/dev` or Procfile.dev
56
+
57
+ ## SWC Loose Mode Breaking Change (v9.1.0)
58
+
59
+ > **⚠️ This breaking change was introduced in v9.1.0.** If you're upgrading from v9.0.0, pay special attention to this section.
60
+
61
+ **What changed:** SWC default configuration now uses `loose: false` instead of `loose: true`.
62
+
63
+ **Why:** The previous default of `loose: true` caused:
64
+
65
+ - **Silent failures with Stimulus controllers** - Controllers wouldn't register properly
66
+ - **Incorrect behavior with spread operators** on iterables (e.g., `[...new Set()]`)
67
+ - **Deviation from SWC and Babel defaults** - Both tools default to `loose: false`
68
+
69
+ **Impact:**
70
+
71
+ - **Most projects:** No action needed. The new default is more correct and fixes bugs.
72
+ - **Stimulus users:** This fixes silent controller failures you may have experienced.
73
+ - **Projects relying on loose mode behavior:** May need to explicitly configure `loose: true` (not recommended).
74
+
75
+ **When you might need the old behavior:**
76
+
77
+ - If you have code that breaks with spec-compliant transforms
78
+ - Note: `loose: true` provides slightly faster build times but generates less spec-compliant code
79
+
80
+ **How to restore old behavior (not recommended):**
81
+
82
+ Create or update `config/swc.config.js`:
83
+
84
+ ```javascript
85
+ module.exports = {
86
+ options: {
87
+ jsc: {
88
+ // Only use this if you have code that requires loose transforms.
89
+ // This provides slightly faster build performance but may cause runtime bugs.
90
+ loose: true // Restore v9.0.0 behavior
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ **Better solution:** Fix your code to work with spec-compliant transforms. The `loose: false` default aligns with both SWC and Babel standards and prevents subtle bugs.
97
+
98
+ **Using Stimulus?** The new default includes `keepClassNames: true` to prevent SWC from mangling class names. If you use `rake shakapacker:migrate_to_swc`, this is configured automatically. See [Using SWC with Stimulus](./using_swc_loader.md#using-swc-with-stimulus) for details.
99
+
100
+ ## Breaking Changes
101
+
102
+ ### 1. CSS Modules Configuration Changed to Named Exports
103
+
104
+ **What changed:** CSS Modules are now configured with `namedExport: true` and `exportLocalsConvention: 'camelCaseOnly'` by default, aligning with Next.js and modern tooling standards.
105
+
106
+ > **Important:** When `namedExport: true` is enabled, css-loader requires `exportLocalsConvention` to be either `'camelCaseOnly'` or `'dashesOnly'`. Using `'camelCase'` will cause a build error: `"exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option`.
107
+
108
+ **Quick Reference: Configuration Options**
109
+
110
+ | Configuration | namedExport | exportLocalsConvention | CSS: `.my-button` | Export Available | Works With |
111
+ | --------------- | ----------- | ---------------------- | ----------------- | --------------------------------- | ----------------- |
112
+ | **v9 Default** | `true` | `'camelCaseOnly'` | `.my-button` | `myButton` only | ✅ Named exports |
113
+ | **Alternative** | `true` | `'dashesOnly'` | `.my-button` | `'my-button'` only | ✅ Named exports |
114
+ | **v8 Style** | `false` | `'camelCase'` | `.my-button` | Both `myButton` AND `'my-button'` | ✅ Default export |
115
+ | **❌ Invalid** | `true` | `'camelCase'` | - | - | ❌ Build Error |
116
+
117
+ **JavaScript Projects:**
118
+
119
+ ```js
120
+ // Before (v8)
121
+ import styles from "./Component.module.css"
122
+ ;<button className={styles.button} />
123
+
124
+ // After (v9)
125
+ import { button } from "./Component.module.css"
126
+ ;<button className={button} />
127
+ ```
128
+
129
+ **TypeScript Projects:**
130
+
131
+ ```typescript
132
+ // Before (v8)
133
+ import styles from './Component.module.css';
134
+ <button className={styles.button} />
135
+
136
+ // After (v9) - namespace import due to TypeScript limitations
137
+ import * as styles from './Component.module.css';
138
+ <button className={styles.button} />
139
+ ```
140
+
141
+ **Migration Options:**
142
+
143
+ 1. **Update your code** (Recommended):
144
+ - JavaScript: Change to named imports (`import { className }`)
145
+ - TypeScript: Change to namespace imports (`import * as styles`)
146
+ - Kebab-case class names are automatically converted to camelCase
147
+
148
+ 2. **Keep v8 behavior** temporarily using configuration file (Easiest):
149
+
150
+ ```yaml
151
+ # config/shakapacker.yml
152
+ css_modules_export_mode: default
153
+ ```
154
+
155
+ This allows you to keep using default imports while migrating gradually
156
+
157
+ 3. **Keep v8 behavior** using webpack configuration (Advanced):
158
+
159
+ If you need more control over the configuration, you can override the css-loader settings directly in your webpack config.
160
+
161
+ **Where to add this:** Create or modify `config/webpack/webpack.config.js`. If this file doesn't exist, create it and ensure your `config/webpacker.yml` or build process loads it. See [Webpack Configuration](./webpack.md) for details on customizing webpack.
162
+
163
+ ```javascript
164
+ // config/webpack/webpack.config.js
165
+ const { generateWebpackConfig, merge } = require("shakapacker")
166
+
167
+ const customConfig = () => {
168
+ const config = merge({}, generateWebpackConfig())
169
+
170
+ // Override CSS Modules to use default exports (v8 behavior)
171
+ config.module.rules.forEach((rule) => {
172
+ if (
173
+ rule.test &&
174
+ (rule.test.test("example.module.scss") ||
175
+ rule.test.test("example.module.css"))
176
+ ) {
177
+ if (Array.isArray(rule.use)) {
178
+ rule.use.forEach((loader) => {
179
+ if (
180
+ loader.loader &&
181
+ loader.loader.includes("css-loader") &&
182
+ loader.options &&
183
+ loader.options.modules
184
+ ) {
185
+ // Disable named exports to support default imports
186
+ loader.options.modules.namedExport = false
187
+ loader.options.modules.exportLocalsConvention = "camelCase"
188
+ }
189
+ })
190
+ }
191
+ }
192
+ })
193
+
194
+ return config
195
+ }
196
+
197
+ module.exports = customConfig
198
+ ```
199
+
200
+ **Key points:**
201
+ - Test both `.module.scss` and `.module.css` file extensions
202
+ - Validate all loader properties exist before accessing them
203
+ - Use `.includes('css-loader')` since the loader path may vary
204
+ - Set both `namedExport: false` and `exportLocalsConvention: 'camelCase'` for full v8 compatibility
205
+
206
+ **Debugging tip:** To verify the override is applied correctly, you can inspect the webpack config:
207
+
208
+ ```javascript
209
+ // Add this temporarily to verify your changes
210
+ if (process.env.DEBUG_WEBPACK_CONFIG) {
211
+ const cssModuleRules = config.module.rules
212
+ .filter((r) => r.test?.test?.("example.module.css"))
213
+ .flatMap((r) => r.use?.filter((l) => l.loader?.includes("css-loader")))
214
+ console.log(
215
+ "CSS Module loader options:",
216
+ JSON.stringify(cssModuleRules, null, 2)
217
+ )
218
+ }
219
+ ```
220
+
221
+ Then run: `DEBUG_WEBPACK_CONFIG=true bin/shakapacker`
222
+
223
+ **Note:** This is a temporary solution. The recommended approach is to migrate your imports to use named exports as shown in the main documentation.
224
+
225
+ For detailed configuration options, see the [CSS Modules Export Mode documentation](./css-modules-export-mode.md)
226
+
227
+ **Benefits of the change:**
228
+
229
+ - Eliminates webpack/TypeScript warnings
230
+ - Better tree-shaking of unused CSS classes
231
+ - More explicit about which classes are used
232
+ - Aligns with modern JavaScript standards
233
+
234
+ ### 2. Configuration Option Renamed: `webpack_loader` → `javascript_transpiler`
235
+
236
+ **What changed:** The configuration option has been renamed to better reflect its purpose.
237
+
238
+ **Before (v8):**
239
+
240
+ ```yml
241
+ # config/shakapacker.yml
242
+ webpack_loader: "babel"
243
+ ```
244
+
245
+ **After (v9):**
246
+
247
+ ```yml
248
+ # config/shakapacker.yml
249
+ javascript_transpiler: "babel"
250
+ ```
251
+
252
+ **Note:** The old `webpack_loader` option is deprecated but still supported with a warning.
253
+
254
+ ### 3. SWC is Now the Default JavaScript Transpiler
255
+
256
+ **What changed:** SWC replaces Babel as the default JavaScript transpiler. Babel is no longer included in peer dependencies.
257
+
258
+ **Why:** SWC is 20x faster than Babel while maintaining compatibility with most JavaScript and TypeScript code.
259
+
260
+ **Impact on existing projects:**
261
+
262
+ - Your project will continue using Babel if you already have babel packages in package.json
263
+ - To switch to SWC for better performance, see migration options below
264
+
265
+ **Impact on new projects:**
266
+
267
+ - New installations will use SWC by default
268
+ - Babel dependencies won't be installed unless explicitly configured
269
+
270
+ ### Migration Options
271
+
272
+ #### Option 1 (Recommended): Switch to SWC
273
+
274
+ ```yml
275
+ # config/shakapacker.yml
276
+ javascript_transpiler: "swc"
277
+ ```
278
+
279
+ Then install SWC:
280
+
281
+ ```bash
282
+ npm install @swc/core swc-loader
283
+ ```
284
+
285
+ #### Option 2: Keep using Babel
286
+
287
+ ```yml
288
+ # config/shakapacker.yml
289
+ javascript_transpiler: "babel"
290
+ ```
291
+
292
+ No other changes needed - your existing babel packages will continue to work.
293
+
294
+ #### Option 3: Use esbuild
295
+
296
+ ```yml
297
+ # config/shakapacker.yml
298
+ javascript_transpiler: "esbuild"
299
+ ```
300
+
301
+ Then install esbuild:
302
+
303
+ ```bash
304
+ npm install esbuild esbuild-loader
305
+ ```
306
+
307
+ ### 4. Rspack Support Added
308
+
309
+ **New feature:** Shakapacker v9 adds support for Rspack as an alternative bundler to webpack.
310
+
311
+ ```yml
312
+ # config/shakapacker.yml
313
+ assets_bundler: "rspack" # or 'webpack' (default)
314
+ ```
315
+
316
+ ### 5. All Peer Dependencies Now Optional
317
+
318
+ **What changed:** All peer dependencies are now marked as optional via `peerDependenciesMeta`.
319
+
320
+ **Benefits:**
321
+
322
+ - **No installation warnings** - You won't see peer dependency warnings for packages you don't use
323
+ - **Install only what you need** - Using webpack? Don't install rspack. Using SWC? Don't install Babel.
324
+ - **Clear version constraints** - When you do install a package, version compatibility is still enforced
325
+
326
+ **What this means for you:**
327
+
328
+ - **Existing projects:** No changes needed. Your existing dependencies will continue to work.
329
+ - **New projects:** The installer only adds the packages you actually need based on your configuration.
330
+ - **Package manager behavior:** npm, yarn, and pnpm will no longer warn about missing peer dependencies.
331
+
332
+ **Example:** If you're using SWC with webpack, you only need:
333
+
334
+ ```json
335
+ {
336
+ "dependencies": {
337
+ "shakapacker": "^9.0.0",
338
+ "@swc/core": "^1.3.0",
339
+ "swc-loader": "^0.2.0",
340
+ "webpack": "^5.76.0",
341
+ "webpack-cli": "^5.0.0",
342
+ "webpack-dev-server": "^5.0.0"
343
+ }
344
+ }
345
+ ```
346
+
347
+ You won't get warnings about missing Babel, Rspack, or esbuild packages.
348
+
349
+ ## Migration Steps
350
+
351
+ > **💡 Tip:** For general upgrade instructions applicable to all Shakapacker versions, see [Upgrading Shakapacker](./common-upgrades.md#upgrading-shakapacker) in the Common Upgrades guide.
352
+
353
+ ### Step 1: Update Gemfile
354
+
355
+ Update the shakapacker version in your `Gemfile`:
356
+
357
+ ```ruby
358
+ # Gemfile
359
+ gem "shakapacker", "9.3.0" # or latest version
360
+ ```
361
+
362
+ **Note:** Ruby gems use dot notation for pre-release versions (e.g., `9.3.0.beta.1`), while npm uses hyphen notation (e.g., `9.3.0-beta.1`). See [Version Format Differences](#version-format-differences) below.
363
+
364
+ ### Step 2: Update package.json
365
+
366
+ Update the shakapacker version in your `package.json`:
367
+
368
+ ```json
369
+ {
370
+ "dependencies": {
371
+ "shakapacker": "9.3.0"
372
+ }
373
+ }
374
+ ```
375
+
376
+ **Note:** For pre-release versions, npm uses hyphen notation (e.g., `"shakapacker": "9.3.0-beta.1"`).
377
+
378
+ ### Step 3: Install Updates
379
+
380
+ Run both bundler and your package manager:
381
+
382
+ ```bash
383
+ # Update Ruby gem
384
+ bundle update shakapacker
385
+
386
+ # Update npm package (choose one based on your package manager)
387
+ yarn install # if using Yarn
388
+ npm install # if using npm
389
+ pnpm install # if using pnpm
390
+ bun install # if using Bun
391
+ ```
392
+
393
+ ### Step 4: Update CSS Module Imports
394
+
395
+ #### For each CSS module import:
396
+
397
+ ```js
398
+ // Find imports like this:
399
+ import styles from "./styles.module.css"
400
+
401
+ // Replace with named imports:
402
+ import { className1, className2 } from "./styles.module.css"
403
+ ```
404
+
405
+ #### Update TypeScript definitions:
406
+
407
+ ```typescript
408
+ // Update your CSS module type definitions
409
+ declare module "*.module.css" {
410
+ // With namedExport: true, css-loader generates individual named exports
411
+ // TypeScript can't know the exact names at compile time, so we declare
412
+ // a module with any number of string exports
413
+ const classes: { readonly [key: string]: string }
414
+ export = classes
415
+ // Note: This allows 'import * as styles' but not 'import styles from'
416
+ // because css-loader with namedExport: true doesn't generate a default export
417
+ }
418
+ ```
419
+
420
+ ### Step 5: Handle Kebab-Case Class Names
421
+
422
+ v9 automatically converts kebab-case to camelCase with `exportLocalsConvention: 'camelCaseOnly'`:
423
+
424
+ ```css
425
+ /* styles.module.css */
426
+ .my-button {
427
+ }
428
+ .primary-color {
429
+ }
430
+ ```
431
+
432
+ ```js
433
+ // v9 default - camelCase conversion
434
+ import { myButton, primaryColor } from "./styles.module.css"
435
+ ```
436
+
437
+ **Alternative: Keep kebab-case names with 'dashesOnly'**
438
+
439
+ If you prefer to keep kebab-case names in JavaScript, you can override the configuration to use `'dashesOnly'`:
440
+
441
+ ```js
442
+ // config/webpack/commonWebpackConfig.js
443
+ modules: {
444
+ namedExport: true,
445
+ exportLocalsConvention: 'dashesOnly' // Keep original kebab-case names
446
+ }
447
+ ```
448
+
449
+ Then use the original kebab-case names in your imports:
450
+
451
+ ```js
452
+ // With dashesOnly configuration
453
+ import { 'my-button': myButton, 'primary-color': primaryColor } from './styles.module.css';
454
+ // or access as properties
455
+ import * as styles from './styles.module.css';
456
+ const buttonClass = styles['my-button'];
457
+ ```
458
+
459
+ **Note:** With `'camelCaseOnly'` (default) or `'dashesOnly'`, only one version is exported. If you need both the original and camelCase versions, you would need to use `'camelCase'` instead, but this requires `namedExport: false` (v8 behavior). See the [CSS Modules Export Mode documentation](./css-modules-export-mode.md) for details on reverting to v8 behavior.
460
+
461
+ ### Step 6: Update Configuration Files
462
+
463
+ If you have `webpack_loader` in your configuration:
464
+
465
+ ```yml
466
+ # config/shakapacker.yml
467
+ # OLD:
468
+ # webpack_loader: 'babel'
469
+
470
+ # NEW:
471
+ javascript_transpiler: "babel"
472
+ ```
473
+
474
+ ### Step 7: Run Tests
475
+
476
+ ```bash
477
+ # Run your test suite
478
+ npm test
479
+
480
+ # Build your application
481
+ bin/shakapacker
482
+
483
+ # Test in development
484
+ bin/shakapacker-dev-server
485
+ ```
486
+
487
+ ## Version Format Differences
488
+
489
+ Shakapacker version numbers differ between the Ruby gem and npm package for pre-release versions:
490
+
491
+ | Version Type | Ruby Gem (Gemfile) | npm Package (package.json) |
492
+ | ------------ | ------------------ | -------------------------- |
493
+ | Stable | `"9.3.0"` | `"9.3.0"` |
494
+ | Pre-release | `"9.3.0.beta.1"` | `"9.3.0-beta.1"` |
495
+
496
+ **Examples:**
497
+
498
+ ```ruby
499
+ # Gemfile - uses dots for pre-release versions
500
+ gem "shakapacker", "9.3.0" # stable
501
+ gem "shakapacker", "9.3.0.beta.1" # pre-release
502
+ ```
503
+
504
+ Stable version in package.json:
505
+
506
+ ```json
507
+ {
508
+ "dependencies": {
509
+ "shakapacker": "9.3.0"
510
+ }
511
+ }
512
+ ```
513
+
514
+ Pre-release version in package.json:
515
+
516
+ ```json
517
+ {
518
+ "dependencies": {
519
+ "shakapacker": "9.3.0-beta.1"
520
+ }
521
+ }
522
+ ```
523
+
524
+ This is due to different versioning conventions between RubyGems (which uses dots) and npm (which follows semantic versioning with hyphens for pre-release identifiers).
525
+
526
+ ## Troubleshooting
527
+
528
+ ### CSS Classes Not Applying
529
+
530
+ - Ensure you're using named imports: `import { className } from '...'`
531
+ - Check camelCase conversion for kebab-case names
532
+ - Clear cache: `rm -rf tmp/cache && bin/shakapacker`
533
+
534
+ ### TypeScript Errors
535
+
536
+ Update your global type definitions as shown in Step 2.
537
+
538
+ ### Build Warnings
539
+
540
+ If you see warnings about CSS module exports, ensure you've updated all imports to use named exports or have properly configured the override.
541
+
542
+ ### Build Error: exportLocalsConvention Incompatible with namedExport
543
+
544
+ If you see this error:
545
+
546
+ ```
547
+ "exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
548
+ ```
549
+
550
+ This means your webpack configuration has `namedExport: true` with `exportLocalsConvention: 'camelCase'`. The fix is to change to `'camelCaseOnly'` or `'dashesOnly'`:
551
+
552
+ ```js
553
+ // config/webpack/commonWebpackConfig.js or wherever you configure css-loader
554
+ modules: {
555
+ namedExport: true,
556
+ exportLocalsConvention: 'camelCaseOnly' // or 'dashesOnly'
557
+ }
558
+ ```
559
+
560
+ If you want to use `'camelCase'` (which exports both original and camelCase versions), you must set `namedExport: false` and revert to v8 behavior. See the [CSS Modules Export Mode documentation](./css-modules-export-mode.md) for details.
561
+
562
+ ### Unexpected Peer Dependency Warnings After Upgrade
563
+
564
+ If you experience unexpected peer dependency warnings after upgrading to v9, you may need to clear your package manager's cache and reinstall dependencies. This ensures the new optional peer dependency configuration takes effect properly.
565
+
566
+ **For npm:**
567
+
568
+ ```bash
569
+ rm -rf node_modules package-lock.json
570
+ npm install
571
+ ```
572
+
573
+ **For Yarn:**
574
+
575
+ ```bash
576
+ rm -rf node_modules yarn.lock
577
+ yarn install
578
+ ```
579
+
580
+ **For pnpm:**
581
+
582
+ ```bash
583
+ rm -rf node_modules pnpm-lock.yaml
584
+ pnpm install
585
+ ```
586
+
587
+ **For Bun:**
588
+
589
+ ```bash
590
+ rm -rf node_modules bun.lockb
591
+ bun install
592
+ ```
593
+
594
+ **When is this necessary?**
595
+
596
+ - If you see peer dependency warnings for packages you don't use (e.g., warnings about Babel when using SWC)
597
+ - If your package manager cached the old dependency resolution from v8
598
+ - After switching transpilers or bundlers (e.g., from Babel to SWC, or webpack to rspack)
599
+
600
+ **Note:** This is typically only needed once after the v8 → v9 upgrade. Subsequent installs will use the correct dependency resolution.
601
+
602
+ ## Need Help?
603
+
604
+ - See [CSS Modules Export Mode documentation](./css-modules-export-mode.md) for detailed configuration options
605
+ - Check the [CHANGELOG](../CHANGELOG.md) for all changes
606
+ - File issues at [GitHub Issues](https://github.com/shakacode/shakapacker/issues)
607
+
608
+ ## Gradual Migration Strategy
609
+
610
+ If you have a large codebase and need to migrate gradually:
611
+
612
+ 1. Override the CSS configuration to keep v8 behavior (see [documentation](./css-modules-export-mode.md))
613
+ 2. Migrate files incrementally
614
+ 3. Remove the override once migration is complete
615
+
616
+ This allows you to upgrade to v9 immediately while taking time to update your CSS module imports.