shakapacker 10.3.1 → 10.3.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66455bdf084a5e63422cf5741b2ce549b2dc77dcd36553e8e5fa7dcc6ce34286
4
- data.tar.gz: a7030178f8ec26e3c1243fbdeb806c2746d6b14484bc99142a35f3fa3f57e9dd
3
+ metadata.gz: d74b345013f03450e4cd5050bd22f2364e2acf8b00a750acf833b07eda6046c7
4
+ data.tar.gz: 4933ffb9d1feb7acc0bdcb2a06fb492b030ea347976da728dd83a7e4b7f3790f
5
5
  SHA512:
6
- metadata.gz: ec0476cbbaaa7044f9f06a3fe207caad61350c309164deffa47d5990e583b025c03e235b4e0e110adac394461fce264b887a71de6159a43ce5664e7a8a133992
7
- data.tar.gz: 3d625e63b6f838c9febaaad2dda13d43cd7486748d7e39d6ff09e5675d06ab8b85414b9d323c41a3b09fca5adb19ec193b6d90d5651dce92f8573cd18e23937f
6
+ metadata.gz: 1a286328ea129dc5e2c9baad6fae0aaeec4c886533a10489301e3396fd45ac3fb9b3db35455407de79eda6506d983c0a474ba7c69ea2e68eed99b715700ae274
7
+ data.tar.gz: 1411acd0a7475961d10fc0a8e99bfa76e235b1c9e83ff7e9a152f726c578e2ac0cb5654dced3a52a492e62d75fc741cf68f8a6cd0deabc23ed294229e87a874e
data/CHANGELOG.md CHANGED
@@ -10,6 +10,21 @@
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [v10.3.2] - August 28, 2026
14
+
15
+ ### Changed
16
+
17
+ - **Bumped the development and CI pin for Rspack from `2.0.1` to `2.2.0`.** [PR #1258](https://github.com/shakacode/shakapacker/pull/1258) by [justin808](https://github.com/justin808). The `@rspack/core`, `@rspack/cli`, `@rspack/dev-server`, and `@rspack/plugin-react-refresh` peer ranges are unchanged at `^2.0.0`, so applications may stay on any Rspack `2.x` release; only this repository's own development pins and dummy-app lockfiles move, so CI now exercises Rspack 2.2 against the range it already advertises. Annotated config exports (`bin/shakapacker-config --annotate`) also now list the complete `optimization.moduleIds` and `optimization.chunkIds` value sets for both bundlers, including `'compat-hashed'`, which requires Rspack 2.2 or newer and is documented as opt-in rather than made a default.
18
+
19
+ ### Fixed
20
+
21
+ - **Fixed `shakapacker:doctor` not telling Rspack apps that a `.swcrc` is ignored outright.** [PR #1276](https://github.com/shakacode/shakapacker/pull/1276) by [justin808](https://github.com/justin808). Doctor previously told Rspack apps only that moving `.swcrc` settings into `config/swc.config.js` would not help, while deliberately making no claim about the `.swcrc` itself, because the behavior had never been verified. It has now been verified against `@rspack/core` 2.0.1 and 2.2.0: Rspack's `builtin:swc-loader` never reads `.swcrc`. A `.swcrc` setting `jsc.target` and injecting an optimizer global produced byte-identical output to no `.swcrc` at all, while the same options passed inline as loader options did change the output. A `.swcrc` enabling decorator syntax likewise failed to unblock a decorator source, including with empty loader options and with `swcrc`/`configFile` set, so this is not merely inline options taking precedence. Doctor now states that a `.swcrc` is not read on Rspack and that every setting in it is silently inert - the mirror image of the `config/swc.config.js` case in [#1259](https://github.com/shakacode/shakapacker/issues/1259) - and continues to point at overriding the built-in `builtin:swc-loader` rule. Webpack messaging is unchanged. Fixes [#1263](https://github.com/shakacode/shakapacker/issues/1263).
22
+ - **Fixed `shakapacker:doctor` staying silent about SWC configuration on Rspack apps that do not set `javascript_transpiler: swc`.** [PR #1269](https://github.com/shakacode/shakapacker/pull/1269) by [justin808](https://github.com/justin808). Shakapacker's Rspack rules always transpile with `builtin:swc-loader` and never consult `javascript_transpiler`, so `config/swc.config.js` goes unread on an Rspack app configured for Babel or esbuild exactly as it does on one configured for SWC. Doctor's Rspack SWC-configuration warnings were gated on `javascript_transpiler: swc`, so those apps got no warning at all. Those warnings now reach any Rspack app: that `config/swc.config.js` is not read, and, for a `.swcrc`, that moving its settings into `config/swc.config.js` would not affect the build. The webpack-only SWC content checks and the "merged with Shakapacker's defaults" messaging stay gated on `javascript_transpiler: swc`, and apps with `javascript_transpiler: none` remain opted out. Fixes [#1265](https://github.com/shakacode/shakapacker/issues/1265).
23
+ - **Fixed `shakapacker:doctor` and `rake shakapacker:migrate_to_swc` telling Rspack apps that `config/swc.config.js` is merged into their build.** [PR #1260](https://github.com/shakacode/shakapacker/pull/1260) by [justin808](https://github.com/justin808). Rspack builds never read `config/swc.config.js`, because Shakapacker's Rspack rules set their `builtin:swc-loader` options inline. Doctor now warns that the file is unread on Rspack instead of reporting it as merged (and skips its content checks there), and no longer tells Rspack users with a `.swcrc` to migrate those settings into `config/swc.config.js`. The migrator's post-migration guidance and the generated file header instead show how to override the built-in `builtin:swc-loader` rule: apply `webpack-merge`'s `mergeWithRules` to the _output_ of `generateRspackConfig()`, since that function merges with plain `merge`, which concatenates `module.rules`, so an override passed into it is appended alongside the built-in rule rather than replacing it. Webpack apps keep the existing messaging. Fixes [#1259](https://github.com/shakacode/shakapacker/issues/1259).
24
+ - **Fixed post-publish GitHub release failures hiding successful package publication details.** The release task now prints its normal publication summary and the exact `sync_github_release` recovery command before exiting nonzero. [PR #1250](https://github.com/shakacode/shakapacker/pull/1250) by [justin808](https://github.com/justin808).
25
+ - **Fixed helper binstubs to fail closed when `PATH` is unset.** [PR #1240](https://github.com/shakacode/shakapacker/pull/1240) by [justin808](https://github.com/justin808). `bin/shakapacker-config` and `bin/diff-bundler-config` now report the missing-Node error without executing an app-root `node` binary when `PATH` is unset, while explicit `PATH` values retain their existing Node-resolution behavior. Fixes [#1234](https://github.com/shakacode/shakapacker/issues/1234).
26
+ - **Fixed the deprecation warning for the legacy `bundler` config key claiming the value was understood while it was ignored.** [PR #1270](https://github.com/shakacode/shakapacker/pull/1270) by [justin808](https://github.com/justin808). An app whose `config/shakapacker.yml` set only `bundler:` was told the key was deprecated and then built with a different bundler, because `assets_bundler` always resolves from the bundled defaults and the legacy key was never read. The warning now states that the value is ignored, names the bundler actually in use, and tells you to set `assets_bundler` instead. One resolution change accompanies it: a config that leaves `assets_bundler:` blank while setting `bundler:` previously resolved the legacy value in Ruby and now resolves `webpack`, matching what `package/config.ts` already built for it. Fixes [#1262](https://github.com/shakacode/shakapacker/issues/1262).
27
+
13
28
  ## [v10.3.1] - August 3, 2026
14
29
 
15
30
  ### Fixed
@@ -34,7 +49,7 @@
34
49
 
35
50
  - **Fixed implicit SWC defaults for existing webpack/Babel apps without `swc-loader`.** [PR #1206](https://github.com/shakacode/shakapacker/pull/1206) by [justin808](https://github.com/justin808). Webpack apps that omit both `javascript_transpiler` and the deprecated `webpack_loader` now fall back to Babel with a warning when Shakapacker's bundled SWC default is active, `swc-loader` is missing, and Babel is present. Explicit transpiler settings, webpack apps with `swc-loader`, and Rspack's built-in SWC path keep their existing behavior. Closes [#1203](https://github.com/shakacode/shakapacker/issues/1203).
36
51
  - **Fixed JavaScript config loading for missing Rails environments to use the production fallback.** [PR #1206](https://github.com/shakacode/shakapacker/pull/1206) by [justin808](https://github.com/justin808). When `RAILS_ENV` has no matching section in `config/shakapacker.yml`, the Node package config now merges the `production` section instead of only bundled defaults, matching Ruby configuration loading and honoring explicit production `javascript_transpiler`, `source_path`, `dev_server`, and related settings for custom environments such as staging.
37
- - **Fixed helper binstubs delegating Node resolution to Ruby `exec` in unset and empty `PATH` environments.** [PR #1200](https://github.com/shakacode/shakapacker/pull/1200) and [PR #1201](https://github.com/shakacode/shakapacker/pull/1201) by [justin808](https://github.com/justin808). Restores shell-compatible Node lookup for `bin/shakapacker-config` and `bin/diff-bundler-config` after the `v10.2.0` Ruby-binstub regression, while keeping friendly missing-Node errors for `ENOENT` and `EACCES`.
52
+ - **Fixed helper binstub Node resolution after the `v10.2.0` Ruby-binstub regression.** [PR #1200](https://github.com/shakacode/shakapacker/pull/1200) and [PR #1201](https://github.com/shakacode/shakapacker/pull/1201) by [justin808](https://github.com/justin808). Restores shell-compatible Node lookup for `bin/shakapacker-config` and `bin/diff-bundler-config` when `PATH` is empty, while keeping friendly missing-Node errors for `ENOENT` and `EACCES`. For an unset `PATH`, PR #1200 deliberately delegates Node resolution to Ruby `exec`'s default-path behavior; PR #1240 later changed that case to fail closed.
38
53
 
39
54
  ## [v10.2.0] - July 3, 2026
40
55
 
@@ -77,7 +92,7 @@
77
92
  - Optional peers (transpilers, `webpack-dev-server`, CSS preprocessors, react-refresh) stay only if your app uses those features.
78
93
  - Adoption is opt-in: leaving your `package.json` untouched on v10.1 also continues to work.
79
94
 
80
- - **Adopting `shakapacker-webpack` requires `webpack-assets-manifest@^6.0.0`**. Core `shakapacker` still accepts both v5 and v6 (`^5.0.6 || ^6.0.0`), but `shakapacker-webpack` pins `~6.5.1`. Apps still on `webpack-assets-manifest@5.x` must upgrade when switching to the supplemental package; v6 fixed an ENOENT crash on clean builds with `merge: true` and dropped a Node 14 install path. See [the v5→v6 release notes](https://github.com/webdeveric/webpack-assets-manifest/releases) and `packages/shakapacker-webpack/README.md` for details.
95
+ - **Adopting `shakapacker-webpack` requires `webpack-assets-manifest@^6.0.0`**. [PR #1096](https://github.com/shakacode/shakapacker/pull/1096) and [PR #1133](https://github.com/shakacode/shakapacker/pull/1133) by [justin808](https://github.com/justin808). Core `shakapacker` still accepts both v5 and v6 (`^5.0.6 || ^6.0.0`), and `shakapacker-webpack` requires `^6.0.0`. Apps still on `webpack-assets-manifest@5.x` must upgrade when switching to the supplemental package; v6 fixed an ENOENT crash on clean builds with `merge: true` and dropped a Node 14 install path. See [the v5→v6 release notes](https://github.com/webdeveric/webpack-assets-manifest/releases) and `packages/shakapacker-webpack/README.md` for details.
81
96
 
82
97
  ### ⚠️ Breaking Changes
83
98
 
@@ -1000,7 +1015,8 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
1000
1015
 
1001
1016
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
1002
1017
 
1003
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v10.3.1...main
1018
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v10.3.2...main
1019
+ [v10.3.2]: https://github.com/shakacode/shakapacker/compare/v10.3.1...v10.3.2
1004
1020
  [v10.3.1]: https://github.com/shakacode/shakapacker/compare/v10.3.0...v10.3.1
1005
1021
  [v10.3.0]: https://github.com/shakacode/shakapacker/compare/v10.2.0...v10.3.0
1006
1022
  [v10.2.0]: https://github.com/shakacode/shakapacker/compare/v10.1.0...v10.2.0
@@ -24,7 +24,7 @@ def shakapacker_warn_missing_node
24
24
  end
25
25
 
26
26
  def shakapacker_exec_env(launch_dir)
27
- return {} unless ENV.key?("PATH")
27
+ return unless ENV.key?("PATH")
28
28
 
29
29
  path_entries = ENV["PATH"].split(File::PATH_SEPARATOR, -1)
30
30
  path_entries = [""] if path_entries.empty?
@@ -38,12 +38,9 @@ end
38
38
 
39
39
  def shakapacker_exec_node(script_path, argv, launch_dir)
40
40
  exec_env = shakapacker_exec_env(launch_dir)
41
+ shakapacker_warn_missing_node unless exec_env
41
42
 
42
- if exec_env.empty?
43
- exec shakapacker_node_binary, script_path, *argv
44
- else
45
- exec exec_env, shakapacker_node_binary, script_path, *argv
46
- end
43
+ exec exec_env, shakapacker_node_binary, script_path, *argv
47
44
  rescue Errno::ENOENT, Errno::EACCES
48
45
  shakapacker_warn_missing_node
49
46
  end
@@ -24,7 +24,7 @@ def shakapacker_warn_missing_node
24
24
  end
25
25
 
26
26
  def shakapacker_exec_env(launch_dir)
27
- return {} unless ENV.key?("PATH")
27
+ return unless ENV.key?("PATH")
28
28
 
29
29
  path_entries = ENV["PATH"].split(File::PATH_SEPARATOR, -1)
30
30
  path_entries = [""] if path_entries.empty?
@@ -38,12 +38,9 @@ end
38
38
 
39
39
  def shakapacker_exec_node(script_path, argv, launch_dir)
40
40
  exec_env = shakapacker_exec_env(launch_dir)
41
+ shakapacker_warn_missing_node unless exec_env
41
42
 
42
- if exec_env.empty?
43
- exec shakapacker_node_binary, script_path, *argv
44
- else
45
- exec exec_env, shakapacker_node_binary, script_path, *argv
46
- end
43
+ exec exec_env, shakapacker_node_binary, script_path, *argv
47
44
  rescue Errno::ENOENT, Errno::EACCES
48
45
  shakapacker_warn_missing_node
49
46
  end
@@ -303,20 +303,30 @@ class Shakapacker::Configuration
303
303
  # Resolution order:
304
304
  # 1. CLI --bundler flag (via bundler_override)
305
305
  # 2. SHAKAPACKER_ASSETS_BUNDLER environment variable
306
- # 3. assets_bundler setting in config file
307
- # 4. bundler setting in config file (deprecated)
308
- # 5. Defaults to "webpack"
306
+ # 3. assets_bundler setting in config file (or the bundled default)
307
+ # 4. Defaults to "webpack"
308
+ #
309
+ # The legacy 'bundler' setting is not consulted; it is only used to warn that
310
+ # its value is ignored.
309
311
  #
310
312
  # @return [String] "webpack" or "rspack"
311
313
  def assets_bundler
312
314
  # CLI --bundler flag takes highest precedence
313
315
  return @bundler_override if @bundler_override
314
316
 
315
- # Show deprecation warning if using old 'bundler' key
317
+ resolved = ENV["SHAKAPACKER_ASSETS_BUNDLER"] || fetch(:assets_bundler) || "webpack"
318
+
319
+ # The legacy 'bundler' key is never read, so warn that its value is ignored
316
320
  if data.has_key?(:bundler) && !data.has_key?(:assets_bundler)
317
- $stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is deprecated. Please use 'assets_bundler' instead to avoid confusion with Ruby's Bundler gem manager."
321
+ legacy_value = data[:bundler].to_s.strip
322
+ described_value = legacy_value.empty? ? "empty" : legacy_value
323
+ # An empty SHAKAPACKER_ASSETS_BUNDLER resolves to an empty string; name that
324
+ # state rather than printing "using ''". Doctor reports it as its own issue.
325
+ described_resolved = resolved.to_s.empty? ? "no bundler (SHAKAPACKER_ASSETS_BUNDLER is set but empty)" : "'#{resolved}'"
326
+ $stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is no longer supported and its value (#{described_value}) is IGNORED. Shakapacker is using #{described_resolved}. Set 'assets_bundler' in your shakapacker.yml instead — it was renamed to avoid confusion with Ruby's Bundler gem manager."
318
327
  end
319
- ENV["SHAKAPACKER_ASSETS_BUNDLER"] || fetch(:assets_bundler) || fetch(:bundler) || "webpack"
328
+
329
+ resolved
320
330
  end
321
331
 
322
332
  # Deprecated alias for {#assets_bundler}
@@ -75,6 +75,25 @@ module Shakapacker
75
75
 
76
76
  PACKAGE_ROOT_MARKERS = (["package.json"] + PACKAGE_MANAGER_LOCKFILES.keys + ["node_modules"]).freeze
77
77
 
78
+ # generateRspackConfig merges with plain webpack-merge `merge` (package/rspack/index.ts),
79
+ # which concatenates `module.rules`, so an override handed to it is appended next to the
80
+ # built-in rule rather than replacing it. mergeWithRules must be applied to its output.
81
+ RSPACK_SWC_OVERRIDE_HINT =
82
+ "Apply webpack-merge's 'mergeWithRules' to the output of generateRspackConfig() in your Rspack config, " \
83
+ "matching on both 'test' and 'use.loader', and using options: 'merge' (not 'replace', which swaps the whole " \
84
+ "options object and drops the built-in parser and transform.react defaults). " \
85
+ "Keep 'test' in the match: matching on 'use.loader' alone makes webpack-merge append 'builtin:swc-loader' to " \
86
+ "every rule's 'use' chain, including the CSS and Sass rules. " \
87
+ "Override the JS (/\\.(js|jsx|mjs)$/) and TypeScript (/\\.(ts|tsx)$/) rules separately, since Shakapacker " \
88
+ "registers a 'builtin:swc-loader' rule for each. " \
89
+ "Passing an override into generateRspackConfig() cannot replace the built-in rule, because it merges with " \
90
+ "plain 'merge', which concatenates 'module.rules' and leaves the built-in 'builtin:swc-loader' rule in place " \
91
+ "alongside yours. Shakapacker re-exports 'mergeWithRules'. " \
92
+ "See: https://github.com/shakacode/shakapacker/blob/main/docs/rspack.md".freeze
93
+
94
+ RSPACK_SWC_OVERRIDE_HINT_BACKREF =
95
+ "See the Rspack 'builtin:swc-loader' override guidance above.".freeze
96
+
78
97
  def initialize(config = nil, root_path = nil, options = {})
79
98
  @config = config || Shakapacker.config
80
99
  @root_path = root_path || (defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd))
@@ -665,8 +684,11 @@ module Shakapacker
665
684
  add_warning("Both esbuild and Babel dependencies are installed. Consider removing Babel dependencies to reduce node_modules size")
666
685
  end
667
686
 
668
- # Check for SWC configuration conflicts
669
- if transpiler == "swc"
687
+ # Check for SWC configuration conflicts. Rspack always transpiles with
688
+ # builtin:swc-loader, so SWC config files go unread there regardless of
689
+ # javascript_transpiler; the webpack-only content checks stay SWC-gated
690
+ # inside check_swc_config_conflicts.
691
+ if transpiler == "swc" || assets_bundler == "rspack"
670
692
  check_swc_config_conflicts
671
693
  end
672
694
  end
@@ -718,16 +740,48 @@ module Shakapacker
718
740
  swcrc_path = root_path.join(".swcrc")
719
741
  swc_config_path = root_path.join("config/swc.config.js")
720
742
 
743
+ rspack = assets_bundler == "rspack"
744
+
721
745
  if swcrc_path.exist?
722
- add_warning("SWC configuration: .swcrc file detected. This file completely overrides Shakapacker's default SWC settings and may cause build failures. " \
723
- "Please migrate to config/swc.config.js which properly merges with Shakapacker defaults. " \
724
- "To migrate: Move your custom settings from .swcrc to config/swc.config.js (see docs for format). " \
725
- "See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md")
746
+ if rspack
747
+ # Verified against @rspack/core 2.0.1 and 2.2.0: 'builtin:swc-loader' never reads .swcrc.
748
+ # A .swcrc setting jsc.target and an optimizer global produced byte-identical output to
749
+ # no .swcrc, and the same options passed inline did change the output. A .swcrc enabling
750
+ # decorator syntax also failed to unblock a decorator source, including with empty loader
751
+ # options and with swcrc/configFile set, so this is not just inline options winning.
752
+ add_warning("SWC configuration: .swcrc is present but is NOT read when assets_bundler is 'rspack'. " \
753
+ "Rspack transpiles with 'builtin:swc-loader', which ignores .swcrc entirely, so every setting in it is silently inert. " \
754
+ "Moving these settings to config/swc.config.js would not help either - Shakapacker's Rspack build does not read that file.")
755
+ add_rspack_swc_override_hint("To apply custom SWC options on Rspack, override the built-in 'builtin:swc-loader' rule.")
756
+ else
757
+ add_warning("SWC configuration: .swcrc file detected. This file completely overrides Shakapacker's default SWC settings and may cause build failures. " \
758
+ "Please migrate to config/swc.config.js which properly merges with Shakapacker defaults. " \
759
+ "To migrate: Move your custom settings from .swcrc to config/swc.config.js (see docs for format). " \
760
+ "See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md")
761
+ end
726
762
  end
727
763
 
728
- if swc_config_path.exist?
729
- @info << "SWC configuration: Using config/swc.config.js (recommended). This config is merged with Shakapacker's defaults."
730
- check_swc_config_settings(swc_config_path)
764
+ return unless swc_config_path.exist?
765
+
766
+ if rspack
767
+ add_warning("SWC configuration: config/swc.config.js is present but is NOT read when assets_bundler is 'rspack'. " \
768
+ "Shakapacker's Rspack config sets its 'builtin:swc-loader' options inline, so nothing in this file reaches your build.")
769
+ add_rspack_swc_override_hint("Customize SWC for Rspack by overriding the built-in 'builtin:swc-loader' rule instead.")
770
+ return
771
+ end
772
+
773
+ @info << "SWC configuration: Using config/swc.config.js (recommended). This config is merged with Shakapacker's defaults."
774
+ check_swc_config_settings(swc_config_path)
775
+ end
776
+
777
+ # The full hint is ~700 characters, and an Rspack app with both .swcrc and
778
+ # config/swc.config.js hits both call sites, so emit it once and refer back after that.
779
+ def add_rspack_swc_override_hint(lead)
780
+ if @rspack_swc_override_hint_emitted
781
+ add_fix_hint("#{lead} #{RSPACK_SWC_OVERRIDE_HINT_BACKREF}")
782
+ else
783
+ @rspack_swc_override_hint_emitted = true
784
+ add_fix_hint("#{lead} #{RSPACK_SWC_OVERRIDE_HINT}")
731
785
  end
732
786
  end
733
787
 
@@ -46,11 +46,65 @@ module Shakapacker
46
46
  .eslintrc.json
47
47
  ].freeze
48
48
 
49
- DEFAULT_SWC_CONFIG = <<~JS.freeze
49
+ SWC_CONFIG_WEBPACK_HEADER = <<~JS.freeze
50
50
  // config/swc.config.js
51
51
  // This file is merged with Shakapacker's default SWC configuration
52
52
  // See: https://swc.rs/docs/configuration/compilation
53
+ JS
54
+
55
+ # Rspack never reads config/swc.config.js: package/rules/rspack.ts sets its
56
+ # builtin:swc-loader options inline, so the file is generated with a header that says so.
57
+ #
58
+ # The example keeps `test: 'match'` and overrides the JS and TS rules separately on
59
+ # purpose. Matching on `use.loader` alone (a single override with no `test`) looks tidier
60
+ # but is wrong: webpack-merge cannot pair such an override with a specific rule, so it
61
+ # appends builtin:swc-loader onto EVERY rule's `use` chain, including the CSS and Sass
62
+ # rules. Verified at webpack-merge 5.10.0 and 6.0.1.
63
+ SWC_CONFIG_RSPACK_HEADER = <<~JS.freeze
64
+ // config/swc.config.js
65
+ // NOTE: This file is NOT read when assets_bundler is 'rspack'.
66
+ // Shakapacker's Rspack config sets its 'builtin:swc-loader' options inline, so nothing
67
+ // here reaches your build. To customize SWC for Rspack, apply webpack-merge's
68
+ // mergeWithRules to the OUTPUT of generateRspackConfig(). Passing an override into
69
+ // generateRspackConfig() cannot replace the built-in rule: it merges with plain merge,
70
+ // which concatenates module.rules and leaves the built-in rule in place alongside yours.
71
+ //
72
+ // Use options: 'merge', not 'replace'. 'merge' deep-merges into the built-in options so
73
+ // parser.jsx (JS), parser.syntax/tsx (TS), and transform.react.runtime survive.
74
+ // 'replace' swaps the whole options object and silently drops them, breaking JSX and
75
+ // TypeScript compilation.
76
+ //
77
+ // Keep test: 'match' in the merge spec. Matching on use.loader alone appends
78
+ // builtin:swc-loader to every rule's use chain, CSS and Sass included.
79
+ //
80
+ // const { mergeWithRules } = require('shakapacker');
81
+ // const { generateRspackConfig } = require('shakapacker/rspack');
82
+ //
83
+ // const swcOptions = { jsc: { keepClassNames: true } }; // your SWC options
84
+ //
85
+ // module.exports = mergeWithRules({
86
+ // module: { rules: { test: 'match', use: { loader: 'match', options: 'merge' } } },
87
+ // })(generateRspackConfig(), {
88
+ // module: {
89
+ // rules: [
90
+ // // Shakapacker registers separate builtin:swc-loader rules for JS and TS.
91
+ // // Override both: a JS-only override never reaches .ts/.tsx files.
92
+ // {
93
+ // test: /\\.(js|jsx|mjs)$/,
94
+ // use: [{ loader: 'builtin:swc-loader', options: swcOptions }],
95
+ // },
96
+ // {
97
+ // test: /\\.(ts|tsx)$/,
98
+ // use: [{ loader: 'builtin:swc-loader', options: swcOptions }],
99
+ // },
100
+ // ],
101
+ // },
102
+ // });
103
+ //
104
+ // See: https://github.com/shakacode/shakapacker/blob/main/docs/rspack.md
105
+ JS
53
106
 
107
+ SWC_CONFIG_BODY = <<~JS.freeze
54
108
  const { env } = require('shakapacker');
55
109
 
56
110
  module.exports = {
@@ -70,6 +124,11 @@ module Shakapacker
70
124
  };
71
125
  JS
72
126
 
127
+ # Kept for backward compatibility: same value as before, and still the webpack variant.
128
+ DEFAULT_SWC_CONFIG = "#{SWC_CONFIG_WEBPACK_HEADER}\n#{SWC_CONFIG_BODY}".freeze
129
+
130
+ RSPACK_SWC_CONFIG = "#{SWC_CONFIG_RSPACK_HEADER}\n#{SWC_CONFIG_BODY}".freeze
131
+
73
132
  def initialize(root_path, logger: nil)
74
133
  @root_path = Pathname.new(root_path)
75
134
  @logger = logger || Logger.new($stdout)
@@ -89,9 +148,7 @@ module Shakapacker
89
148
  logger.info " Note: SWC is approximately 20x faster than Babel for transpilation."
90
149
  logger.info " Please test your application thoroughly after migration."
91
150
  logger.info "\n📝 Configuration Info:"
92
- logger.info " - config/swc.config.js is merged with Shakapacker's default SWC configuration"
93
- logger.info " - You can customize config/swc.config.js to add additional options"
94
- logger.info " - Avoid using .swcrc as it overrides Shakapacker defaults completely"
151
+ log_swc_config_guidance
95
152
 
96
153
  # Show cleanup recommendations if babel packages found
97
154
  if results[:babel_packages_found].any?
@@ -275,6 +332,48 @@ module Shakapacker
275
332
  {}
276
333
  end
277
334
 
335
+ def log_swc_config_guidance
336
+ if rspack?
337
+ logger.info " - config/swc.config.js is NOT read when assets_bundler is 'rspack'"
338
+ logger.info " Shakapacker's Rspack config sets its 'builtin:swc-loader' options inline"
339
+ logger.info " - To customize SWC for Rspack, apply webpack-merge's 'mergeWithRules' to the"
340
+ logger.info " OUTPUT of generateRspackConfig(), matching on both 'test' and 'use.loader'"
341
+ logger.info " and merging 'use.options'. Matching on 'use.loader' alone would append"
342
+ logger.info " 'builtin:swc-loader' to every rule's 'use' chain, CSS and Sass included."
343
+ logger.info " Passing an override into generateRspackConfig() cannot replace"
344
+ logger.info " the built-in rule, because it merges with plain 'merge', which concatenates"
345
+ logger.info " 'module.rules' and leaves the built-in rule in place alongside yours"
346
+ logger.info " - Use options: 'merge', not 'replace', so the built-in parser and"
347
+ logger.info " transform.react defaults survive, and override the JS and TS rules separately"
348
+ logger.info " - The generated config/swc.config.js header contains a copy-pasteable example"
349
+ logger.info " - See: https://github.com/shakacode/shakapacker/blob/main/docs/rspack.md"
350
+ else
351
+ logger.info " - config/swc.config.js is merged with Shakapacker's default SWC configuration"
352
+ logger.info " - You can customize config/swc.config.js to add additional options"
353
+ logger.info " - Avoid using .swcrc as it overrides Shakapacker defaults completely"
354
+ end
355
+ end
356
+
357
+ def rspack?
358
+ assets_bundler == "rspack"
359
+ end
360
+
361
+ # Resolves through Shakapacker's own configuration rather than re-reading
362
+ # config/shakapacker.yml here, so this guidance can never disagree with the bundler the
363
+ # build actually uses. Instance handles SHAKAPACKER_CONFIG, Env.inquire (Rails.env plus
364
+ # the production fallback), and Configuration's own precedence. Falls back to webpack
365
+ # when the config is missing or unparseable, since migration must not crash on it.
366
+ def assets_bundler
367
+ return @assets_bundler if defined?(@assets_bundler)
368
+
369
+ @assets_bundler = begin
370
+ require "shakapacker"
371
+ Shakapacker::Instance.new(root_path: root_path).config.assets_bundler
372
+ rescue StandardError, LoadError
373
+ "webpack"
374
+ end
375
+ end
376
+
278
377
  def create_swc_config
279
378
  config_dir = root_path.join("config")
280
379
  swc_config_path = config_dir.join("swc.config.js")
@@ -287,7 +386,7 @@ module Shakapacker
287
386
  FileUtils.mkdir_p(config_dir) unless config_dir.exist?
288
387
 
289
388
  logger.info "📄 Creating config/swc.config.js..."
290
- File.write(swc_config_path, DEFAULT_SWC_CONFIG)
389
+ File.write(swc_config_path, rspack? ? RSPACK_SWC_CONFIG : DEFAULT_SWC_CONFIG)
291
390
  logger.info "✅ config/swc.config.js created"
292
391
  true
293
392
  rescue StandardError => e
@@ -33,7 +33,11 @@ module Shakapacker
33
33
  [true, "true", "yes", 1, "1", "t"].include?(value.instance_of?(String) ? value.downcase : value)
34
34
  end
35
35
 
36
- # Executes a string or an array of strings in a shell in the given directory in an unbundled environment
36
+ # Executes a string or an array of strings in a shell in the given directory.
37
+ # NOTE: this does NOT unbundle. The caller's Bundler environment (BUNDLE_GEMFILE,
38
+ # RUBYOPT, ...) is inherited, so a `bundle` command run here against a different
39
+ # directory's Gemfile still resolves the caller's Gemfile. Wrap the call in
40
+ # Bundler.with_unbundled_env when that matters.
37
41
  def self.sh_in_dir(dir, *shell_commands)
38
42
  shell_commands.flatten.each { |shell_command| sh %(cd '#{dir}' && #{shell_command.strip}) }
39
43
  end
@@ -1,4 +1,4 @@
1
1
  module Shakapacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "10.3.1".freeze
3
+ VERSION = "10.3.2".freeze
4
4
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "10.3.1",
3
+ "version": "10.3.2",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "homepage": "https://github.com/shakacode/shakapacker",
6
6
  "bugs": {
@@ -58,8 +58,8 @@
58
58
  "@babel/core": "7.29.0",
59
59
  "@eslint/eslintrc": "^3.2.0",
60
60
  "@eslint/js": "^9.37.0",
61
- "@rspack/cli": "2.0.1",
62
- "@rspack/core": "2.0.1",
61
+ "@rspack/cli": "2.2.0",
62
+ "@rspack/core": "2.2.0",
63
63
  "@swc/core": "1.15.33",
64
64
  "@types/babel__core": "^7.20.5",
65
65
  "@types/js-yaml": "^4.0.9",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shakapacker
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.3.1
4
+ version: 10.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -225,7 +225,7 @@ homepage: https://github.com/shakacode/shakapacker
225
225
  licenses:
226
226
  - MIT
227
227
  metadata:
228
- source_code_uri: https://github.com/shakacode/shakapacker/tree/v10.3.1
228
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v10.3.2
229
229
  rdoc_options: []
230
230
  require_paths:
231
231
  - lib
@@ -240,7 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
240
240
  - !ruby/object:Gem::Version
241
241
  version: '0'
242
242
  requirements: []
243
- rubygems_version: 3.6.9
243
+ rubygems_version: 4.0.18
244
244
  specification_version: 4
245
245
  summary: Use webpack to manage app-like JavaScript modules in Rails
246
246
  test_files: []