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
data/docs/rspack.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Shakapacker supports [Rspack](https://rspack.rs) as an alternative assets bundler to Webpack. Rspack is a fast Rust-based web bundler with webpack-compatible API that can significantly speed up your build times.
4
4
 
5
+ **📖 For configuration options, see the [Configuration Guide](./configuration.md)**
6
+
5
7
  ## Installation
6
8
 
7
9
  First, install the required Rspack dependencies:
@@ -10,7 +12,7 @@ First, install the required Rspack dependencies:
10
12
  npm install @rspack/core @rspack/cli -D
11
13
  # or
12
14
  yarn add @rspack/core @rspack/cli -D
13
- # or
15
+ # or
14
16
  pnpm add @rspack/core @rspack/cli -D
15
17
  # or
16
18
  bun add @rspack/core @rspack/cli -D
@@ -23,9 +25,8 @@ Note: These packages are already listed as optional peer dependencies in Shakapa
23
25
  To enable Rspack, update your `config/shakapacker.yml`:
24
26
 
25
27
  ```yaml
26
- default: &default
27
- # ... other config options
28
- assets_bundler: 'rspack' # Change from 'webpack' to 'rspack'
28
+ default: &default # ... other config options
29
+ assets_bundler: "rspack" # Change from 'webpack' to 'rspack'
29
30
  ```
30
31
 
31
32
  ### Configuration Files
@@ -33,7 +34,7 @@ default: &default
33
34
  Rspack uses its own configuration directory to keep things organized. Create your Rspack configuration file at `config/rspack/rspack.config.js`:
34
35
 
35
36
  ```javascript
36
- const { generateRspackConfig } = require('shakapacker/rspack')
37
+ const { generateRspackConfig } = require("shakapacker/rspack")
37
38
 
38
39
  module.exports = generateRspackConfig()
39
40
  ```
@@ -43,14 +44,12 @@ module.exports = generateRspackConfig()
43
44
  If you need to customize your Rspack configuration:
44
45
 
45
46
  ```javascript
46
- const { generateRspackConfig } = require('shakapacker/rspack')
47
+ const { generateRspackConfig } = require("shakapacker/rspack")
47
48
 
48
49
  const rspackConfig = generateRspackConfig({
49
- plugins: [
50
- new SomeRspackCompatiblePlugin()
51
- ],
50
+ plugins: [new SomeRspackCompatiblePlugin()],
52
51
  resolve: {
53
- extensions: ['.ts', '.tsx', '.js', '.jsx']
52
+ extensions: [".ts", ".tsx", ".js", ".jsx"]
54
53
  }
55
54
  })
56
55
 
@@ -62,14 +61,16 @@ module.exports = rspackConfig
62
61
  If you have an existing `config/webpack/webpack.config.js`, you can migrate it to `config/rspack/rspack.config.js`:
63
62
 
64
63
  **Old (webpack.config.js):**
64
+
65
65
  ```javascript
66
- const { generateWebpackConfig } = require('shakapacker')
66
+ const { generateWebpackConfig } = require("shakapacker")
67
67
  module.exports = generateWebpackConfig()
68
68
  ```
69
69
 
70
70
  **New (rspack.config.js):**
71
+
71
72
  ```javascript
72
- const { generateRspackConfig } = require('shakapacker/rspack')
73
+ const { generateRspackConfig } = require("shakapacker/rspack")
73
74
  module.exports = generateRspackConfig()
74
75
  ```
75
76
 
@@ -89,11 +90,11 @@ Rspack has built-in loaders that are faster than their webpack counterparts:
89
90
 
90
91
  Most webpack plugins work with Rspack, but some have Rspack-specific alternatives:
91
92
 
92
- | Webpack Plugin | Rspack Alternative | Status |
93
- |---|---|---|
94
- | `mini-css-extract-plugin` | `rspack.CssExtractRspackPlugin` | Built-in |
95
- | `copy-webpack-plugin` | `rspack.CopyRspackPlugin` | Built-in |
96
- | `terser-webpack-plugin` | `rspack.SwcJsMinimizerRspackPlugin` | Built-in |
93
+ | Webpack Plugin | Rspack Alternative | Status |
94
+ | ------------------------- | ----------------------------------- | -------- |
95
+ | `mini-css-extract-plugin` | `rspack.CssExtractRspackPlugin` | Built-in |
96
+ | `copy-webpack-plugin` | `rspack.CopyRspackPlugin` | Built-in |
97
+ | `terser-webpack-plugin` | `rspack.SwcJsMinimizerRspackPlugin` | Built-in |
97
98
 
98
99
  ### Minification
99
100
 
@@ -136,28 +137,31 @@ The same dev server configuration in `shakapacker.yml` applies to both webpack a
136
137
  Rspack typically provides:
137
138
 
138
139
  - **2-10x faster** cold builds
139
- - **5-20x faster** incremental builds
140
+ - **5-20x faster** incremental builds
140
141
  - **Faster HMR** (Hot Module Replacement)
141
142
  - **Lower memory usage**
142
143
 
143
144
  ## Migration Checklist
144
145
 
145
146
  1. **Install Rspack dependencies:**
147
+
146
148
  ```bash
147
149
  npm install @rspack/core @rspack/cli -D
148
150
  ```
149
151
 
150
152
  2. **Update configuration:**
153
+
151
154
  ```yaml
152
155
  # config/shakapacker.yml
153
156
  default: &default
154
- assets_bundler: 'rspack'
157
+ assets_bundler: "rspack"
155
158
  ```
156
159
 
157
160
  3. **Create Rspack config:**
161
+
158
162
  ```javascript
159
163
  // config/rspack/rspack.config.js
160
- const { generateRspackConfig } = require('shakapacker/rspack')
164
+ const { generateRspackConfig } = require("shakapacker/rspack")
161
165
  module.exports = generateRspackConfig()
162
166
  ```
163
167
 
@@ -187,4 +191,4 @@ If builds are unexpectedly slow:
187
191
 
188
192
  - [Rspack Official Documentation](https://rspack.rs)
189
193
  - [Rspack Migration Guide](https://rspack.rs/guide/migration/webpack)
190
- - [Rspack Plugins](https://rspack.rs/plugins/webpack/)
194
+ - [Rspack Plugins](https://rspack.rs/plugins/webpack/)
@@ -2,10 +2,67 @@
2
2
 
3
3
  > 💡 **Quick Start**: For a step-by-step migration guide from Webpack to Rspack, see [Common Upgrades Guide - Webpack to Rspack](./common-upgrades.md#migrating-from-webpack-to-rspack).
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Before You Migrate](#before-you-migrate)
9
+ - [Migration Timeline Expectations](#migration-timeline-expectations)
10
+ - [Testing Strategy](#testing-strategy)
11
+ - [Server-Side Rendering (SSR) Considerations](#server-side-rendering-ssr-considerations)
12
+ - [Key Differences from Webpack](#key-differences-from-webpack)
13
+ - [Migration Steps](#migration-steps)
14
+ - [Configuration Best Practices](#configuration-best-practices)
15
+ - [Common Issues and Solutions](#common-issues-and-solutions)
16
+ - [Performance Tips](#performance-tips)
17
+ - [Debugging Configuration](#debugging-configuration)
18
+ - [Resources](#resources)
19
+
5
20
  ## Overview
6
21
 
7
22
  This guide documents the differences between webpack and Rspack configurations in Shakapacker, and provides migration guidance for users switching to Rspack.
8
23
 
24
+ [Rspack](https://rspack.rs/) is a high-performance bundler written in Rust, offering 5-10x faster build times than webpack with excellent webpack compatibility.
25
+
26
+ ## Before You Migrate
27
+
28
+ ### Migration Timeline Expectations
29
+
30
+ Based on real-world migrations, plan your migration time accordingly:
31
+
32
+ - **Simple projects** (no SSR, no CSS modules, no custom config): 1-2 hours
33
+ - **Standard projects** (CSS modules, basic SSR): 4-8 hours
34
+ - **Complex projects** (CSS modules, SSR, ReScript, custom config): 2-3 days
35
+
36
+ **Without good documentation**: A complex migration can take 3+ days with 11+ commits to resolve all issues.
37
+
38
+ **With this documentation**: Most issues can be resolved in 2-3 commits.
39
+
40
+ ### Testing Strategy
41
+
42
+ When migrating from webpack to Rspack, follow this testing strategy to minimize issues:
43
+
44
+ 1. **Test locally first**: Ensure you can run the full test suite locally before pushing
45
+ 2. **Incremental migration**: Consider migrating to SWC first (while on webpack), test thoroughly, then migrate to Rspack
46
+ 3. **Watch for test flakiness**: SSR-related issues (especially CSS extraction) can cause non-deterministic test failures
47
+ 4. **Run full test suite**: Don't rely solely on CI - run tests locally to catch issues faster
48
+
49
+ ### Server-Side Rendering (SSR) Considerations
50
+
51
+ ⚠️ **If your application uses SSR**, be aware of these critical issues before migrating:
52
+
53
+ 1. **CSS Extraction Differences**: Rspack uses different loader paths than webpack for CSS extraction
54
+ 2. **CSS Modules Breaking Change**: Shakapacker 9 changed from default exports to named exports
55
+ 3. **React Runtime Compatibility**: SWC's automatic runtime may not work with React on Rails SSR detection
56
+
57
+ **SSR Migration Checklist** (complete before migrating):
58
+
59
+ - [ ] Understand how your server bundle filters CSS extraction loaders
60
+ - [ ] Know whether you're using CSS modules and how they're imported
61
+ - [ ] Check if you're using React on Rails SSR (may need classic React runtime)
62
+ - [ ] Plan for potential configuration changes to handle both webpack and Rspack paths
63
+
64
+ **Detailed SSR solutions** are provided in the [Common Issues](#common-issues-and-solutions) section below.
65
+
9
66
  ## Key Differences from Webpack
10
67
 
11
68
  ### 1. Built-in Loaders
@@ -145,7 +202,7 @@ devServer: {
145
202
  }
146
203
  ```
147
204
 
148
- ## Migration Checklist
205
+ ## Migration Steps
149
206
 
150
207
  ### Quick Start: Using the Switch Bundler Task
151
208
 
@@ -194,7 +251,7 @@ rails shakapacker:switch_bundler --init-config
194
251
 
195
252
  If you prefer to migrate manually or need more control:
196
253
 
197
- ### Step 1: Update Dependencies
254
+ #### Step 1: Update Dependencies
198
255
 
199
256
  ```bash
200
257
  # Remove webpack dependencies
@@ -204,7 +261,7 @@ npm uninstall webpack webpack-cli webpack-dev-server
204
261
  npm install --save-dev @rspack/core @rspack/cli
205
262
  ```
206
263
 
207
- ### Step 2: Update Configuration Files
264
+ #### Step 2: Update Configuration Files
208
265
 
209
266
  1. Create `config/rspack/rspack.config.js` based on your webpack config
210
267
  2. Update `config/shakapacker.yml`:
@@ -213,24 +270,24 @@ npm install --save-dev @rspack/core @rspack/cli
213
270
  assets_bundler: "rspack"
214
271
  ```
215
272
 
216
- ### Step 3: Replace Loaders
273
+ #### Step 3: Replace Loaders
217
274
 
218
275
  - Replace `babel-loader` with `builtin:swc-loader`
219
276
  - Remove `file-loader`, `url-loader`, `raw-loader` - use asset modules
220
277
  - Update CSS loaders to use Rspack's built-in support
221
278
 
222
- ### Step 4: Update Plugins
279
+ #### Step 4: Update Plugins
223
280
 
224
281
  - Replace plugins with Rspack alternatives (see table above)
225
282
  - Remove incompatible plugins
226
283
  - Add Rspack-specific plugins as needed
227
284
 
228
- ### Step 5: TypeScript Setup
285
+ #### Step 5: TypeScript Setup
229
286
 
230
287
  1. Add `isolatedModules: true` to `tsconfig.json`
231
288
  2. Optional: Add `ts-checker-rspack-plugin` for type checking
232
289
 
233
- ### Step 6: Test Your Build
290
+ #### Step 6: Test Your Build
234
291
 
235
292
  ```bash
236
293
  # Development build
@@ -240,26 +297,285 @@ bin/shakapacker
240
297
  bin/shakapacker --mode production
241
298
  ```
242
299
 
300
+ #### Step 7: Review Migration Checklist
301
+
302
+ - [ ] Install Rspack dependencies
303
+ - [ ] Update `config/shakapacker.yml`
304
+ - [ ] Create `config/rspack/rspack.config.js`
305
+ - [ ] Replace incompatible plugins
306
+ - [ ] Update TypeScript config (add `isolatedModules: true`)
307
+ - [ ] Convert file loaders to asset modules
308
+ - [ ] Test development build
309
+ - [ ] Test production build
310
+ - [ ] Run test suite
311
+ - [ ] Update CI/CD pipelines
312
+ - [ ] Deploy to staging
313
+ - [ ] Monitor performance improvements
314
+
315
+ ## Configuration Best Practices
316
+
317
+ ### Configuration Organization
318
+
319
+ **Recommended approach**: Keep webpack and rspack configs in the same directory with conditional logic:
320
+
321
+ ```javascript
322
+ // config/webpack/webpack.config.js (works for both bundlers)
323
+ const { config } = require("shakapacker")
324
+ const bundler =
325
+ config.assets_bundler === "rspack"
326
+ ? require("@rspack/core")
327
+ : require("webpack")
328
+
329
+ // Use for plugins
330
+ clientConfig.plugins.push(
331
+ new bundler.ProvidePlugin({
332
+ /* ... */
333
+ })
334
+ )
335
+
336
+ serverConfig.plugins.unshift(
337
+ new bundler.optimize.LimitChunkCountPlugin({ maxChunks: 1 })
338
+ )
339
+ ```
340
+
341
+ **Avoid**: Creating separate `config/rspack/` directory unless configs diverge significantly.
342
+
343
+ **Benefits**:
344
+
345
+ - Smaller diff when comparing configurations
346
+ - Easy to see what's different between bundlers
347
+ - Single source of truth for webpack/rspack config
348
+ - Easier maintenance and debugging
349
+
350
+ ### CSS Modules Configuration Placement
351
+
352
+ **Critical**: CSS modules configuration overrides must be inside the config function:
353
+
354
+ ```javascript
355
+ // ✅ CORRECT - Inside function (applied fresh each time)
356
+ const commonWebpackConfig = () => {
357
+ const baseConfig = generateWebpackConfig()
358
+
359
+ baseConfig.module.rules.forEach((rule) => {
360
+ // Override CSS modules here
361
+ })
362
+
363
+ return merge({}, baseConfig, commonOptions)
364
+ }
365
+
366
+ // ❌ INCORRECT - Outside function (may not apply consistently)
367
+ const baseConfig = generateWebpackConfig()
368
+ baseConfig.module.rules.forEach((rule) => {
369
+ // This may not work correctly
370
+ })
371
+ ```
372
+
373
+ ### Handling Breaking Changes
374
+
375
+ When upgrading to Shakapacker 9 with Rspack:
376
+
377
+ 1. **CSS Modules default exports → named exports**: This is a breaking change. Either:
378
+ - Update your code to use named imports (recommended for new projects)
379
+ - Override the configuration to keep default exports (easier for existing large codebases)
380
+
381
+ 2. **Document your decisions**: Add comments explaining why you chose a particular configuration approach
382
+
383
+ 3. **Create patches for broken dependencies**: If ReScript or other compiled-to-JS dependencies are missing build configs, use `patch-package` and file upstream issues
384
+
385
+ ### Common Pitfalls to Avoid
386
+
387
+ 1. **Don't commit generated files**: Check your `.gitignore` for files that should not be committed (e.g., `i18n/translations.js`)
388
+ 2. **Update lockfiles**: Always run your package manager after adding dependencies (especially `patch-package`)
389
+ 3. **Test with frozen lockfile**: Ensure your CI runs with `--frozen-lockfile` or equivalent to catch lockfile issues
390
+ 4. **Check Node version compatibility**: Verify your Node version meets all dependency requirements
391
+ 5. **Don't make empty commits**: If CI fails but local passes, investigate the root cause - don't try to "trigger CI re-run" with empty commits
392
+
243
393
  ## Common Issues and Solutions
244
394
 
395
+ ### Issue: CSS Modules Returning Undefined (CRITICAL)
396
+
397
+ **Error:** `Cannot read properties of undefined (reading 'className')` in SSR or `export 'default' (imported as 'css') was not found`
398
+
399
+ **Root Cause:** Shakapacker 9 changed the default CSS Modules configuration to use named exports (`namedExport: true`), which is a breaking change from v8's default export behavior.
400
+
401
+ **Solution:** If you want to keep the v8 default export behavior, override the CSS loader configuration:
402
+
403
+ ```javascript
404
+ // config/webpack/commonWebpackConfig.js (or rspack equivalent)
405
+ const { generateWebpackConfig, merge } = require("shakapacker")
406
+
407
+ const commonWebpackConfig = () => {
408
+ const baseWebpackConfig = generateWebpackConfig()
409
+
410
+ // Override CSS modules to use default exports for backward compatibility
411
+ baseWebpackConfig.module.rules.forEach((rule) => {
412
+ if (rule.use && Array.isArray(rule.use)) {
413
+ const cssLoader = rule.use.find((loader) => {
414
+ const loaderName = typeof loader === "string" ? loader : loader?.loader
415
+ return loaderName?.includes("css-loader")
416
+ })
417
+
418
+ if (cssLoader?.options?.modules) {
419
+ cssLoader.options.modules.namedExport = false
420
+ cssLoader.options.modules.exportLocalsConvention = "camelCase"
421
+ }
422
+ }
423
+ })
424
+
425
+ return merge({}, baseWebpackConfig, commonOptions)
426
+ }
427
+ ```
428
+
429
+ **Important:** This configuration must be inside the function so it applies to fresh config each time.
430
+
431
+ See [CSS Modules Export Mode](./css-modules-export-mode.md) for detailed migration guidance.
432
+
433
+ ### Issue: Server-Side Rendering CSS Extraction (CRITICAL for SSR)
434
+
435
+ **Error:** Intermittent failures with `Cannot read properties of undefined (reading 'className')` or flaky tests
436
+
437
+ **Root Cause:** When configuring server bundles, the code that removes CSS extraction loaders must handle both webpack and Rspack loader paths. Rspack uses `cssExtractLoader.js` instead of `mini-css-extract-plugin`.
438
+
439
+ **Solution:** Update your server webpack config to filter both loader types:
440
+
441
+ ```javascript
442
+ // config/webpack/serverWebpackConfig.js
443
+ rule.use = rule.use.filter((item) => {
444
+ let testValue
445
+ if (typeof item === "string") {
446
+ testValue = item
447
+ } else if (typeof item.loader === "string") {
448
+ testValue = item.loader
449
+ }
450
+ // Handle both Webpack and Rspack CSS extract loaders
451
+ return !(
452
+ testValue?.match(/mini-css-extract-plugin/) ||
453
+ testValue?.includes("cssExtractLoader") || // Rspack loader path!
454
+ testValue === "style-loader"
455
+ )
456
+ })
457
+ ```
458
+
459
+ **Additional SSR Requirement:** When modifying CSS modules options for SSR, use spread operator to preserve common config:
460
+
461
+ ```javascript
462
+ if (cssLoader && cssLoader.options && cssLoader.options.modules) {
463
+ // Preserve existing modules config but add exportOnlyLocals for SSR
464
+ cssLoader.options.modules = {
465
+ ...cssLoader.options.modules, // Preserve namedExport and other settings!
466
+ exportOnlyLocals: true
467
+ }
468
+ }
469
+ ```
470
+
471
+ ### Issue: SWC React Runtime with SSR
472
+
473
+ **Error:** `Invalid call to renderToString. Possibly you have a renderFunction...`
474
+
475
+ **Root Cause:** React on Rails SSR detection logic expects a specific function signature that may not work with SWC's automatic React runtime.
476
+
477
+ **Solution:** Use classic React runtime in your SWC configuration:
478
+
479
+ ```javascript
480
+ // config/swc.config.js
481
+ const customConfig = {
482
+ options: {
483
+ jsc: {
484
+ transform: {
485
+ react: {
486
+ runtime: "classic", // Changed from 'automatic' for SSR compatibility
487
+ refresh: env.isDevelopment && env.runningWebpackDevServer
488
+ }
489
+ }
490
+ }
491
+ }
492
+ }
493
+ ```
494
+
495
+ ### Issue: ReScript Module Resolution
496
+
497
+ **Error:** `Module not found: Can't resolve './Actions.bs.js'`
498
+
499
+ **Solution:** Add `.bs.js` to your resolve extensions:
500
+
501
+ ```javascript
502
+ const commonOptions = {
503
+ resolve: {
504
+ extensions: [".css", ".ts", ".tsx", ".bs.js"] // Add .bs.js for ReScript
505
+ }
506
+ }
507
+ ```
508
+
509
+ ### Issue: ReScript Dependencies Missing Compiled Files
510
+
511
+ **Error:** `Module not found: Can't resolve '@some-package/src/Module.bs.js'`
512
+
513
+ **Root Cause:** Some ReScript packages ship only `.res` source files without compiled `.bs.js` files, or have broken `bsconfig.json` configurations.
514
+
515
+ **Solution:** Use `patch-package` to fix the dependency:
516
+
517
+ 1. Install patch-package:
518
+
519
+ ```bash
520
+ npm install --save-dev patch-package
521
+ ```
522
+
523
+ 2. Add postinstall script to `package.json`:
524
+
525
+ ```json
526
+ {
527
+ "scripts": {
528
+ "postinstall": "patch-package"
529
+ }
530
+ }
531
+ ```
532
+
533
+ 3. Fix the package's `bsconfig.json` (example for a package missing `package-specs`):
534
+
535
+ ```json
536
+ {
537
+ "name": "@package/name",
538
+ "sources": ["src"],
539
+ "package-specs": [
540
+ {
541
+ "module": "esmodule",
542
+ "in-source": true
543
+ }
544
+ ],
545
+ "suffix": ".bs.js"
546
+ }
547
+ ```
548
+
549
+ 4. Generate the patch:
550
+
551
+ ```bash
552
+ npx patch-package @package/name
553
+ ```
554
+
555
+ 5. Consider filing an issue with the upstream package maintainer.
556
+
245
557
  ### Issue: LimitChunkCountPlugin Error
246
558
 
247
559
  **Error:** `Cannot read properties of undefined (reading 'tap')`
560
+
248
561
  **Solution:** Remove `webpack.optimize.LimitChunkCountPlugin` and use `splitChunks` configuration instead.
249
562
 
250
563
  ### Issue: Missing Loaders
251
564
 
252
565
  **Error:** Module parse errors
566
+
253
567
  **Solution:** Check console logs for skipped loaders and install missing dependencies.
254
568
 
255
569
  ### Issue: CSS Extraction
256
570
 
257
571
  **Error:** CSS not being extracted properly
572
+
258
573
  **Solution:** Use `rspack.CssExtractRspackPlugin` instead of `mini-css-extract-plugin`.
259
574
 
260
575
  ### Issue: TypeScript Errors
261
576
 
262
577
  **Error:** TypeScript compilation errors
578
+
263
579
  **Solution:** Ensure `isolatedModules: true` is set in `tsconfig.json`.
264
580
 
265
581
  ## Performance Tips
@@ -269,6 +585,16 @@ bin/shakapacker --mode production
269
585
  3. **Enable Caching:** Rspack has built-in persistent caching
270
586
  4. **Use SWC:** The built-in SWC loader is significantly faster than Babel
271
587
 
588
+ **Expected Performance Improvements:**
589
+
590
+ | Build Type | Webpack | Rspack | Improvement |
591
+ | ---------------- | ------- | ------ | ----------- |
592
+ | Cold build | 60s | 8s | 7.5x faster |
593
+ | Hot reload | 3s | 0.5s | 6x faster |
594
+ | Production build | 120s | 15s | 8x faster |
595
+
596
+ **Note:** Actual improvements vary based on project size, configuration, and hardware. Rspack's Rust-based architecture provides consistent 5-10x performance gains across most scenarios.
597
+
272
598
  ## Debugging Configuration
273
599
 
274
600
  To compare your webpack and rspack configurations during migration:
@@ -302,4 +628,5 @@ See the [Troubleshooting Guide](./troubleshooting.md#exporting-webpack--rspack-c
302
628
  - [Rspack Documentation](https://rspack.rs)
303
629
  - [Rspack Examples](https://github.com/rspack-contrib/rspack-examples)
304
630
  - [Awesome Rspack](https://github.com/rspack-contrib/awesome-rspack)
305
- - [Migration Guide](https://rspack.rs/guide/migration/webpack)
631
+ - [Rspack Migration Guide](https://rspack.rs/guide/migration/webpack)
632
+ - [Real-world Migration Example](https://github.com/shakacode/react-webpack-rails-tutorial/pull/680) - Complete migration from webpack to Rspack with SSR, CSS Modules, and ReScript
data/docs/sprockets.md CHANGED
@@ -5,6 +5,7 @@
5
5
  If you are still using Sprockets for some of your assets, you might want to include files from `node_modules` directory in your asset pipeline. This is useful, for example, if you want to reference a stylesheet from a node package in your `.scss` stylesheet.
6
6
 
7
7
  In order to enable this, make sure you add `node_modules` to the asset load path by adding the following in an initializer (for example `config/initializers/assets.rb`)
8
+
8
9
  ```ruby
9
10
  Rails.application.config.assets.paths << Rails.root.join('node_modules')
10
11
  ```
@@ -21,15 +21,15 @@ style-loader is how you are probably are used to serving CSS in development with
21
21
 
22
22
  ### benefits
23
23
 
24
- * No [Flash Of Unstyled Content (FOUC)](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) on HMR refreshes
25
- * Smaller/faster incremental updates.
24
+ - No [Flash Of Unstyled Content (FOUC)](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) on HMR refreshes
25
+ - Smaller/faster incremental updates.
26
26
 
27
27
  ### drawbacks
28
28
 
29
- * Inflated JS deliverable size; requires JS execution before CSS is available
30
- * FOUC on initial page load
31
- * Adds an extra dependency
32
- * Divergence in delivery mechanism from production
29
+ - Inflated JS deliverable size; requires JS execution before CSS is available
30
+ - FOUC on initial page load
31
+ - Adds an extra dependency
32
+ - Divergence in delivery mechanism from production
33
33
 
34
34
  ## Why would I pick mini-css-extract-plugin?
35
35
 
@@ -37,12 +37,12 @@ mini-css-extract-plugin's behavior is much more true to a production deployment'
37
37
 
38
38
  ### benefits
39
39
 
40
- * Required for production, so it's going to be in play anyhow. Using only it simplifies the config and eliminates the style-loader dependency.
41
- * No FOUC on initial page loads
42
- * CSS delivered via `<link>` tags matches the mechanism used in production (I have been guilty of omitting my `stylesheet_pack_tag` for my first deploy because CSS worked fine with just the `javascript_pack_tag` in development.)
40
+ - Required for production, so it's going to be in play anyhow. Using only it simplifies the config and eliminates the style-loader dependency.
41
+ - No FOUC on initial page loads
42
+ - CSS delivered via `<link>` tags matches the mechanism used in production (I have been guilty of omitting my `stylesheet_pack_tag` for my first deploy because CSS worked fine with just the `javascript_pack_tag` in development.)
43
43
 
44
44
  ### drawbacks
45
45
 
46
- * Invokes a separate HTTP request, compared to style-loader
47
- * Potential for FOUC on HMR refreshes
48
- * More data transferred per refresh (full stylesheet reload, rather than just an incremental patch). Not likely to be noticed for local development, but still a technical difference. This may only be the case [when you're using local CSS modules](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/src/hmr/hotModuleReplacement.js#L267-L273).
46
+ - Invokes a separate HTTP request, compared to style-loader
47
+ - Potential for FOUC on HMR refreshes
48
+ - More data transferred per refresh (full stylesheet reload, rather than just an incremental patch). Not likely to be noticed for local development, but still a technical difference. This may only be the case [when you're using local CSS modules](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/src/hmr/hotModuleReplacement.js#L267-L273).
@@ -1,13 +1,16 @@
1
1
  # Subresource integrity
2
+
2
3
  It's a cryptographic hash that helps browsers check that the served js or css file has not been tampered in any way.
3
4
 
4
5
  [MDN - Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
5
6
 
6
7
  ## Important notes
8
+
7
9
  - If you somehow modify the file after the hash was generated, it will automatically be considered as tampered, and the browser will not allow it to be executed.
8
10
  - Enabling subresource integrity generation, will change the structure of `manifest.json`. Keep that in mind if you utilize this file in any other custom implementation.
9
11
 
10
12
  Before:
13
+
11
14
  ```json
12
15
  {
13
16
  "application.js": "/path_to_asset"
@@ -15,6 +18,7 @@ Before:
15
18
  ```
16
19
 
17
20
  After:
21
+
18
22
  ```json
19
23
  {
20
24
  "application.js": {
@@ -25,6 +29,7 @@ After:
25
29
  ```
26
30
 
27
31
  ## Possible CORS issues
32
+
28
33
  Enabling subresource integrity for an asset, actually enforces CORS checks on that resource too. Which means that
29
34
  if you haven't set that up properly beforehand, it will probably lead to CORS errors with cached assets.
30
35
 
@@ -36,19 +41,20 @@ By default, this setting is disabled, to ensure backwards compatibility, and let
36
41
  This may change in the future, as it is a very nice security feature, and it should be enabled by default.
37
42
 
38
43
  To enable it, just add this in `shakapacker.yml`
44
+
39
45
  ```yml
40
- integrity:
41
- enabled: true
46
+ integrity:
47
+ enabled: true
42
48
  ```
43
49
 
44
- For further customization, you can also utilize the options `hash_functions` that control the functions used to generate
50
+ For further customization, you can also utilize the options `hash_functions` that control the functions used to generate
45
51
  integrity hashes. And `cross_origin` that sets the cross-origin loading attribute.
46
52
 
47
53
  ```yml
48
- integrity:
49
- enabled: true
50
- hash_functions: ["sha256", "sha384", "sha512"]
51
- cross_origin: "anonymous" # or "use-credentials"
54
+ integrity:
55
+ enabled: true
56
+ hash_functions: ["sha256", "sha384", "sha512"]
57
+ cross_origin: "anonymous" # or "use-credentials"
52
58
  ```
53
59
 
54
60
  This will utilize under the hood webpack-subresource-integrity plugin and will modify `manifest.json` to include integrity hashes.