shakapacker 10.2.0 → 10.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 441942bb193dab20df1d0956197e3d441adb6fef5e25001a4eb1f05c69543312
4
- data.tar.gz: 4aea05e5fa5f72da3b65fd108514400ca6c7140c85cdf7d10aa1d6edf6118d43
3
+ metadata.gz: 9066233e8c71edd7018db4b397e132013a8dcde79a111acbd6f3a79617fa9367
4
+ data.tar.gz: 1e7d899486f7bf0c43cebb923ba4a013bd5bdb7e4348193662872a5b77278208
5
5
  SHA512:
6
- metadata.gz: a2d3907c1a2d5121c7b70f72cb46c8ea09157e263241803b67ce2d250f7183121442bc0e0b6387d1552008565a3802e5a57c6447f4f6bee0d3e3f9214f16786f
7
- data.tar.gz: 3b59158ac878b1403defdc3a22293a2559bb99d13c106175d2c72d637596ad4dfbdd5174aea80222070f50ee911bb7e585a1cae830886bb75be9f942f310e6ed
6
+ metadata.gz: 6becce7cfd7598aeb8b6cb734882e0dbd6668125e61c711608d81e242724c8b27d613efdf5cecff77e737ca46d8f8c5c727938c5bbb13551f3394f7149d495a0
7
+ data.tar.gz: f56d65242942ce8ce7b372af52b28a7a62dd6ce47c7fce5b4268bd638d22691276166796620179682b0b61ad2cc4028d951219d208652f84a99cb60bf0d8757a
data/CHANGELOG.md CHANGED
@@ -1,4 +1,5 @@
1
1
  - For the changelog of versions prior to v6, see the [5.x stable branch of rails/webpacker](https://github.com/rails/webpacker/tree/5-x-stable).
2
+ - **Please see the [v10 Upgrade Guide](./docs/v10_upgrade.md) for upgrading to version 10 and accounting for breaking changes.**
2
3
  - **Please see the [v9 Upgrade Guide](./docs/v9_upgrade.md) for upgrading to version 9 and accounting for breaking changes.**
3
4
  - Please see the [v8 Upgrade Guide](./docs/v8_upgrade.md) for upgrading to version 8 and accounting for breaking changes.
4
5
  - Please see the [v7 Upgrade Guide](./docs/v7_upgrade.md) for upgrading to new spelling in version 7.
@@ -9,6 +10,18 @@
9
10
 
10
11
  ## [Unreleased]
11
12
 
13
+ ## [v10.3.0] - July 5, 2026
14
+
15
+ ### Added
16
+
17
+ - **Allowed `webpack-dev-server` v6**. [PR #1102](https://github.com/shakacode/shakapacker/pull/1102) by [G-Rath](https://github.com/G-Rath).
18
+
19
+ ### Fixed
20
+
21
+ - **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).
22
+ - **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.
23
+ - **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`.
24
+
12
25
  ## [v10.2.0] - July 3, 2026
13
26
 
14
27
  ### Added
@@ -973,7 +986,8 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
973
986
 
974
987
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
975
988
 
976
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v10.2.0...main
989
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v10.3.0...main
990
+ [v10.3.0]: https://github.com/shakacode/shakapacker/compare/v10.2.0...v10.3.0
977
991
  [v10.2.0]: https://github.com/shakacode/shakapacker/compare/v10.1.0...v10.2.0
978
992
  [v10.1.0]: https://github.com/shakacode/shakapacker/compare/v10.0.0...v10.1.0
979
993
  [v10.0.0]: https://github.com/shakacode/shakapacker/compare/v9.7.0...v10.0.0
data/README.md CHANGED
@@ -114,7 +114,8 @@ and release-to-release upgrade paths.
114
114
 
115
115
  Older major-version docs:
116
116
 
117
- - [v10.0.0 release notes](https://github.com/shakacode/shakapacker/releases/tag/v10.0.0)
117
+ - [v10 upgrade guide](https://github.com/shakacode/shakapacker/blob/main/docs/v10_upgrade.md)
118
+ ([docs site](https://shakapacker.com/docs/v10_upgrade/))
118
119
  - [v9 upgrade guide](https://shakapacker.com/docs/v9_upgrade/)
119
120
  - [v8 upgrade guide](https://shakapacker.com/docs/v8_upgrade/)
120
121
  - [v7 upgrade guide](https://shakapacker.com/docs/v7_upgrade/)
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "rbconfig"
5
4
  require "yaml"
6
5
 
7
6
  # This binstub is managed by Shakapacker. Delete it and rerun the install or init command to regenerate it.
@@ -14,36 +13,39 @@ def shakapacker_app_root
14
13
  Dir.pwd
15
14
  end
16
15
 
17
- def shakapacker_executable_candidates(executable)
18
- extensions = [
19
- RbConfig::CONFIG["EXEEXT"],
20
- *ENV.fetch("PATHEXT", "").split(File::PATH_SEPARATOR)
21
- ].compact.reject(&:empty?)
22
- return [executable] if extensions.empty? || File.extname(executable) != ""
16
+ def shakapacker_node_binary
17
+ "node"
18
+ end
23
19
 
24
- ([executable] + extensions.map { |extension| "#{executable}#{extension}" }).uniq
20
+ def shakapacker_warn_missing_node
21
+ warn '[Shakapacker] Could not find Node.js executable "node". ' \
22
+ "Install Node.js and try again."
23
+ exit 1
25
24
  end
26
25
 
27
- def shakapacker_find_executable(executable)
28
- ENV.fetch("PATH", "").split(File::PATH_SEPARATOR, -1).each do |path|
29
- search_path = path.empty? ? Dir.pwd : path
26
+ def shakapacker_exec_env(launch_dir)
27
+ return {} unless ENV.key?("PATH")
30
28
 
31
- shakapacker_executable_candidates(executable).each do |candidate|
32
- executable_path = File.join(search_path, candidate)
33
- return executable_path if File.file?(executable_path) && File.executable?(executable_path)
34
- end
35
- end
29
+ path_entries = ENV["PATH"].split(File::PATH_SEPARATOR, -1)
30
+ path_entries = [""] if path_entries.empty?
36
31
 
37
- nil
32
+ normalized_path = path_entries.map do |entry|
33
+ entry.empty? ? launch_dir : File.absolute_path(entry, launch_dir)
34
+ end.join(File::PATH_SEPARATOR)
35
+
36
+ { "PATH" => normalized_path }
38
37
  end
39
38
 
40
- def shakapacker_node_binary
41
- node_bin = shakapacker_find_executable("node")
42
- return node_bin if node_bin
39
+ def shakapacker_exec_node(script_path, argv, launch_dir)
40
+ exec_env = shakapacker_exec_env(launch_dir)
43
41
 
44
- warn '[Shakapacker] Could not find Node.js executable "node". ' \
45
- "Install Node.js and try again."
46
- exit 1
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
47
+ rescue Errno::ENOENT, Errno::EACCES
48
+ shakapacker_warn_missing_node
47
49
  end
48
50
 
49
51
  def shakapacker_node_env
@@ -130,9 +132,9 @@ end
130
132
  ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
131
133
  ENV["NODE_ENV"] ||= shakapacker_node_env
132
134
 
135
+ launch_dir = Dir.pwd
133
136
  app_root = shakapacker_app_root
134
137
  client_root = shakapacker_client_root(app_root)
135
- node_bin = shakapacker_node_binary
136
138
  script_paths = shakapacker_package_script_paths(app_root, client_root, "diff-bundler-config.cjs")
137
139
  script_path = script_paths.find { |path| File.file?(path) }
138
140
 
@@ -142,5 +144,5 @@ unless script_path
142
144
  end
143
145
 
144
146
  Dir.chdir(app_root) do
145
- exec node_bin, script_path, *ARGV
147
+ shakapacker_exec_node(script_path, ARGV, launch_dir)
146
148
  end
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "rbconfig"
5
4
  require "yaml"
6
5
 
7
6
  # This binstub is managed by Shakapacker. Delete it and rerun the install or init command to regenerate it.
@@ -14,36 +13,39 @@ def shakapacker_app_root
14
13
  Dir.pwd
15
14
  end
16
15
 
17
- def shakapacker_executable_candidates(executable)
18
- extensions = [
19
- RbConfig::CONFIG["EXEEXT"],
20
- *ENV.fetch("PATHEXT", "").split(File::PATH_SEPARATOR)
21
- ].compact.reject(&:empty?)
22
- return [executable] if extensions.empty? || File.extname(executable) != ""
16
+ def shakapacker_node_binary
17
+ "node"
18
+ end
23
19
 
24
- ([executable] + extensions.map { |extension| "#{executable}#{extension}" }).uniq
20
+ def shakapacker_warn_missing_node
21
+ warn '[Shakapacker] Could not find Node.js executable "node". ' \
22
+ "Install Node.js and try again."
23
+ exit 1
25
24
  end
26
25
 
27
- def shakapacker_find_executable(executable)
28
- ENV.fetch("PATH", "").split(File::PATH_SEPARATOR, -1).each do |path|
29
- search_path = path.empty? ? Dir.pwd : path
26
+ def shakapacker_exec_env(launch_dir)
27
+ return {} unless ENV.key?("PATH")
30
28
 
31
- shakapacker_executable_candidates(executable).each do |candidate|
32
- executable_path = File.join(search_path, candidate)
33
- return executable_path if File.file?(executable_path) && File.executable?(executable_path)
34
- end
35
- end
29
+ path_entries = ENV["PATH"].split(File::PATH_SEPARATOR, -1)
30
+ path_entries = [""] if path_entries.empty?
36
31
 
37
- nil
32
+ normalized_path = path_entries.map do |entry|
33
+ entry.empty? ? launch_dir : File.absolute_path(entry, launch_dir)
34
+ end.join(File::PATH_SEPARATOR)
35
+
36
+ { "PATH" => normalized_path }
38
37
  end
39
38
 
40
- def shakapacker_node_binary
41
- node_bin = shakapacker_find_executable("node")
42
- return node_bin if node_bin
39
+ def shakapacker_exec_node(script_path, argv, launch_dir)
40
+ exec_env = shakapacker_exec_env(launch_dir)
43
41
 
44
- warn '[Shakapacker] Could not find Node.js executable "node". ' \
45
- "Install Node.js and try again."
46
- exit 1
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
47
+ rescue Errno::ENOENT, Errno::EACCES
48
+ shakapacker_warn_missing_node
47
49
  end
48
50
 
49
51
  def shakapacker_node_env
@@ -130,9 +132,9 @@ end
130
132
  ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
131
133
  ENV["NODE_ENV"] ||= shakapacker_node_env
132
134
 
135
+ launch_dir = Dir.pwd
133
136
  app_root = shakapacker_app_root
134
137
  client_root = shakapacker_client_root(app_root)
135
- node_bin = shakapacker_node_binary
136
138
  script_paths = shakapacker_package_script_paths(app_root, client_root, "shakapacker-config.cjs")
137
139
  script_path = script_paths.find { |path| File.file?(path) }
138
140
 
@@ -142,5 +144,5 @@ unless script_path
142
144
  end
143
145
 
144
146
  Dir.chdir(app_root) do
145
- exec node_bin, script_path, *ARGV
147
+ shakapacker_exec_node(script_path, ARGV, launch_dir)
146
148
  end
@@ -59,7 +59,9 @@ default: &default
59
59
  cache_manifest: false
60
60
 
61
61
  # Select JavaScript transpiler to use
62
- # Available options: 'swc' (default, 20x faster), 'babel', 'esbuild', or 'none'
62
+ # Available options: 'swc' (bundled default), 'babel', 'esbuild', or 'none'
63
+ # Webpack apps that omit this setting use the bundled SWC default when swc-loader is
64
+ # installed, but fall back to Babel with a warning when only Babel tooling is present.
63
65
  # Use 'none' when providing a completely custom webpack configuration
64
66
  # Note: When using rspack, swc is used automatically regardless of this setting
65
67
  javascript_transpiler: "swc"
@@ -11,7 +11,7 @@
11
11
  "webpack": "^5.101.0",
12
12
  "webpack-assets-manifest": "^5.0.6 || ^6.0.0",
13
13
  "webpack-cli": "^4.9.2 || ^5.0.0 || ^6.0.0 || ^7.0.0",
14
- "webpack-dev-server": "^5.2.2",
14
+ "webpack-dev-server": "^5.2.2 || ^6.0.0",
15
15
  "webpack-merge": "^5.8.0 || ^6.0.0",
16
16
  "webpack-subresource-integrity": "^5.1.0"
17
17
  },
@@ -50,6 +50,13 @@ class Shakapacker::Configuration
50
50
  /\A(?:--config|-c|--node-env|--nodeEnv|--bundler|--build|--init|--list-builds)(?:=.*)?\z/
51
51
  private_constant :SHAKAPACKER_MANAGED_COMPILE_FLAG_PATTERN
52
52
 
53
+ IMPLICIT_SWC_BABEL_FALLBACK_WARNING =
54
+ "`javascript_transpiler` is not set in config/shakapacker.yml. " \
55
+ "Shakapacker defaults to SWC, but swc-loader is not installed and Babel was detected, so Babel will be used. " \
56
+ "Set `javascript_transpiler: babel` (or `swc`) explicitly to silence this message. " \
57
+ "See https://github.com/shakacode/shakapacker/blob/main/docs/transpiler-migration.md"
58
+ private_constant :IMPLICIT_SWC_BABEL_FALLBACK_WARNING
59
+
53
60
  DISALLOWED_WEBPACK_COMPILE_FLAGS =
54
61
  (SHAKAPACKER_NODE_FLAGS + SHAKAPACKER_RUNNER_COMMANDS + SHAKAPACKER_WATCH_FLAGS +
55
62
  SHAKAPACKER_MANAGED_COMPILE_FLAGS).freeze
@@ -357,24 +364,54 @@ class Shakapacker::Configuration
357
364
  # Resolution order:
358
365
  # 1. javascript_transpiler setting in config file
359
366
  # 2. webpack_loader setting in config file (deprecated)
360
- # 3. Default based on bundler (swc for rspack, babel for webpack)
367
+ # 3. Bundled defaults
368
+ # 4. Babel fallback for implicit webpack/SWC defaults when swc-loader is missing
361
369
  #
362
370
  # Validates that the configured transpiler matches installed packages.
363
371
  #
364
372
  # @return [String] "babel", "swc", or "esbuild"
365
373
  def javascript_transpiler
366
- # Show deprecation warning if using old 'webpack_loader' key
367
- if data.has_key?(:webpack_loader) && !data.has_key?(:javascript_transpiler)
368
- $stderr.puts "⚠️ DEPRECATION WARNING: The 'webpack_loader' configuration option is deprecated. Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
369
- end
374
+ return @javascript_transpiler if defined?(@javascript_transpiler)
375
+
376
+ @javascript_transpiler =
377
+ begin
378
+ javascript_transpiler_configured = config_value_configured?(:javascript_transpiler)
379
+ webpack_loader_configured = config_value_present?(:webpack_loader)
380
+
381
+ # Show deprecation warning if using old 'webpack_loader' key
382
+ if webpack_loader_configured && !javascript_transpiler_configured
383
+ $stderr.puts "⚠️ DEPRECATION WARNING: The 'webpack_loader' configuration option is deprecated. Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
384
+ end
385
+
386
+ # Use explicit config if set, otherwise default based on bundler
387
+ current_javascript_transpiler =
388
+ if javascript_transpiler_configured
389
+ fetch(:javascript_transpiler)
390
+ elsif data.has_key?(:javascript_transpiler)
391
+ defaults[:javascript_transpiler]
392
+ else
393
+ fetch(:javascript_transpiler)
394
+ end
395
+
396
+ transpiler =
397
+ if webpack_loader_configured && !javascript_transpiler_configured
398
+ fetch(:webpack_loader) || default_javascript_transpiler
399
+ else
400
+ current_javascript_transpiler || fetch(:webpack_loader) || default_javascript_transpiler
401
+ end
370
402
 
371
- # Use explicit config if set, otherwise default based on bundler
372
- transpiler = fetch(:javascript_transpiler) || fetch(:webpack_loader) || default_javascript_transpiler
403
+ if !javascript_transpiler_configured &&
404
+ !webpack_loader_configured &&
405
+ implicit_swc_to_babel_fallback?(transpiler)
406
+ $stderr.puts IMPLICIT_SWC_BABEL_FALLBACK_WARNING
407
+ transpiler = "babel"
408
+ end
373
409
 
374
- # Validate transpiler configuration
375
- validate_transpiler_configuration(transpiler) unless self.class.installing
410
+ # Validate transpiler configuration
411
+ validate_transpiler_configuration(transpiler) unless self.class.installing
376
412
 
377
- transpiler
413
+ transpiler
414
+ end
378
415
  end
379
416
 
380
417
  # Deprecated alias for {#javascript_transpiler}
@@ -461,42 +498,124 @@ class Shakapacker::Configuration
461
498
  rspack? ? "swc" : "babel"
462
499
  end
463
500
 
501
+ def implicit_swc_to_babel_fallback?(transpiler)
502
+ webpack? &&
503
+ transpiler == "swc" &&
504
+ !package_dependency?("swc-loader") &&
505
+ package_dependency?("babel-loader")
506
+ end
507
+
508
+ def package_dependency?(package_name)
509
+ declared_package_dependencies.key?(package_name)
510
+ end
511
+
512
+ def config_value_present?(key)
513
+ return false unless data.has_key?(key)
514
+
515
+ value = data[key]
516
+ value.is_a?(String) && !value.strip.empty?
517
+ end
518
+
519
+ def config_value_configured?(key)
520
+ return false unless data.has_key?(key)
521
+
522
+ value = data[key]
523
+ return false if value.nil?
524
+ return false if value.is_a?(String) && value.strip.empty?
525
+
526
+ true
527
+ end
528
+
529
+ def declared_package_dependencies
530
+ return @declared_package_dependencies if defined?(@declared_package_dependencies)
531
+
532
+ @declared_package_dependencies =
533
+ package_json_paths.reverse_each.each_with_object({}) do |path, dependencies|
534
+ package_json = parse_package_json(path)
535
+ next unless package_json
536
+
537
+ dependencies.merge!(installable_package_dependencies(package_json))
538
+ end
539
+ end
540
+
541
+ def installable_package_dependencies(package_json)
542
+ (package_json["optionalDependencies"] || {})
543
+ .merge(package_json["devDependencies"] || {})
544
+ .merge(package_json["dependencies"] || {})
545
+ end
546
+
547
+ def package_json_paths
548
+ @package_json_paths ||= package_root_paths
549
+ .map { |path| path.join("package.json") }
550
+ .select(&:exist?)
551
+ end
552
+
553
+ def parse_package_json(path)
554
+ JSON.parse(File.read(path))
555
+ rescue JSON::ParserError, SystemCallError
556
+ nil
557
+ end
558
+
559
+ def package_root_paths
560
+ @package_root_paths ||= [javascript_package_root_path, root_path].uniq
561
+ end
562
+
563
+ def javascript_package_root_path
564
+ @javascript_package_root_path ||= begin
565
+ resolved_source_path = source_path.expand_path
566
+ app_root = root_path.expand_path
567
+
568
+ if path_within?(resolved_source_path, app_root)
569
+ current = resolved_source_path
570
+ loop do
571
+ break current if current.join("package.json").exist?
572
+ break root_path if current == app_root
573
+
574
+ parent = current.dirname
575
+ break root_path if parent == current
576
+
577
+ current = parent
578
+ end
579
+ else
580
+ root_path
581
+ end
582
+ rescue StandardError
583
+ root_path
584
+ end
585
+ end
586
+
587
+ def path_within?(path, parent)
588
+ path.to_s == parent.to_s || path.to_s.start_with?("#{parent}#{File::SEPARATOR}")
589
+ end
590
+
464
591
  def validate_transpiler_configuration(transpiler)
465
592
  return unless ENV["NODE_ENV"] != "test" # Skip validation in test environment
466
593
 
467
594
  # Skip validation if transpiler is set to 'none' (custom webpack config)
468
595
  return if transpiler == "none"
469
596
 
470
- # Check if package.json exists
471
- package_json_path = root_path.join("package.json")
472
- return unless package_json_path.exist?
597
+ all_deps = declared_package_dependencies
598
+ return if all_deps.empty?
473
599
 
474
- begin
475
- package_json = JSON.parse(File.read(package_json_path))
476
- all_deps = (package_json["dependencies"] || {}).merge(package_json["devDependencies"] || {})
477
-
478
- # Check for transpiler mismatch
479
- has_babel = all_deps.keys.any? { |pkg| pkg.start_with?("@babel/", "babel-") }
480
- has_swc = all_deps.keys.any? { |pkg| pkg.include?("swc") }
481
- has_esbuild = all_deps.keys.any? { |pkg| pkg.include?("esbuild") }
482
-
483
- case transpiler
484
- when "babel"
485
- if !has_babel && has_swc
486
- warn_transpiler_mismatch("Babel", "SWC packages found but Babel is configured")
487
- end
488
- when "swc"
489
- if !has_swc && has_babel
490
- warn_transpiler_mismatch("SWC", "Babel packages found but SWC is configured")
491
- end
492
- when "esbuild"
493
- if !has_esbuild && (has_babel || has_swc)
494
- other = has_babel ? "Babel" : "SWC"
495
- warn_transpiler_mismatch("esbuild", "#{other} packages found but esbuild is configured")
496
- end
600
+ # Check for transpiler mismatch
601
+ has_babel = all_deps.keys.any? { |pkg| pkg.start_with?("@babel/", "babel-") }
602
+ has_swc = all_deps.keys.any? { |pkg| pkg.include?("swc") }
603
+ has_esbuild = all_deps.keys.any? { |pkg| pkg.include?("esbuild") }
604
+
605
+ case transpiler
606
+ when "babel"
607
+ if !has_babel && has_swc
608
+ warn_transpiler_mismatch("Babel", "SWC packages found but Babel is configured")
609
+ end
610
+ when "swc"
611
+ if !has_swc && has_babel
612
+ warn_transpiler_mismatch("SWC", "Babel packages found but SWC is configured")
613
+ end
614
+ when "esbuild"
615
+ if !has_esbuild && (has_babel || has_swc)
616
+ other = has_babel ? "Babel" : "SWC"
617
+ warn_transpiler_mismatch("esbuild", "#{other} packages found but esbuild is configured")
497
618
  end
498
- rescue JSON::ParserError
499
- # Ignore if package.json is malformed
500
619
  end
501
620
  end
502
621
 
@@ -511,12 +511,14 @@ module Shakapacker
511
511
 
512
512
  def check_javascript_transpiler_dependencies
513
513
  transpiler = explicit_javascript_transpiler
514
+ implicit_babel_fallback = false
514
515
 
515
516
  if transpiler.nil?
516
- @info << "No javascript_transpiler configured - defaulting to SWC (20x faster than Babel)"
517
- transpiler = "swc"
517
+ transpiler = javascript_transpiler
518
+ implicit_babel_fallback = transpiler == "babel"
518
519
  end
519
520
 
521
+ @resolved_javascript_transpiler = transpiler
520
522
  return if transpiler == "none"
521
523
 
522
524
  bundler = assets_bundler
@@ -541,7 +543,7 @@ module Shakapacker
541
543
  case transpiler
542
544
  when "babel"
543
545
  check_babel_dependencies
544
- check_babel_performance_suggestion
546
+ check_babel_performance_suggestion unless implicit_babel_fallback
545
547
  when "swc"
546
548
  check_swc_dependencies(bundler) unless inferred_hybrid_graph
547
549
  when "esbuild"
@@ -573,6 +575,20 @@ module Shakapacker
573
575
  @info << "Consider switching to SWC for 20x faster compilation. Set javascript_transpiler: 'swc' in shakapacker.yml"
574
576
  end
575
577
 
578
+ def implicit_javascript_transpiler
579
+ if assets_bundler == "webpack" && !package_installed?("swc-loader") && package_installed?("babel-loader")
580
+ @info << "`javascript_transpiler` is not set in config/shakapacker.yml. " \
581
+ "Shakapacker defaults to SWC, but swc-loader is not installed and Babel was detected, so Babel will be used. " \
582
+ "Set `javascript_transpiler: babel` (or `swc`) explicitly to silence this message. " \
583
+ "See https://github.com/shakacode/shakapacker/blob/main/docs/transpiler-migration.md"
584
+ "babel"
585
+ else
586
+ @info << "No javascript_transpiler configured - using bundled SWC default. " \
587
+ "Set javascript_transpiler: 'swc' or 'babel' explicitly in shakapacker.yml to silence this message."
588
+ "swc"
589
+ end
590
+ end
591
+
576
592
  def check_swc_dependencies(bundler)
577
593
  if bundler == "webpack"
578
594
  unless package_installed?("@swc/core")
@@ -1271,13 +1287,20 @@ module Shakapacker
1271
1287
 
1272
1288
  def javascript_transpiler_configured?
1273
1289
  !javascript_transpiler_env_override.nil? ||
1274
- config_key_defined?(:javascript_transpiler) ||
1275
- config_key_defined?(:webpack_loader)
1290
+ config_key_configured?(:javascript_transpiler) ||
1291
+ config_key_present?(:webpack_loader)
1276
1292
  end
1277
1293
 
1278
1294
  def javascript_transpiler
1279
- transpiler = javascript_transpiler_env_override || config.javascript_transpiler
1280
- blank_config_value?(transpiler) ? default_javascript_transpiler : transpiler
1295
+ return @resolved_javascript_transpiler if defined?(@resolved_javascript_transpiler)
1296
+
1297
+ @resolved_javascript_transpiler =
1298
+ if javascript_transpiler_configured?
1299
+ transpiler = javascript_transpiler_env_override || config.javascript_transpiler
1300
+ blank_config_value?(transpiler) ? default_javascript_transpiler : transpiler
1301
+ else
1302
+ implicit_javascript_transpiler
1303
+ end
1281
1304
  end
1282
1305
 
1283
1306
  def explicit_javascript_transpiler
@@ -1329,7 +1352,7 @@ module Shakapacker
1329
1352
  end
1330
1353
 
1331
1354
  def blank_config_value?(value)
1332
- value.nil? || (value.respond_to?(:empty?) && value.empty?)
1355
+ value.nil? || (value.is_a?(String) && value.strip.empty?) || (value.respond_to?(:empty?) && value.empty?)
1333
1356
  end
1334
1357
 
1335
1358
  def default_javascript_transpiler
@@ -1337,14 +1360,15 @@ module Shakapacker
1337
1360
  end
1338
1361
 
1339
1362
  def config_key_present?(key)
1340
- !blank_config_value?(config_value(key))
1363
+ value = config_value(key)
1364
+ value.is_a?(String) && !value.strip.empty?
1341
1365
  end
1342
1366
 
1343
- def config_key_defined?(key)
1367
+ def config_key_configured?(key)
1344
1368
  return false unless config.respond_to?(:data)
1345
1369
 
1346
1370
  data = config.data
1347
- data.respond_to?(:key?) && data.key?(key)
1371
+ data.respond_to?(:key?) && data.key?(key) && !blank_config_value?(data[key])
1348
1372
  end
1349
1373
 
1350
1374
  def config_value(key)
@@ -1383,7 +1407,7 @@ module Shakapacker
1383
1407
  if path_within?(source_path, app_root)
1384
1408
  current = source_path
1385
1409
  loop do
1386
- break current if package_root_marker?(current)
1410
+ break current if current.join("package.json").exist?
1387
1411
  break root_path if current == app_root
1388
1412
 
1389
1413
  parent = current.dirname
@@ -1458,7 +1482,7 @@ module Shakapacker
1458
1482
  def detect_package_manager
1459
1483
  root_package_manager = package_manager_for(root_path)
1460
1484
 
1461
- package_root_paths.each do |package_root|
1485
+ package_manager_root_paths.each do |package_root|
1462
1486
  next if package_root == root_path
1463
1487
 
1464
1488
  package_manager_name = package_manager_for(package_root)
@@ -1478,6 +1502,34 @@ module Shakapacker
1478
1502
  nil
1479
1503
  end
1480
1504
 
1505
+ def package_manager_root_paths
1506
+ @package_manager_root_paths ||= [javascript_package_manager_root_path, root_path].uniq
1507
+ end
1508
+
1509
+ def javascript_package_manager_root_path
1510
+ @javascript_package_manager_root_path ||= begin
1511
+ source_path = config.source_path.expand_path
1512
+ app_root = root_path.expand_path
1513
+
1514
+ if path_within?(source_path, app_root)
1515
+ current = source_path
1516
+ loop do
1517
+ break current if package_root_marker?(current)
1518
+ break root_path if current == app_root
1519
+
1520
+ parent = current.dirname
1521
+ break root_path if parent == current
1522
+
1523
+ current = parent
1524
+ end
1525
+ else
1526
+ root_path
1527
+ end
1528
+ rescue StandardError
1529
+ root_path
1530
+ end
1531
+ end
1532
+
1481
1533
  def versions_compatible?(gem_version, npm_version)
1482
1534
  # Handle pre-release versions and ranges properly
1483
1535
  npm_clean = npm_version.gsub(/[\^~]/, "")
@@ -1,4 +1,4 @@
1
1
  module Shakapacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "10.2.0".freeze
3
+ VERSION = "10.3.0".freeze
4
4
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "10.2.0",
3
+ "version": "10.3.0",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "homepage": "https://github.com/shakacode/shakapacker",
6
6
  "bugs": {
@@ -127,7 +127,7 @@
127
127
  "webpack": "^5.101.0",
128
128
  "webpack-assets-manifest": "^5.0.6 || ^6.0.0",
129
129
  "webpack-cli": "^4.9.2 || ^5.0.0 || ^6.0.0 || ^7.0.0",
130
- "webpack-dev-server": "^5.2.2",
130
+ "webpack-dev-server": "^5.2.2 || ^6.0.0",
131
131
  "webpack-subresource-integrity": "^5.1.0"
132
132
  },
133
133
  "peerDependenciesMeta": {
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.2.0
4
+ version: 10.3.0
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.2.0
228
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v10.3.0
229
229
  rdoc_options: []
230
230
  require_paths:
231
231
  - lib