shakapacker 9.2.0 → 9.3.0.beta.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
  4. data/.github/workflows/claude-code-review.yml +4 -5
  5. data/.github/workflows/claude.yml +1 -2
  6. data/.github/workflows/dummy.yml +4 -4
  7. data/.github/workflows/generator.yml +9 -9
  8. data/.github/workflows/node.yml +11 -2
  9. data/.github/workflows/ruby.yml +16 -16
  10. data/.github/workflows/test-bundlers.yml +9 -9
  11. data/.gitignore +4 -0
  12. data/CHANGELOG.md +19 -4
  13. data/CLAUDE.md +6 -1
  14. data/CONTRIBUTING.md +0 -1
  15. data/Gemfile.lock +1 -1
  16. data/README.md +14 -14
  17. data/TODO.md +10 -2
  18. data/TODO_v9.md +13 -3
  19. data/bin/export-bundler-config +1 -1
  20. data/conductor-setup.sh +1 -1
  21. data/conductor.json +1 -1
  22. data/docs/cdn_setup.md +13 -8
  23. data/docs/common-upgrades.md +2 -1
  24. data/docs/configuration.md +630 -0
  25. data/docs/css-modules-export-mode.md +120 -100
  26. data/docs/customizing_babel_config.md +16 -16
  27. data/docs/deployment.md +18 -0
  28. data/docs/developing_shakapacker.md +6 -0
  29. data/docs/optional-peer-dependencies.md +9 -4
  30. data/docs/peer-dependencies.md +17 -6
  31. data/docs/precompile_hook.md +342 -0
  32. data/docs/react.md +57 -47
  33. data/docs/releasing.md +0 -2
  34. data/docs/rspack.md +25 -21
  35. data/docs/rspack_migration_guide.md +335 -8
  36. data/docs/sprockets.md +1 -0
  37. data/docs/style_loader_vs_mini_css.md +12 -12
  38. data/docs/subresource_integrity.md +13 -7
  39. data/docs/transpiler-performance.md +40 -19
  40. data/docs/troubleshooting.md +0 -2
  41. data/docs/typescript-migration.md +48 -39
  42. data/docs/typescript.md +12 -8
  43. data/docs/using_esbuild_loader.md +10 -10
  44. data/docs/v6_upgrade.md +33 -20
  45. data/docs/v7_upgrade.md +8 -6
  46. data/docs/v8_upgrade.md +13 -12
  47. data/docs/v9_upgrade.md +2 -1
  48. data/eslint.config.fast.js +134 -0
  49. data/eslint.config.js +140 -0
  50. data/knip.ts +54 -0
  51. data/lib/install/bin/export-bundler-config +1 -1
  52. data/lib/install/config/shakapacker.yml +16 -5
  53. data/lib/shakapacker/compiler.rb +80 -0
  54. data/lib/shakapacker/configuration.rb +33 -5
  55. data/lib/shakapacker/dev_server_runner.rb +140 -1
  56. data/lib/shakapacker/doctor.rb +294 -65
  57. data/lib/shakapacker/instance.rb +8 -3
  58. data/lib/shakapacker/runner.rb +244 -8
  59. data/lib/shakapacker/version.rb +1 -1
  60. data/lib/tasks/shakapacker/doctor.rake +42 -2
  61. data/package/babel/preset.ts +7 -4
  62. data/package/config.ts +42 -30
  63. data/package/configExporter/cli.ts +799 -208
  64. data/package/configExporter/configFile.ts +520 -0
  65. data/package/configExporter/fileWriter.ts +12 -8
  66. data/package/configExporter/index.ts +9 -1
  67. data/package/configExporter/types.ts +36 -2
  68. data/package/configExporter/yamlSerializer.ts +22 -8
  69. data/package/dev_server.ts +1 -1
  70. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +11 -5
  71. data/package/environments/base.ts +18 -13
  72. data/package/environments/development.ts +1 -1
  73. data/package/environments/production.ts +4 -1
  74. data/package/index.d.ts +50 -3
  75. data/package/index.d.ts.template +50 -0
  76. data/package/index.ts +7 -7
  77. data/package/loaders.d.ts +2 -2
  78. data/package/optimization/rspack.ts +1 -1
  79. data/package/plugins/rspack.ts +15 -4
  80. data/package/plugins/webpack.ts +7 -3
  81. data/package/rspack/index.ts +10 -2
  82. data/package/rules/raw.ts +3 -2
  83. data/package/rules/sass.ts +1 -1
  84. data/package/types/README.md +15 -13
  85. data/package/types/index.ts +5 -5
  86. data/package/types.ts +0 -1
  87. data/package/utils/defaultConfigPath.ts +4 -1
  88. data/package/utils/errorCodes.ts +129 -100
  89. data/package/utils/errorHelpers.ts +34 -29
  90. data/package/utils/getStyleRule.ts +5 -2
  91. data/package/utils/helpers.ts +21 -11
  92. data/package/utils/pathValidation.ts +43 -35
  93. data/package/utils/requireOrError.ts +1 -1
  94. data/package/utils/snakeToCamelCase.ts +1 -1
  95. data/package/utils/typeGuards.ts +132 -83
  96. data/package/utils/validateDependencies.ts +1 -1
  97. data/package/webpack-types.d.ts +3 -3
  98. data/package/webpackDevServerConfig.ts +22 -10
  99. data/package-lock.json +2 -2
  100. data/package.json +36 -28
  101. data/scripts/type-check-no-emit.js +1 -1
  102. data/test/configExporter/configFile.test.js +392 -0
  103. data/test/configExporter/integration.test.js +275 -0
  104. data/test/helpers.js +1 -1
  105. data/test/package/configExporter.test.js +154 -0
  106. data/test/package/helpers.test.js +2 -2
  107. data/test/package/rules/sass-version-parsing.test.js +71 -0
  108. data/test/package/rules/sass.test.js +2 -4
  109. data/test/package/rules/sass1.test.js +1 -3
  110. data/test/package/rules/sass16.test.js +23 -0
  111. data/tools/README.md +15 -5
  112. data/tsconfig.eslint.json +2 -9
  113. data/yarn.lock +1894 -1492
  114. metadata +19 -3
  115. data/.eslintignore +0 -5
@@ -10,8 +10,8 @@ In pure JavaScript projects, you can use true named imports:
10
10
 
11
11
  ```js
12
12
  // v9 - named exports in JavaScript
13
- import { bright, container } from './Foo.module.css';
14
- <button className={bright} />
13
+ import { bright, container } from "./Foo.module.css"
14
+ ;<button className={bright} />
15
15
  ```
16
16
 
17
17
  ### TypeScript Usage
@@ -27,6 +27,7 @@ import * as styles from './Foo.module.css';
27
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
28
 
29
29
  ### Benefits of v9 Configuration
30
+
30
31
  - Eliminates certain webpack warnings
31
32
  - Provides better tree-shaking potential
32
33
  - Aligns with modern JavaScript module standards
@@ -37,6 +38,7 @@ import * as styles from './Foo.module.css';
37
38
  When `namedExport: true` is enabled (v9 default), css-loader requires `exportLocalsConvention` to be either `'camelCaseOnly'` or `'dashesOnly'`.
38
39
 
39
40
  **The following will cause a build error:**
41
+
40
42
  ```js
41
43
  modules: {
42
44
  namedExport: true,
@@ -45,11 +47,13 @@ modules: {
45
47
  ```
46
48
 
47
49
  **Error message:**
50
+
48
51
  ```
49
52
  "exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
50
53
  ```
51
54
 
52
55
  **Correct v9 configuration:**
56
+
53
57
  ```js
54
58
  modules: {
55
59
  namedExport: true,
@@ -60,23 +64,26 @@ modules: {
60
64
  **exportLocalsConvention options with namedExport:**
61
65
 
62
66
  When `namedExport: true`, you can use:
67
+
63
68
  - `'camelCaseOnly'` (v9 default): Exports ONLY the camelCase version (e.g., only `myButton`)
64
69
  - `'dashesOnly'`: Exports ONLY the original kebab-case version (e.g., only `my-button`)
65
70
 
66
71
  **Not compatible with namedExport: true:**
72
+
67
73
  - `'camelCase'`: Exports both versions (both `my-button` and `myButton`) - only works with `namedExport: false` (v8 behavior)
68
74
 
69
75
  **Configuration Quick Reference:**
70
76
 
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 |
77
+ | namedExport | exportLocalsConvention | `.my-button` exports | Use Case | Compatible? |
78
+ | ----------- | ---------------------- | --------------------------------- | ---------------------- | -------------- |
79
+ | `true` | `'camelCaseOnly'` | `myButton` | JavaScript conventions | ✅ Valid |
80
+ | `true` | `'dashesOnly'` | `'my-button'` | Preserve CSS naming | ✅ Valid |
81
+ | `false` | `'camelCase'` | Both `myButton` AND `'my-button'` | v8 compatibility | ✅ Valid |
82
+ | `false` | `'asIs'` | `'my-button'` | No transformation | ✅ Valid |
83
+ | `true` | `'camelCase'` | - | - | ❌ Build Error |
78
84
 
79
85
  **When to use each option:**
86
+
80
87
  - Use `'camelCaseOnly'` if you prefer standard JavaScript naming conventions
81
88
  - Use `'dashesOnly'` if you want to preserve your CSS class names exactly as written
82
89
  - Use `'camelCase'` (with `namedExport: false`) only if you need both versions available
@@ -87,8 +94,8 @@ In Shakapacker v8 and earlier, the default behavior was to use a **default expor
87
94
 
88
95
  ```js
89
96
  // v8 and earlier default
90
- import styles from './Foo.module.css';
91
- <button className={styles.bright} />
97
+ import styles from "./Foo.module.css"
98
+ ;<button className={styles.bright} />
92
99
  ```
93
100
 
94
101
  ---
@@ -102,21 +109,23 @@ When upgrading to Shakapacker v9, you'll need to update your CSS Module imports
102
109
  #### Option 1: Update Your Code (Recommended)
103
110
 
104
111
  **For JavaScript projects:**
112
+
105
113
  ```js
106
114
  // Before (v8)
107
- import styles from './Component.module.css';
108
- <div className={styles.container}>
115
+ import styles from "./Component.module.css"
116
+ ;<div className={styles.container}>
109
117
  <button className={styles.button}>Click me</button>
110
118
  </div>
111
119
 
112
120
  // After (v9) - JavaScript
113
- import { container, button } from './Component.module.css';
114
- <div className={container}>
121
+ import { container, button } from "./Component.module.css"
122
+ ;<div className={container}>
115
123
  <button className={button}>Click me</button>
116
124
  </div>
117
125
  ```
118
126
 
119
127
  **For TypeScript projects:**
128
+
120
129
  ```typescript
121
130
  // Before (v8)
122
131
  import styles from './Component.module.css';
@@ -149,44 +158,44 @@ This approach modifies the common webpack configuration that applies to all envi
149
158
 
150
159
  ```js
151
160
  // config/webpack/commonWebpackConfig.js
152
- const { generateWebpackConfig, merge } = require('shakapacker');
161
+ const { generateWebpackConfig, merge } = require("shakapacker")
153
162
 
154
- const baseClientWebpackConfig = generateWebpackConfig();
163
+ const baseClientWebpackConfig = generateWebpackConfig()
155
164
 
156
165
  // Override CSS Modules configuration to use v8-style default exports
157
166
  const overrideCssModulesConfig = (config) => {
158
167
  // 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
- );
168
+ const cssRule = config.module.rules.find(
169
+ (rule) => rule.test && rule.test.toString().includes("css")
170
+ )
162
171
 
163
172
  if (cssRule && cssRule.use) {
164
- const cssLoaderUse = cssRule.use.find(use =>
165
- use.loader && use.loader.includes('css-loader')
166
- );
173
+ const cssLoaderUse = cssRule.use.find(
174
+ (use) => use.loader && use.loader.includes("css-loader")
175
+ )
167
176
 
168
177
  if (cssLoaderUse && cssLoaderUse.options && cssLoaderUse.options.modules) {
169
178
  // Override v9 default to use v8-style default exports
170
- cssLoaderUse.options.modules.namedExport = false;
171
- cssLoaderUse.options.modules.exportLocalsConvention = 'asIs';
179
+ cssLoaderUse.options.modules.namedExport = false
180
+ cssLoaderUse.options.modules.exportLocalsConvention = "asIs"
172
181
  }
173
182
  }
174
183
 
175
- return config;
176
- };
184
+ return config
185
+ }
177
186
 
178
187
  const commonOptions = {
179
188
  resolve: {
180
- extensions: ['.css', '.ts', '.tsx'],
181
- },
182
- };
189
+ extensions: [".css", ".ts", ".tsx"]
190
+ }
191
+ }
183
192
 
184
193
  const commonWebpackConfig = () => {
185
- const config = merge({}, baseClientWebpackConfig, commonOptions);
186
- return overrideCssModulesConfig(config);
187
- };
194
+ const config = merge({}, baseClientWebpackConfig, commonOptions)
195
+ return overrideCssModulesConfig(config)
196
+ }
188
197
 
189
- module.exports = commonWebpackConfig;
198
+ module.exports = commonWebpackConfig
190
199
  ```
191
200
 
192
201
  ### Option 2: Create `config/webpack/environment.js` (Alternative)
@@ -195,8 +204,8 @@ If you prefer using a separate environment file:
195
204
 
196
205
  ```js
197
206
  // config/webpack/environment.js
198
- const { environment } = require('@shakacode/shakapacker');
199
- const getStyleRule = require('@shakacode/shakapacker/package/utils/getStyleRule');
207
+ const { environment } = require("@shakacode/shakapacker")
208
+ const getStyleRule = require("@shakacode/shakapacker/package/utils/getStyleRule")
200
209
 
201
210
  // CSS Modules rule for *.module.css with v8-style default export
202
211
  const cssModulesRule = getStyleRule(/\.module\.css$/i, [], {
@@ -204,14 +213,14 @@ const cssModulesRule = getStyleRule(/\.module\.css$/i, [], {
204
213
  importLoaders: 2,
205
214
  modules: {
206
215
  auto: true,
207
- namedExport: false, // <-- override v9 default
208
- exportLocalsConvention: 'asIs' // keep class names as-is instead of camelCase
216
+ namedExport: false, // <-- override v9 default
217
+ exportLocalsConvention: "asIs" // keep class names as-is instead of camelCase
209
218
  }
210
- });
219
+ })
211
220
 
212
221
  // Ensure this rule wins for *.module.css
213
222
  if (cssModulesRule) {
214
- environment.loaders.prepend('css-modules', cssModulesRule);
223
+ environment.loaders.prepend("css-modules", cssModulesRule)
215
224
  }
216
225
 
217
226
  // Plain CSS rule for non-modules
@@ -219,13 +228,13 @@ const plainCssRule = getStyleRule(/(?<!\.module)\.css$/i, [], {
219
228
  sourceMap: true,
220
229
  importLoaders: 2,
221
230
  modules: false
222
- });
231
+ })
223
232
 
224
233
  if (plainCssRule) {
225
- environment.loaders.append('css', plainCssRule);
234
+ environment.loaders.append("css", plainCssRule)
226
235
  }
227
236
 
228
- module.exports = environment;
237
+ module.exports = environment
229
238
  ```
230
239
 
231
240
  Then reference this in your environment-specific configs (development.js, production.js, etc.).
@@ -238,25 +247,32 @@ If you also use Sass modules, add similar configuration for SCSS files:
238
247
  // For Option 1 approach, extend the overrideCssModulesConfig function:
239
248
  const overrideCssModulesConfig = (config) => {
240
249
  // 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 => {
250
+ const styleRules = config.module.rules.filter(
251
+ (rule) =>
252
+ rule.test &&
253
+ (rule.test.toString().includes("css") ||
254
+ rule.test.toString().includes("scss"))
255
+ )
256
+
257
+ styleRules.forEach((rule) => {
246
258
  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';
259
+ const cssLoaderUse = rule.use.find(
260
+ (use) => use.loader && use.loader.includes("css-loader")
261
+ )
262
+
263
+ if (
264
+ cssLoaderUse &&
265
+ cssLoaderUse.options &&
266
+ cssLoaderUse.options.modules
267
+ ) {
268
+ cssLoaderUse.options.modules.namedExport = false
269
+ cssLoaderUse.options.modules.exportLocalsConvention = "asIs"
254
270
  }
255
271
  }
256
- });
272
+ })
257
273
 
258
- return config;
259
- };
274
+ return config
275
+ }
260
276
  ```
261
277
 
262
278
  ---
@@ -269,10 +285,10 @@ const overrideCssModulesConfig = (config) => {
269
285
 
270
286
  ```js
271
287
  // Old (v8 - default export)
272
- import styles from './Component.module.css';
288
+ import styles from "./Component.module.css"
273
289
 
274
290
  // New (v9 - named exports)
275
- import { bright, container, button } from './Component.module.css';
291
+ import { bright, container, button } from "./Component.module.css"
276
292
  ```
277
293
 
278
294
  #### 2. Update Class References
@@ -305,8 +321,8 @@ With `exportLocalsConvention: 'camelCaseOnly'`, kebab-case class names are autom
305
321
 
306
322
  ```js
307
323
  // v9 default - camelCase conversion
308
- import { myButton, primaryColor } from './styles.module.css';
309
- <button className={myButton} />
324
+ import { myButton, primaryColor } from "./styles.module.css"
325
+ ;<button className={myButton} />
310
326
  ```
311
327
 
312
328
  **Option B: Keep kebab-case with 'dashesOnly'**
@@ -339,30 +355,33 @@ For large codebases, you can create a codemod to automate the migration:
339
355
 
340
356
  ```js
341
357
  // css-modules-v9-migration.js
342
- module.exports = function(fileInfo, api) {
343
- const j = api.jscodeshift;
344
- const root = j(fileInfo.source);
345
-
358
+ module.exports = function (fileInfo, api) {
359
+ const j = api.jscodeshift
360
+ const root = j(fileInfo.source)
361
+
346
362
  // 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
+ root
364
+ .find(j.ImportDeclaration, {
365
+ source: { value: (value) => value.endsWith(".module.css") }
366
+ })
367
+ .forEach((path) => {
368
+ const defaultSpecifier = path.node.specifiers.find(
369
+ (spec) => spec.type === "ImportDefaultSpecifier"
370
+ )
371
+
372
+ if (defaultSpecifier) {
373
+ // Convert default import to namespace import for analysis
374
+ // Then extract used properties and convert to named imports
375
+ // ... codemod implementation
376
+ }
377
+ })
378
+
379
+ return root.toSource()
380
+ }
363
381
  ```
364
382
 
365
383
  Run with:
384
+
366
385
  ```bash
367
386
  npx jscodeshift -t css-modules-v9-migration.js src/
368
387
  ```
@@ -371,14 +390,14 @@ npx jscodeshift -t css-modules-v9-migration.js src/
371
390
 
372
391
  ## Version Comparison
373
392
 
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 |
393
+ | Feature | v8 (and earlier) | v9 |
394
+ | ------------------- | -------------------------- | --------------------------------- |
395
+ | Default behavior | Default export object | Named exports |
396
+ | Import syntax | `import styles from '...'` | `import { className } from '...'` |
397
+ | Class reference | `styles.className` | `className` |
398
+ | Export convention | `asIs` (no transformation) | `camelCaseOnly` |
399
+ | TypeScript warnings | May show warnings | No warnings |
400
+ | Tree-shaking | Limited | Optimized |
382
401
 
383
402
  ---
384
403
 
@@ -419,12 +438,12 @@ Verify your imports work correctly:
419
438
 
420
439
  ```js
421
440
  // v9 default (named exports)
422
- import { bright } from './Foo.module.css';
423
- console.log(bright); // 'Foo_bright__hash'
441
+ import { bright } from "./Foo.module.css"
442
+ console.log(bright) // 'Foo_bright__hash'
424
443
 
425
444
  // Or if using v8 configuration (default export)
426
- import styles from './Foo.module.css';
427
- console.log(styles); // { bright: 'Foo_bright__hash' }
445
+ import styles from "./Foo.module.css"
446
+ console.log(styles) // { bright: 'Foo_bright__hash' }
428
447
  ```
429
448
 
430
449
  ### 3. Debug Webpack Configuration (Optional)
@@ -444,6 +463,7 @@ Then search for `css-loader` options in the generated JSON file.
444
463
  ### Build Error: exportLocalsConvention Incompatible with namedExport
445
464
 
446
465
  If you see this error during build:
466
+
447
467
  ```
448
468
  "exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
449
469
  ```
@@ -477,9 +497,9 @@ If your CSS classes aren't applying after the upgrade:
477
497
 
478
498
  ```typescript
479
499
  // src/types/css-modules.d.ts
480
- declare module '*.module.css' {
481
- const classes: { [key: string]: string };
482
- export = classes;
500
+ declare module "*.module.css" {
501
+ const classes: { [key: string]: string }
502
+ export = classes
483
503
  }
484
504
  ```
485
505
 
@@ -487,9 +507,9 @@ declare module '*.module.css' {
487
507
 
488
508
  ```typescript
489
509
  // src/types/css-modules.d.ts
490
- declare module '*.module.css' {
491
- const classes: { [key: string]: string };
492
- export default classes;
510
+ declare module "*.module.css" {
511
+ const classes: { [key: string]: string }
512
+ export default classes
493
513
  }
494
514
  ```
495
515
 
@@ -1,14 +1,13 @@
1
1
  # Customizing Babel Config
2
2
 
3
3
  ## Default Configuration
4
+
4
5
  The default configuration of babel is done by using `package.json` to use the file within the `shakapacker` package.
5
6
 
6
7
  ```json
7
8
  {
8
9
  "babel": {
9
- "presets": [
10
- "./node_modules/shakapacker/package/babel/preset.js"
11
- ]
10
+ "presets": ["./node_modules/shakapacker/package/babel/preset.js"]
12
11
  }
13
12
  }
14
13
  ```
@@ -22,7 +21,7 @@ This is a very basic skeleton that you can use that includes the Shakapacker pre
22
21
  ```js
23
22
  // babel.config.js
24
23
  module.exports = function (api) {
25
- const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
24
+ const defaultConfigFunc = require("shakapacker/package/babel/preset.js")
26
25
  const resultConfig = defaultConfigFunc(api)
27
26
 
28
27
  const changesOnDefault = {
@@ -31,7 +30,7 @@ module.exports = function (api) {
31
30
  ].filter(Boolean),
32
31
  plugins: [
33
32
  // put custom plugins here
34
- ].filter(Boolean),
33
+ ].filter(Boolean)
35
34
  }
36
35
 
37
36
  resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
@@ -55,30 +54,31 @@ And then update the configuration:
55
54
  ```js
56
55
  // babel.config.js
57
56
  module.exports = function (api) {
58
- const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
57
+ const defaultConfigFunc = require("shakapacker/package/babel/preset.js")
59
58
  const resultConfig = defaultConfigFunc(api)
60
- const isDevelopmentEnv = api.env('development')
61
- const isProductionEnv = api.env('production')
62
- const isTestEnv = api.env('test')
59
+ const isDevelopmentEnv = api.env("development")
60
+ const isProductionEnv = api.env("production")
61
+ const isTestEnv = api.env("test")
63
62
 
64
63
  const changesOnDefault = {
65
64
  presets: [
66
65
  [
67
- '@babel/preset-react',
66
+ "@babel/preset-react",
68
67
  {
69
68
  development: isDevelopmentEnv || isTestEnv,
70
69
  useBuiltIns: true
71
- }
70
+ }
72
71
  ]
73
72
  ].filter(Boolean),
74
73
  plugins: [
75
- isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
76
- {
77
- removeImport: true
74
+ isProductionEnv && [
75
+ "babel-plugin-transform-react-remove-prop-types",
76
+ {
77
+ removeImport: true
78
78
  }
79
79
  ],
80
- process.env.WEBPACK_SERVE && 'react-refresh/babel'
81
- ].filter(Boolean),
80
+ process.env.WEBPACK_SERVE && "react-refresh/babel"
81
+ ].filter(Boolean)
82
82
  }
83
83
 
84
84
  resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
data/docs/deployment.md CHANGED
@@ -3,6 +3,24 @@
3
3
  Shakapacker hooks up a new `shakapacker:compile` task to `assets:precompile`, which gets run whenever you run `assets:precompile`.
4
4
  If you are not using Sprockets `shakapacker:compile` is automatically aliased to `assets:precompile`.
5
5
 
6
+ **📖 For configuration options, see the [Configuration Guide](./configuration.md)**
7
+
8
+ ## Precompile Hook
9
+
10
+ Shakapacker supports running a custom command before webpack compilation via the `precompile_hook` configuration option. This is useful for dynamically generating entry points (e.g., for React on Rails) or performing other preparatory tasks.
11
+
12
+ **Note:** The precompile hook runs in both development and production environments. For complete documentation, see the [Precompile Hook Guide](precompile_hook.md).
13
+
14
+ Quick example for production deployment:
15
+
16
+ ```yaml
17
+ # config/shakapacker.yml
18
+ production:
19
+ precompile_hook: "bin/rails react_on_rails:generate_packs"
20
+ ```
21
+
22
+ This ensures your dynamic entry points are generated before `assets:precompile` runs.
23
+
6
24
  ## Heroku
7
25
 
8
26
  In order for your Shakapacker app to run on Heroku, you'll need to do a bit of configuration before hand.
@@ -3,19 +3,25 @@
3
3
  It's a little trickier for Rails developers to work on the JS code of a project like shakacode/shakapacker. So here are some tips!
4
4
 
5
5
  ## Use some test app
6
+
6
7
  For example, for React on Rails Changes, I'm using [shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh](https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh).
7
8
  This directory is the `TEST_APP_DIR`.
8
9
 
9
10
  ## Fork shakacode/shakapacker
11
+
10
12
  Let's call the shakacode/shakapacker directory `SHAKAPACKER_DIR` which has shakacode/shakapacker's `package.json`.
11
13
 
12
14
  ## Changing the Package
15
+
13
16
  ### Setup with Yalc
17
+
14
18
  Use [`yalc`](https://github.com/wclr/yalc) unless you like yak shaving weird errors.
19
+
15
20
  1. In `SHAKAPACKER_DIR`, run `yalc publish`
16
21
  2. In `TEST_APP_DIR`, run `yalc link shakapacker`
17
22
 
18
23
  ## Update the Package Code
24
+
19
25
  1. Make some JS change in SHAKAPACKER_DIR
20
26
  2. Run `yalc push` and your changes will be pushed to your `TEST_APP_DIR`'s node_modules.
21
27
  3. You may need to run `yarn` in `TEST_APP_DIR` if you added or removed dependencies of shakacode/shakapacker.
@@ -20,16 +20,16 @@ As of Shakapacker v9, all peer dependencies are marked as optional via `peerDepe
20
20
  "dependencies": {
21
21
  "js-yaml": "^4.1.0",
22
22
  "path-complete-extname": "^1.0.0",
23
- "webpack-merge": "^5.8.0" // Direct dependency - always available
23
+ "webpack-merge": "^5.8.0" // Direct dependency - always available
24
24
  },
25
25
  "peerDependencies": {
26
26
  "webpack": "^5.76.0",
27
- "@rspack/core": "^1.0.0",
27
+ "@rspack/core": "^1.0.0"
28
28
  // ... all build tools
29
29
  },
30
30
  "peerDependenciesMeta": {
31
31
  "webpack": { "optional": true },
32
- "@rspack/core": { "optional": true },
32
+ "@rspack/core": { "optional": true }
33
33
  // ... all marked as optional
34
34
  }
35
35
  }
@@ -49,6 +49,7 @@ Type-only imports are erased during compilation and don't trigger module resolut
49
49
  ## Configuration Examples
50
50
 
51
51
  ### Webpack + Babel (Traditional)
52
+
52
53
  ```json
53
54
  {
54
55
  "dependencies": {
@@ -63,6 +64,7 @@ Type-only imports are erased during compilation and don't trigger module resolut
63
64
  ```
64
65
 
65
66
  ### Webpack + SWC (20x Faster)
67
+
66
68
  ```json
67
69
  {
68
70
  "dependencies": {
@@ -76,6 +78,7 @@ Type-only imports are erased during compilation and don't trigger module resolut
76
78
  ```
77
79
 
78
80
  ### Rspack + SWC (10x Faster Bundling)
81
+
79
82
  ```json
80
83
  {
81
84
  "dependencies": {
@@ -100,6 +103,7 @@ If upgrading from Shakapacker v8:
100
103
  ### New Installations
101
104
 
102
105
  The installer (`rails shakapacker:install`) only adds packages needed for your configuration:
106
+
103
107
  - Detects your preferred bundler (webpack/rspack)
104
108
  - Installs appropriate JavaScript transpiler (babel/swc/esbuild)
105
109
  - Adds only required dependencies
@@ -129,12 +133,13 @@ Verify Shakapacker loads without optional dependencies:
129
133
 
130
134
  ```javascript
131
135
  // This works even without webpack installed (when using rspack)
132
- const shakapacker = require('shakapacker');
136
+ const shakapacker = require("shakapacker")
133
137
  ```
134
138
 
135
139
  ### CI Integration
136
140
 
137
141
  The test suite includes:
142
+
138
143
  - `spec/shakapacker/optional_dependencies_spec.rb` - Package.json structure validation
139
144
  - `spec/shakapacker/doctor_optional_peer_spec.rb` - Doctor command validation
140
145
  - `test/peer-dependencies.sh` - Installation warning tests