shakapacker 10.3.0 → 10.3.1

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: 9066233e8c71edd7018db4b397e132013a8dcde79a111acbd6f3a79617fa9367
4
- data.tar.gz: 1e7d899486f7bf0c43cebb923ba4a013bd5bdb7e4348193662872a5b77278208
3
+ metadata.gz: 66455bdf084a5e63422cf5741b2ce549b2dc77dcd36553e8e5fa7dcc6ce34286
4
+ data.tar.gz: a7030178f8ec26e3c1243fbdeb806c2746d6b14484bc99142a35f3fa3f57e9dd
5
5
  SHA512:
6
- metadata.gz: 6becce7cfd7598aeb8b6cb734882e0dbd6668125e61c711608d81e242724c8b27d613efdf5cecff77e737ca46d8f8c5c727938c5bbb13551f3394f7149d495a0
7
- data.tar.gz: f56d65242942ce8ce7b372af52b28a7a62dd6ce47c7fce5b4268bd638d22691276166796620179682b0b61ad2cc4028d951219d208652f84a99cb60bf0d8757a
6
+ metadata.gz: ec0476cbbaaa7044f9f06a3fe207caad61350c309164deffa47d5990e583b025c03e235b4e0e110adac394461fce264b887a71de6159a43ce5664e7a8a133992
7
+ data.tar.gz: 3d625e63b6f838c9febaaad2dda13d43cd7486748d7e39d6ff09e5675d06ab8b85414b9d323c41a3b09fca5adb19ec193b6d90d5651dce92f8573cd18e23937f
data/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [v10.3.1] - August 3, 2026
14
+
15
+ ### Fixed
16
+
17
+ - **Fixed 502 responses for proxied dev server assets under `rack-proxy` v1.** [PR #1222](https://github.com/shakacode/shakapacker/pull/1222) by [jcbpl](https://github.com/jcbpl). Fixes [#1220](https://github.com/shakacode/shakapacker/issues/1220).
18
+ - **Fixed dev-server liveness checks treating refused macOS 27 connections as running.** The Ruby probe now verifies the connected socket's `SO_ERROR` result before proxying asset requests, avoiding false-positive dev-server detection and resulting 502 responses. [PR #1225](https://github.com/shakacode/shakapacker/pull/1225) by [justin808](https://github.com/justin808). Fixes [#1224](https://github.com/shakacode/shakapacker/issues/1224).
19
+ - **Fixed webpack Babel, SWC, and esbuild rules skipping explicitly included `.cjs` files.** [PR #1219](https://github.com/shakacode/shakapacker/pull/1219) by [oiahoon](https://github.com/oiahoon). Fixes [#1218](https://github.com/shakacode/shakapacker/issues/1218).
20
+ - **Added a `shakapacker:doctor` warning for Rspack React Refresh v2 configs that still use the v1 default-export constructor pattern.** [PR #1207](https://github.com/shakacode/shakapacker/pull/1207) by [justin808](https://github.com/justin808). Existing configs with `const ReactRefreshPlugin = require("@rspack/plugin-react-refresh")` followed by `new ReactRefreshPlugin()` can fail after upgrading to `@rspack/plugin-react-refresh` v2 with `ReactRefreshPlugin is not a constructor`; Doctor now points to the affected JS/TS config file and suggests the named-export/default/module compatibility form. Fixes [#1204](https://github.com/shakacode/shakapacker/issues/1204).
21
+ - **Fixed the missing-`@babel/core` failure to report an actionable install message.** [PR #1212](https://github.com/shakacode/shakapacker/pull/1212) by [justin808](https://github.com/justin808). Babel-transpiled builds whose app lacks `@babel/core` previously surfaced a raw module-resolution error from the Babel 8 compatibility check; the rule now explains which package to install and how to switch `javascript_transpiler` instead. Refs [#1163](https://github.com/shakacode/shakapacker/issues/1163).
22
+
23
+ ### Documentation
24
+
25
+ - **Documented the required `css-loader@^7.1.4` in the v10 Rspack upgrade instructions.** [PR #1211](https://github.com/shakacode/shakapacker/pull/1211) by [justin808](https://github.com/justin808). The v10 upgrade guide's copy-paste Rspack v2 commands omitted `css-loader`, so apps following them could upgrade into an unsatisfied peer dependency.
26
+
13
27
  ## [v10.3.0] - July 5, 2026
14
28
 
15
29
  ### Added
@@ -986,7 +1000,8 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
986
1000
 
987
1001
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
988
1002
 
989
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v10.3.0...main
1003
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v10.3.1...main
1004
+ [v10.3.1]: https://github.com/shakacode/shakapacker/compare/v10.3.0...v10.3.1
990
1005
  [v10.3.0]: https://github.com/shakacode/shakapacker/compare/v10.2.0...v10.3.0
991
1006
  [v10.2.0]: https://github.com/shakacode/shakapacker/compare/v10.1.0...v10.2.0
992
1007
  [v10.1.0]: https://github.com/shakacode/shakapacker/compare/v10.0.0...v10.1.0
@@ -47,8 +47,13 @@ class Shakapacker::DevServer
47
47
  # @return [Boolean] true if the dev server is running
48
48
  def running?
49
49
  if config.dev_server.present?
50
- Socket.tcp(host, port, connect_timeout: connect_timeout).close
51
- true
50
+ socket = Socket.tcp(host, port, connect_timeout: connect_timeout)
51
+
52
+ begin
53
+ socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).int.zero?
54
+ ensure
55
+ socket.close
56
+ end
52
57
  else
53
58
  false
54
59
  end
@@ -11,6 +11,10 @@ class Shakapacker::DevServerProxy < Rack::Proxy
11
11
 
12
12
  def perform_request(env)
13
13
  if env["PATH_INFO"].start_with?("/#{public_output_uri_path}") && dev_server.running?
14
+ # rack-proxy v1 refuses to derive the backend from the Host header.
15
+ env["rack.backend"] = URI::Generic.build(
16
+ scheme: dev_server.protocol, host: dev_server.host, port: dev_server.port
17
+ )
14
18
  env["HTTP_HOST"] = env["HTTP_X_FORWARDED_HOST"] = dev_server.host
15
19
  env["HTTP_X_FORWARDED_SERVER"] = dev_server.host_with_port
16
20
  env["HTTP_PORT"] = env["HTTP_X_FORWARDED_PORT"] = dev_server.port.to_s
@@ -59,6 +59,9 @@ module Shakapacker
59
59
  "@rspack/plugin-react-refresh" => "^2.0.0"
60
60
  }.freeze
61
61
 
62
+ RSPACK_REACT_REFRESH_PACKAGE = "@rspack/plugin-react-refresh".freeze
63
+ VERSION_UPPER_BOUND_OPERATORS = %w[< <=].freeze
64
+
62
65
  CUSTOM_HYBRID_LOADER_DEPS = %w[
63
66
  babel-loader
64
67
  esbuild-loader
@@ -170,6 +173,7 @@ module Shakapacker
170
173
  check_css_modules_configuration
171
174
  check_bundler_dependencies if config_exists?
172
175
  check_rspack_cache_configuration if config_exists?
176
+ check_rspack_react_refresh_plugin_constructor if config_exists?
173
177
  check_file_type_dependencies if config_exists?
174
178
  check_sri_dependencies if config_exists?
175
179
  check_peer_dependencies
@@ -878,14 +882,89 @@ module Shakapacker
878
882
  "See https://rspack.rs/config/cache for options.")
879
883
  end
880
884
 
885
+ def check_rspack_react_refresh_plugin_constructor
886
+ return unless rspack_react_refresh_check_applicable?
887
+ return unless rspack_react_refresh_plugin_v2_or_newer?
888
+
889
+ rspack_react_refresh_config_paths.each do |path|
890
+ content = File.read(path)
891
+ next unless legacy_rspack_react_refresh_constructor?(content)
892
+
893
+ relative = config_path_for_warning(path)
894
+ add_action_required("Rspack React Refresh config #{relative} uses the " \
895
+ "#{RSPACK_REACT_REFRESH_PACKAGE} v1 default-export constructor pattern. " \
896
+ "With #{RSPACK_REACT_REFRESH_PACKAGE} v2, rspack may fail with " \
897
+ "'ReactRefreshPlugin is not a constructor'.")
898
+ add_fix_hint("Use a compat constructor: const ReactRefresh = require(\"#{RSPACK_REACT_REFRESH_PACKAGE}\"); " \
899
+ "const ReactRefreshRspackPlugin = ReactRefresh.ReactRefreshRspackPlugin || " \
900
+ "ReactRefresh.default || ReactRefresh; then call new ReactRefreshRspackPlugin().")
901
+ rescue SystemCallError => e
902
+ add_info_warning("Unable to validate Rspack React Refresh config #{config_path_for_warning(path)}: #{e.message}")
903
+ end
904
+ end
905
+
906
+ def rspack_react_refresh_check_applicable?
907
+ assets_bundler == "rspack" || unconfigured_hybrid_loader_graph?
908
+ end
909
+
910
+ def rspack_react_refresh_plugin_v2_or_newer?
911
+ version = installed_package_version(RSPACK_REACT_REFRESH_PACKAGE)
912
+ return version >= Gem::Version.new("2.0.0") if version
913
+
914
+ package_specifier_allows_version_or_newer?(
915
+ package_json_dependency_version(RSPACK_REACT_REFRESH_PACKAGE),
916
+ Gem::Version.new("2.0.0")
917
+ )
918
+ end
919
+
920
+ def rspack_react_refresh_config_paths
921
+ config_dir = rspack_react_refresh_config_dir
922
+ config_dir_path = Pathname.new(File.join(root_path.to_s, config_dir))
923
+ return [] unless config_dir_path.directory?
924
+
925
+ path = active_bundler_config_path_in(config_dir)
926
+ path ? [path] : []
927
+ end
928
+
929
+ def rspack_react_refresh_config_dir
930
+ config_dir = config.assets_bundler_config_path.to_s
931
+
932
+ if assets_bundler == "rspack" || same_directory_hybrid_config_present?(config_dir)
933
+ config_dir
934
+ else
935
+ "config/rspack"
936
+ end
937
+ end
938
+
939
+ def legacy_rspack_react_refresh_constructor?(content)
940
+ stripped = strip_rspack_config_comments_and_literals(content, preserve_literals: [RSPACK_REACT_REFRESH_PACKAGE])
941
+ constructor_names = []
942
+
943
+ constructor_names.concat(stripped.scan(
944
+ /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*require\(\s*["']#{Regexp.escape(RSPACK_REACT_REFRESH_PACKAGE)}["']\s*\)(?=\s*(?:[;,\)]|$))/
945
+ ).flatten)
946
+ constructor_names.concat(stripped.scan(
947
+ /\bimport\s+([A-Za-z_$][\w$]*)\s*(?:,\s*\{[^}]*\}\s*)?\s+from\s*["']#{Regexp.escape(RSPACK_REACT_REFRESH_PACKAGE)}["']/
948
+ ).flatten)
949
+ constructor_names.concat(stripped.scan(
950
+ /\bimport\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*["']#{Regexp.escape(RSPACK_REACT_REFRESH_PACKAGE)}["']/
951
+ ).flatten)
952
+
953
+ constructor_names.uniq.any? do |constructor_name|
954
+ stripped.match?(/\bnew\s+#{Regexp.escape(constructor_name)}\s*\(/)
955
+ end
956
+ end
957
+
881
958
  # Returns the single active config path the runner would load, or nil. Mirrors the
882
959
  # resolution order in Shakapacker::Runner#find_rspack_config_with_fallback so the
883
960
  # doctor inspects the same file the build will actually use (and so unused sibling
884
961
  # configs in the same directory don't trigger spurious warnings).
885
962
  # NOTE: keep this candidate list in sync with Runner#find_rspack_config_with_fallback.
886
963
  def active_assets_bundler_config_path
887
- config_dir = config.assets_bundler_config_path.to_s
964
+ active_bundler_config_path_in(config.assets_bundler_config_path.to_s)
965
+ end
888
966
 
967
+ def active_bundler_config_path_in(config_dir)
889
968
  candidates = %w[ts js].map { |ext| Pathname.new(File.join(root_path.to_s, config_dir, "rspack.config.#{ext}")) }
890
969
  candidates += %w[ts js].map { |ext| Pathname.new(File.join(root_path.to_s, config_dir, "webpack.config.#{ext}")) }
891
970
  if default_rspack_config_dir?(config_dir)
@@ -944,7 +1023,7 @@ module Shakapacker
944
1023
  stripped.gsub(%r{/(?:\\.|\[[^\]\n]*\]|[^/\n\\\[])+?/[gimsuy]*}, "")
945
1024
  end
946
1025
 
947
- def strip_rspack_config_comments_and_literals(content)
1026
+ def strip_rspack_config_comments_and_literals(content, preserve_literals: [])
948
1027
  stripped = +""
949
1028
  index = 0
950
1029
 
@@ -987,8 +1066,12 @@ module Shakapacker
987
1066
  end
988
1067
 
989
1068
  literal = content[index...literal_end]
990
- stripped << '""'
991
- stripped << literal.gsub(/[^\n]/, "")
1069
+ if preserved_js_string_literal?(literal, char, preserve_literals)
1070
+ stripped << literal
1071
+ else
1072
+ stripped << '""'
1073
+ stripped << literal.gsub(/[^\n]/, "")
1074
+ end
992
1075
  index = literal_end
993
1076
  elsif char == "`"
994
1077
  literal_end = index + 1
@@ -1029,6 +1112,12 @@ module Shakapacker
1029
1112
  stripped
1030
1113
  end
1031
1114
 
1115
+ def preserved_js_string_literal?(literal, quote, preserve_literals)
1116
+ return false unless literal.end_with?(quote)
1117
+
1118
+ preserve_literals.include?(literal[1...-1])
1119
+ end
1120
+
1032
1121
  def direct_export_cache_disabled?(stripped)
1033
1122
  # Match `cache: false` only near an exported config object at brace depth
1034
1123
  # 1. This avoids warning on local base config objects while still
@@ -1183,8 +1272,9 @@ module Shakapacker
1183
1272
  return versions.min
1184
1273
  end
1185
1274
 
1275
+ return package_version_from_range_specifier(cleaned) if cleaned.match?(/[<>=]/)
1186
1276
  return nil unless cleaned.match?(/\A[\^~]?\d/)
1187
- return nil if cleaned.match?(/(\s|\|\||[<>=:]|\A(?:git|file|link|workspace|npm):)/)
1277
+ return nil if cleaned.match?(/(\s|\|\||:|\A(?:git|file|link|workspace|npm):)/)
1188
1278
 
1189
1279
  match = cleaned.sub(/\A[\^~]/, "").match(/\A(\d+(?:\.\d+){0,2})(?:-[0-9A-Za-z.-]+)?\z/)
1190
1280
  match && Gem::Version.new(match[1])
@@ -1192,6 +1282,48 @@ module Shakapacker
1192
1282
  nil
1193
1283
  end
1194
1284
 
1285
+ def package_version_from_range_specifier(specifier)
1286
+ lower_bounds = specifier.scan(/(?:\A|\s)(?:>=|>)\s*(\d+(?:\.\d+){0,2})(?:-[0-9A-Za-z.-]+)?/)
1287
+ versions = lower_bounds.flatten.map { |version| Gem::Version.new(version) }
1288
+
1289
+ versions.max
1290
+ rescue ArgumentError
1291
+ nil
1292
+ end
1293
+
1294
+ def package_specifier_allows_version_or_newer?(specifier, minimum)
1295
+ return false unless specifier
1296
+
1297
+ cleaned = specifier.strip
1298
+ if cleaned.include?("||")
1299
+ return cleaned.split("||").any? { |part| package_specifier_allows_version_or_newer?(part, minimum) }
1300
+ end
1301
+ return false if cleaned.match?(/(:|\A(?:git|file|link|workspace|npm):)/)
1302
+
1303
+ if cleaned.match?(/[<>=]/)
1304
+ return package_range_specifier_allows_version_or_newer?(cleaned, minimum)
1305
+ end
1306
+
1307
+ version = package_version_from_specifier(cleaned)
1308
+ version && version >= minimum
1309
+ end
1310
+
1311
+ def package_range_specifier_allows_version_or_newer?(specifier, minimum)
1312
+ comparators = specifier.scan(/(?:\A|\s)(<=|<|>=|>|=)\s*(\d+(?:\.\d+){0,2})(?:-[0-9A-Za-z.-]+)?/)
1313
+ return false if comparators.empty?
1314
+
1315
+ equalities = comparators.select { |operator, _version| operator == "=" }
1316
+ return equalities.any? { |_operator, version| Gem::Version.new(version) >= minimum } if equalities.any?
1317
+
1318
+ upper_bounds = comparators.select { |operator, _version| VERSION_UPPER_BOUND_OPERATORS.include?(operator) }
1319
+ upper_bounds.all? do |operator, version|
1320
+ parsed = Gem::Version.new(version)
1321
+ operator == "<" ? parsed > minimum : parsed >= minimum
1322
+ end
1323
+ rescue ArgumentError
1324
+ false
1325
+ end
1326
+
1195
1327
  def declared_package_dependencies
1196
1328
  @declared_package_dependencies ||= begin
1197
1329
  package_json_paths.reverse_each.each_with_object({}) do |path, dependencies|
@@ -1,4 +1,4 @@
1
1
  module Shakapacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "10.3.0".freeze
3
+ VERSION = "10.3.1".freeze
4
4
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "10.3.0",
3
+ "version": "10.3.1",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "homepage": "https://github.com/shakacode/shakapacker",
6
6
  "bugs": {
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.0
4
+ version: 10.3.1
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.0
228
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v10.3.1
229
229
  rdoc_options: []
230
230
  require_paths:
231
231
  - lib