shakapacker 8.4.0 → 9.7.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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/address-review.md +206 -0
  3. data/.claude/commands/update-changelog.md +354 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
  6. data/.github/STATUS.md +1 -0
  7. data/.github/actionlint-matcher.json +17 -0
  8. data/.github/workflows/claude-code-review.yml +45 -0
  9. data/.github/workflows/claude.yml +55 -0
  10. data/.github/workflows/dummy.yml +18 -5
  11. data/.github/workflows/eslint-validation.yml +46 -0
  12. data/.github/workflows/generator.yml +38 -22
  13. data/.github/workflows/node.yml +116 -2
  14. data/.github/workflows/ruby.yml +57 -15
  15. data/.github/workflows/test-bundlers.yml +180 -0
  16. data/.gitignore +27 -0
  17. data/.husky/pre-commit +2 -0
  18. data/.npmignore +56 -0
  19. data/.prettierignore +7 -0
  20. data/.rubocop.yml +2 -0
  21. data/.yalcignore +26 -0
  22. data/CHANGELOG.md +487 -19
  23. data/CLAUDE.md +63 -0
  24. data/CONTRIBUTING.md +268 -21
  25. data/ESLINT_TECHNICAL_DEBT.md +165 -0
  26. data/README.md +497 -137
  27. data/Rakefile +44 -4
  28. data/TODO.md +58 -0
  29. data/TODO_v9.md +97 -0
  30. data/bin/conductor-exec +24 -0
  31. data/bin/shakapacker-config +11 -0
  32. data/conductor-setup.sh +147 -0
  33. data/conductor.json +9 -0
  34. data/docs/api-reference.md +519 -0
  35. data/docs/cdn_setup.md +384 -0
  36. data/docs/common-upgrades.md +695 -0
  37. data/docs/configuration.md +845 -0
  38. data/docs/css-modules-export-mode.md +566 -0
  39. data/docs/customizing_babel_config.md +16 -16
  40. data/docs/deployment.md +78 -7
  41. data/docs/developing_shakapacker.md +6 -0
  42. data/docs/early_hints.md +433 -0
  43. data/docs/early_hints_manual_api.md +454 -0
  44. data/docs/feature_testing.md +492 -0
  45. data/docs/node_package_api.md +70 -0
  46. data/docs/optional-peer-dependencies.md +203 -0
  47. data/docs/peer-dependencies.md +71 -0
  48. data/docs/precompile_hook.md +486 -0
  49. data/docs/preventing_fouc.md +132 -0
  50. data/docs/react.md +58 -48
  51. data/docs/releasing.md +288 -0
  52. data/docs/rspack.md +218 -0
  53. data/docs/rspack_migration_guide.md +862 -0
  54. data/docs/sprockets.md +1 -0
  55. data/docs/style_loader_vs_mini_css.md +12 -12
  56. data/docs/subresource_integrity.md +13 -7
  57. data/docs/transpiler-migration.md +212 -0
  58. data/docs/transpiler-performance.md +200 -0
  59. data/docs/troubleshooting.md +272 -24
  60. data/docs/typescript-migration.md +388 -0
  61. data/docs/typescript.md +103 -0
  62. data/docs/using_esbuild_loader.md +12 -12
  63. data/docs/using_swc_loader.md +121 -16
  64. data/docs/v6_upgrade.md +42 -19
  65. data/docs/v7_upgrade.md +8 -6
  66. data/docs/v8_upgrade.md +13 -12
  67. data/docs/v9_upgrade.md +616 -0
  68. data/eslint.config.fast.js +254 -0
  69. data/eslint.config.js +309 -0
  70. data/jest.config.js +8 -1
  71. data/knip.ts +61 -0
  72. data/lib/install/bin/shakapacker +4 -6
  73. data/lib/install/bin/shakapacker-config +11 -0
  74. data/lib/install/bin/shakapacker-dev-server +1 -1
  75. data/lib/install/binstubs.rb +6 -2
  76. data/lib/install/config/rspack/rspack.config.js +6 -0
  77. data/lib/install/config/rspack/rspack.config.ts +7 -0
  78. data/lib/install/config/shakapacker.yml +75 -12
  79. data/lib/install/config/webpack/webpack.config.ts +7 -0
  80. data/lib/install/package.json +38 -0
  81. data/lib/install/template.rb +207 -45
  82. data/lib/shakapacker/build_config_loader.rb +147 -0
  83. data/lib/shakapacker/bundler_switcher.rb +415 -0
  84. data/lib/shakapacker/compiler.rb +87 -0
  85. data/lib/shakapacker/configuration.rb +475 -6
  86. data/lib/shakapacker/dev_server.rb +88 -1
  87. data/lib/shakapacker/dev_server_runner.rb +240 -6
  88. data/lib/shakapacker/doctor.rb +1191 -0
  89. data/lib/shakapacker/env.rb +19 -3
  90. data/lib/shakapacker/helper.rb +411 -14
  91. data/lib/shakapacker/install/env.rb +33 -0
  92. data/lib/shakapacker/instance.rb +93 -4
  93. data/lib/shakapacker/manifest.rb +167 -30
  94. data/lib/shakapacker/railtie.rb +4 -0
  95. data/lib/shakapacker/rspack_runner.rb +19 -0
  96. data/lib/shakapacker/runner.rb +668 -9
  97. data/lib/shakapacker/swc_migrator.rb +384 -0
  98. data/lib/shakapacker/utils/manager.rb +2 -0
  99. data/lib/shakapacker/utils/version_syntax_converter.rb +1 -1
  100. data/lib/shakapacker/version.rb +1 -1
  101. data/lib/shakapacker/version_checker.rb +1 -1
  102. data/lib/shakapacker/webpack_runner.rb +4 -42
  103. data/lib/shakapacker.rb +159 -1
  104. data/lib/tasks/shakapacker/binstubs.rake +4 -2
  105. data/lib/tasks/shakapacker/check_binstubs.rake +2 -2
  106. data/lib/tasks/shakapacker/doctor.rake +48 -0
  107. data/lib/tasks/shakapacker/export_bundler_config.rake +68 -0
  108. data/lib/tasks/shakapacker/install.rake +16 -4
  109. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  110. data/lib/tasks/shakapacker/switch_bundler.rake +72 -0
  111. data/lib/tasks/shakapacker.rake +2 -0
  112. data/package/.npmignore +4 -0
  113. data/package/babel/preset.ts +59 -0
  114. data/package/config.ts +189 -0
  115. data/package/configExporter/buildValidator.ts +906 -0
  116. data/package/configExporter/cli.ts +1748 -0
  117. data/package/configExporter/configDocs.ts +102 -0
  118. data/package/configExporter/configFile.ts +663 -0
  119. data/package/configExporter/fileWriter.ts +112 -0
  120. data/package/configExporter/index.ts +15 -0
  121. data/package/configExporter/types.ts +159 -0
  122. data/package/configExporter/yamlSerializer.ts +391 -0
  123. data/package/dev_server.ts +27 -0
  124. data/package/env.ts +92 -0
  125. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +36 -0
  126. data/package/environments/base.ts +147 -0
  127. data/package/environments/development.ts +88 -0
  128. data/package/environments/production.ts +82 -0
  129. data/package/environments/test.ts +55 -0
  130. data/package/environments/types.ts +98 -0
  131. data/package/esbuild/index.ts +40 -0
  132. data/package/index.d.ts +68 -93
  133. data/package/index.d.ts.template +72 -0
  134. data/package/index.ts +104 -0
  135. data/package/loaders.d.ts +28 -0
  136. data/package/optimization/rspack.ts +36 -0
  137. data/package/optimization/webpack.ts +55 -0
  138. data/package/plugins/envFilter.ts +82 -0
  139. data/package/plugins/rspack.ts +119 -0
  140. data/package/plugins/webpack.ts +82 -0
  141. data/package/rspack/index.ts +91 -0
  142. data/package/rules/{babel.js → babel.ts} +2 -2
  143. data/package/rules/{coffee.js → coffee.ts} +1 -1
  144. data/package/rules/css.ts +3 -0
  145. data/package/rules/{erb.js → erb.ts} +1 -1
  146. data/package/rules/esbuild.ts +10 -0
  147. data/package/rules/file.ts +41 -0
  148. data/package/rules/{jscommon.js → jscommon.ts} +5 -4
  149. data/package/rules/{less.js → less.ts} +4 -4
  150. data/package/rules/raw.ts +28 -0
  151. data/package/rules/rspack.ts +174 -0
  152. data/package/rules/sass.ts +21 -0
  153. data/package/rules/{stylus.js → stylus.ts} +4 -8
  154. data/package/rules/swc.ts +10 -0
  155. data/package/rules/{index.js → webpack.ts} +1 -2
  156. data/package/swc/index.ts +54 -0
  157. data/package/types/README.md +90 -0
  158. data/package/types/index.ts +69 -0
  159. data/package/types.ts +105 -0
  160. data/package/utils/bundlerUtils.ts +232 -0
  161. data/package/utils/configPath.ts +6 -0
  162. data/package/utils/debug.ts +45 -0
  163. data/package/utils/defaultConfigPath.ts +7 -0
  164. data/package/utils/ensureManifestExists.ts +17 -0
  165. data/package/utils/errorCodes.ts +249 -0
  166. data/package/utils/errorHelpers.ts +152 -0
  167. data/package/utils/getStyleRule.ts +75 -0
  168. data/package/utils/helpers.ts +99 -0
  169. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  170. data/package/utils/pathValidation.ts +207 -0
  171. data/package/utils/requireOrError.ts +24 -0
  172. data/package/utils/snakeToCamelCase.ts +5 -0
  173. data/package/utils/typeGuards.ts +388 -0
  174. data/package/utils/validateDependencies.ts +61 -0
  175. data/package/webpack-types.d.ts +33 -0
  176. data/package/webpackDevServerConfig.ts +130 -0
  177. data/package.json +157 -18
  178. data/scripts/remove-use-strict.js +44 -0
  179. data/scripts/type-check-no-emit.js +27 -0
  180. data/shakapacker.gemspec +4 -2
  181. data/sig/shakapacker/commands.rbs +35 -0
  182. data/sig/shakapacker/compiler.rbs +65 -0
  183. data/sig/shakapacker/compiler_strategy.rbs +41 -0
  184. data/sig/shakapacker/configuration.rbs +140 -0
  185. data/sig/shakapacker/dev_server.rbs +56 -0
  186. data/sig/shakapacker/env.rbs +25 -0
  187. data/sig/shakapacker/helper.rbs +98 -0
  188. data/sig/shakapacker/instance.rbs +46 -0
  189. data/sig/shakapacker/manifest.rbs +69 -0
  190. data/sig/shakapacker/version.rbs +4 -0
  191. data/sig/shakapacker.rbs +66 -0
  192. data/test/configExporter/buildValidator.test.js +1295 -0
  193. data/test/configExporter/configFile.test.js +393 -0
  194. data/test/configExporter/integration.test.js +262 -0
  195. data/test/helpers.js +1 -1
  196. data/test/package/bundlerUtils.rspack.test.js +145 -0
  197. data/test/package/bundlerUtils.test.js +97 -0
  198. data/test/package/config.test.js +14 -0
  199. data/test/package/configExporter/cli.test.js +440 -0
  200. data/test/package/configExporter/types.test.js +163 -0
  201. data/test/package/configExporter.test.js +491 -0
  202. data/test/package/env.test.js +42 -7
  203. data/test/package/environments/base.test.js +14 -4
  204. data/test/package/helpers.test.js +2 -2
  205. data/test/package/plugins/envFiltering.test.js +453 -0
  206. data/test/package/plugins/webpackSubresourceIntegrity.test.js +89 -0
  207. data/test/package/rspack/index.test.js +293 -0
  208. data/test/package/rspack/optimization.test.js +86 -0
  209. data/test/package/rspack/plugins.test.js +185 -0
  210. data/test/package/rspack/rules.test.js +229 -0
  211. data/test/package/rules/babel.test.js +65 -38
  212. data/test/package/rules/esbuild.test.js +13 -4
  213. data/test/package/rules/file.test.js +7 -1
  214. data/test/package/rules/raw.test.js +40 -7
  215. data/test/package/rules/sass-version-parsing.test.js +71 -0
  216. data/test/package/rules/sass.test.js +11 -6
  217. data/test/package/rules/sass1.test.js +8 -5
  218. data/test/package/rules/sass16.test.js +24 -0
  219. data/test/package/rules/swc.test.js +50 -39
  220. data/test/package/rules/webpack.test.js +35 -0
  221. data/test/package/staging.test.js +4 -3
  222. data/test/package/transpiler-defaults.test.js +169 -0
  223. data/test/package/utils/ensureManifestExists.test.js +51 -0
  224. data/test/package/yamlSerializer.test.js +204 -0
  225. data/test/peer-dependencies.sh +85 -0
  226. data/test/resolver.js +34 -3
  227. data/test/scripts/remove-use-strict.test.js +125 -0
  228. data/test/typescript/build.test.js +118 -0
  229. data/test/typescript/environments.test.js +107 -0
  230. data/test/typescript/pathValidation.test.js +186 -0
  231. data/test/typescript/requireOrError.test.js +49 -0
  232. data/test/typescript/securityValidation.test.js +182 -0
  233. data/tools/README.md +134 -0
  234. data/tools/css-modules-v9-codemod.js +179 -0
  235. data/tsconfig.eslint.json +9 -0
  236. data/tsconfig.json +38 -0
  237. data/yarn.lock +3202 -1097
  238. metadata +212 -44
  239. data/.eslintignore +0 -4
  240. data/.eslintrc.js +0 -36
  241. data/Gemfile.lock +0 -251
  242. data/package/babel/preset.js +0 -48
  243. data/package/config.js +0 -56
  244. data/package/dev_server.js +0 -23
  245. data/package/env.js +0 -48
  246. data/package/environments/base.js +0 -171
  247. data/package/environments/development.js +0 -13
  248. data/package/environments/production.js +0 -88
  249. data/package/environments/test.js +0 -3
  250. data/package/esbuild/index.js +0 -40
  251. data/package/index.js +0 -40
  252. data/package/rules/css.js +0 -3
  253. data/package/rules/esbuild.js +0 -10
  254. data/package/rules/file.js +0 -29
  255. data/package/rules/raw.js +0 -5
  256. data/package/rules/sass.js +0 -18
  257. data/package/rules/swc.js +0 -10
  258. data/package/swc/index.js +0 -50
  259. data/package/utils/configPath.js +0 -4
  260. data/package/utils/defaultConfigPath.js +0 -2
  261. data/package/utils/getStyleRule.js +0 -40
  262. data/package/utils/helpers.js +0 -62
  263. data/package/utils/snakeToCamelCase.js +0 -5
  264. data/package/webpackDevServerConfig.js +0 -71
  265. data/test/package/rules/index.test.js +0 -16
@@ -11,8 +11,8 @@ def verify_file_existence(binstub_file)
11
11
  puts <<~MSG
12
12
  Couldn't find shakapacker binstubs!
13
13
  Possible solutions:
14
- - Ensure you have run `rails shakapacker:install`.
15
- - Run `rails shakapacker:binstubs` if you have already installed shakapacker.
14
+ - Ensure you have run `bundle exec rake shakapacker:install`.
15
+ - Run `bundle exec rake shakapacker:binstubs` if you have already installed shakapacker.
16
16
  - Ensure the `bin` directory, `bin/shakapacker`, and `bin/shakapacker-dev-server` are not included in .gitignore.
17
17
  MSG
18
18
  exit!
@@ -0,0 +1,48 @@
1
+ require "shakapacker/doctor"
2
+
3
+ namespace :shakapacker do
4
+ desc <<~DESC
5
+ Checks for common Shakapacker configuration issues and missing dependencies
6
+
7
+ Performs comprehensive diagnostics including:
8
+ • Configuration file validity and deprecated settings
9
+ • Entry points, output paths, and asset compilation status
10
+ • Node.js and package manager installation
11
+ • Required and optional npm dependencies
12
+ • JavaScript transpiler (Babel, SWC, esbuild) configuration
13
+ • CSS, CSS Modules, and stylesheet preprocessor setup
14
+ • Binstubs presence (shakapacker, shakapacker-dev-server, shakapacker-config)
15
+ • Version consistency between gem and npm package
16
+ • Legacy Webpacker file detection
17
+
18
+ Options:
19
+ --help Show detailed help and usage information
20
+ --verbose Display additional diagnostic details (paths, versions, environment)
21
+
22
+ Examples:
23
+ bundle exec rake shakapacker:doctor
24
+ bundle exec rake shakapacker:doctor -- --verbose
25
+ bundle exec rake shakapacker:doctor -- --help
26
+
27
+ Exit codes:
28
+ 0 - No issues found
29
+ 1 - Issues or warnings detected (see output for details)
30
+ DESC
31
+ task doctor: :environment do
32
+ # Parse command-line options
33
+ options = {}
34
+ ARGV.each do |arg|
35
+ case arg
36
+ when "--help", "-h"
37
+ options[:help] = true
38
+ when "--verbose", "-v"
39
+ options[:verbose] = true
40
+ end
41
+ end
42
+
43
+ Shakapacker::Doctor.new(nil, nil, options).run
44
+
45
+ # Prevent rake from treating options as task names
46
+ ARGV.each { |arg| task arg.to_sym do; end if arg.start_with?("--", "-") }
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ namespace :shakapacker do
2
+ desc <<~DESC
3
+ Export webpack or rspack configuration for debugging and analysis
4
+
5
+ Exports your resolved webpack/rspack configuration in human-readable formats.
6
+ Use this to debug configuration issues, compare environments, or analyze
7
+ client vs server bundle differences.
8
+
9
+ Usage:
10
+ rake shakapacker:export_bundler_config -- [OPTIONS]
11
+
12
+ Quick Start (Recommended):
13
+ rails shakapacker:export_bundler_config --doctor
14
+
15
+ This exports all configs (dev + prod, client + server) to shakapacker-config-exports/
16
+ directory in annotated YAML format - perfect for troubleshooting.
17
+
18
+ Common Options:
19
+ --doctor Export everything for troubleshooting (recommended)
20
+ --save Save current environment configs to files
21
+ --save-dir=<dir> Custom output directory (requires --save)
22
+ --env=development|production|test Specify environment
23
+ --client-only Export only client config
24
+ --server-only Export only server config
25
+ --format=yaml|json|inspect Output format
26
+ --help, -h Show detailed help
27
+
28
+ Examples:
29
+ # Export all configs for troubleshooting
30
+ bundle exec rake shakapacker:export_bundler_config -- --doctor
31
+
32
+ # Save production client config
33
+ bundle exec rake shakapacker:export_bundler_config -- --save --env=production --client-only
34
+
35
+ # View development config in terminal
36
+ bundle exec rake shakapacker:export_bundler_config
37
+
38
+ # Show detailed help
39
+ bundle exec rake shakapacker:export_bundler_config -- --help
40
+
41
+ The task automatically falls back to the gem version if bin/shakapacker-config
42
+ binstub is not installed. To install all binstubs, run: bundle exec rake shakapacker:binstubs
43
+ DESC
44
+ task :export_bundler_config do
45
+ # Try to use the binstub if it exists, otherwise use the gem's version
46
+ bin_path = Rails.root.join("bin/shakapacker-config")
47
+
48
+ unless File.exist?(bin_path)
49
+ # Binstub not installed, use the gem's version directly
50
+ gem_bin_path = File.expand_path("../../install/bin/shakapacker-config", __dir__)
51
+
52
+ $stderr.puts "Note: bin/shakapacker-config binstub not found."
53
+ $stderr.puts "Using gem version directly. To install the binstub, run: rake shakapacker:binstubs"
54
+ $stderr.puts ""
55
+
56
+ Dir.chdir(Rails.root) do
57
+ exec("node", gem_bin_path, *ARGV[1..])
58
+ end
59
+ else
60
+ # Pass through command-line arguments after the task name
61
+ # Use exec to replace the rake process with the export script
62
+ # This ensures proper exit codes and signal handling
63
+ Dir.chdir(Rails.root) do
64
+ exec(bin_path.to_s, *ARGV[1..])
65
+ end
66
+ end
67
+ end
68
+ end
@@ -2,16 +2,28 @@ install_template_path = File.expand_path("../../install/template.rb", __dir__).f
2
2
  bin_path = ENV["BUNDLE_BIN"] || Rails.root.join("bin")
3
3
 
4
4
  namespace :shakapacker do
5
- desc "Install Shakapacker in this application"
6
- task install: [:check_node] do |task|
5
+ desc "Install Shakapacker in this application (use SHAKAPACKER_ASSETS_BUNDLER=rspack for Rspack, --typescript for TypeScript)"
6
+ task :install, [:bundler, :typescript] => [:check_node] do |task, args|
7
7
  Shakapacker::Configuration.installing = true
8
8
 
9
+ if args[:bundler] == "rspack" || ENV["SHAKAPACKER_ASSETS_BUNDLER"] == "rspack"
10
+ ENV["SHAKAPACKER_ASSETS_BUNDLER"] = "rspack"
11
+ end
12
+
13
+ # Set typescript flag if passed as argument
14
+ # Accepts: typescript, true, or any truthy value
15
+ if args[:typescript] && args[:typescript] != "false"
16
+ ENV["SHAKAPACKER_USE_TYPESCRIPT"] = "true"
17
+ end
18
+
9
19
  prefix = task.name.split(/#|shakapacker:install/).first
10
20
 
11
21
  if Rails::VERSION::MAJOR >= 5
12
- exec "#{RbConfig.ruby} '#{bin_path}/rails' #{prefix}app:template LOCATION='#{install_template_path}'"
22
+ system "#{RbConfig.ruby} '#{bin_path}/rails' #{prefix}app:template LOCATION='#{install_template_path}'" or
23
+ raise "Installation failed"
13
24
  else
14
- exec "#{RbConfig.ruby} '#{bin_path}/rake' #{prefix}rails:template LOCATION='#{install_template_path}'"
25
+ system "#{RbConfig.ruby} '#{bin_path}/rake' #{prefix}rails:template LOCATION='#{install_template_path}'" or
26
+ raise "Installation failed"
15
27
  end
16
28
  end
17
29
  end
@@ -0,0 +1,13 @@
1
+ require "shakapacker/swc_migrator"
2
+
3
+ namespace :shakapacker do
4
+ desc "Migrate from Babel to SWC transpiler"
5
+ task :migrate_to_swc do
6
+ Shakapacker::SwcMigrator.new(Rails.root).migrate_to_swc
7
+ end
8
+
9
+ desc "Remove Babel packages after migrating to SWC"
10
+ task :clean_babel_packages do
11
+ Shakapacker::SwcMigrator.new(Rails.root).clean_babel_packages
12
+ end
13
+ end
@@ -0,0 +1,72 @@
1
+ require "shakapacker/bundler_switcher"
2
+
3
+ namespace :shakapacker do
4
+ desc <<~DESC
5
+ Switch between webpack and rspack bundlers
6
+
7
+ Easily switch your Shakapacker configuration between webpack and rspack bundlers.
8
+ This task updates config/shakapacker.yml and optionally manages npm dependencies.
9
+
10
+ Usage:
11
+ bin/rake shakapacker:switch_bundler [webpack|rspack] -- [OPTIONS]
12
+
13
+ Options:
14
+ --install-deps Automatically install/uninstall bundler dependencies
15
+ --no-uninstall Skip uninstalling old bundler packages
16
+ --init-config Create custom dependencies configuration file
17
+ --help, -h Show detailed help message
18
+
19
+ Examples:
20
+ bin/rake shakapacker:switch_bundler rspack -- --install-deps
21
+ bin/rake shakapacker:switch_bundler webpack -- --install-deps --no-uninstall
22
+ bin/rake shakapacker:switch_bundler -- --init-config
23
+ bin/rake shakapacker:switch_bundler -- --help
24
+
25
+ What it does:
26
+ - Updates 'assets_bundler' in config/shakapacker.yml
27
+ - Preserves YAML comments and structure
28
+ - Updates 'javascript_transpiler' to 'swc' when switching to rspack
29
+ - With --install-deps: installs/uninstalls npm dependencies automatically
30
+ - Without: shows manual installation commands
31
+
32
+ Custom Dependencies:
33
+ Create .shakapacker-switch-bundler-dependencies.yml to customize which
34
+ npm packages are installed/uninstalled during bundler switching.
35
+
36
+ See docs/rspack_migration_guide.md for more information.
37
+ DESC
38
+ task :switch_bundler do
39
+ # This task must be run with rake, not rails
40
+ # Check the actual command name, not just if the path contains "rails"
41
+ command_name = File.basename($0)
42
+ if command_name == "rails" || $0.end_with?("/rails")
43
+ puts "\nError: This task must be run with 'bin/rake', not 'bin/rails'"
44
+ puts "Usage: bin/rake shakapacker:switch_bundler [bundler] -- [options]"
45
+ puts "Run 'bin/rake shakapacker:switch_bundler -- --help' for more information"
46
+ exit 1
47
+ end
48
+
49
+ switcher = Shakapacker::BundlerSwitcher.new
50
+
51
+ # Parse command line arguments
52
+ # ARGV[0] is the task name, ARGV[1] would be the bundler name if provided
53
+ bundler = ARGV.length > 1 ? ARGV[1] : nil
54
+ install_deps = ARGV.include?("--install-deps")
55
+ no_uninstall = ARGV.include?("--no-uninstall")
56
+ init_config = ARGV.include?("--init-config")
57
+ show_help = ARGV.include?("--help") || ARGV.include?("-h")
58
+
59
+ if ARGV.empty? || show_help || (bundler.nil? && !init_config)
60
+ switcher.show_usage
61
+ elsif init_config
62
+ switcher.init_config
63
+ elsif bundler.nil? || bundler.start_with?("-")
64
+ switcher.show_usage
65
+ else
66
+ switcher.switch_to(bundler, install_deps: install_deps, no_uninstall: no_uninstall)
67
+ end
68
+
69
+ # Prevent rake from trying to execute arguments as tasks
70
+ ARGV.each { |arg| task arg.to_sym {} }
71
+ end
72
+ end
@@ -9,6 +9,8 @@ tasks = {
9
9
  "shakapacker:check_binstubs" => "Verifies that bin/shakapacker is present",
10
10
  "shakapacker:binstubs" => "Installs Shakapacker binstubs in this application",
11
11
  "shakapacker:verify_install" => "Verifies if Shakapacker is installed",
12
+ "shakapacker:doctor" => "Checks for configuration issues and missing dependencies",
13
+ "shakapacker:switch_bundler" => "Switch between webpack and rspack bundlers"
12
14
  }.freeze
13
15
 
14
16
  desc "Lists all available tasks in Shakapacker"
@@ -0,0 +1,4 @@
1
+ # Exclude TypeScript source files from the package directory
2
+ *.ts
3
+ # But keep the TypeScript declaration files
4
+ !*.d.ts
@@ -0,0 +1,59 @@
1
+ import { moduleExists, packageFullVersion } from "../utils/helpers"
2
+ import type { ConfigAPI, PluginItem } from "@babel/core"
3
+
4
+ const CORE_JS_VERSION_REGEX = /^\d+\.\d+/
5
+
6
+ const coreJsVersion = (): string => {
7
+ try {
8
+ const version = packageFullVersion("core-js").match(CORE_JS_VERSION_REGEX)
9
+ return version?.[0] ?? "3.8"
10
+ } catch (e) {
11
+ const error = e as NodeJS.ErrnoException
12
+ if (error.code !== "MODULE_NOT_FOUND") {
13
+ throw e
14
+ }
15
+
16
+ return "3.8"
17
+ }
18
+ }
19
+
20
+ export = function config(api: ConfigAPI): {
21
+ presets: PluginItem[]
22
+ plugins: PluginItem[]
23
+ } {
24
+ const validEnv = ["development", "test", "production"]
25
+ const currentEnv = api.env()
26
+ const isDevelopmentEnv = api.env("development")
27
+ const isProductionEnv = api.env("production")
28
+ const isTestEnv = api.env("test")
29
+
30
+ if (!validEnv.includes(currentEnv)) {
31
+ throw new Error(
32
+ `Please specify a valid NODE_ENV or BABEL_ENV environment variable. Valid values are "development", "test", and "production". Instead, received: "${currentEnv}".`
33
+ )
34
+ }
35
+
36
+ const presets: PluginItem[] = [
37
+ isTestEnv && ["@babel/preset-env", { targets: { node: "current" } }],
38
+ (isProductionEnv || isDevelopmentEnv) && [
39
+ "@babel/preset-env",
40
+ {
41
+ useBuiltIns: "entry",
42
+ corejs: coreJsVersion(),
43
+ modules: "auto",
44
+ bugfixes: true,
45
+ exclude: ["transform-typeof-symbol"]
46
+ }
47
+ ],
48
+ moduleExists("@babel/preset-typescript") && "@babel/preset-typescript"
49
+ ].filter(Boolean) as PluginItem[]
50
+
51
+ const plugins: PluginItem[] = [
52
+ ["@babel/plugin-transform-runtime", { helpers: false }]
53
+ ].filter(Boolean) as PluginItem[]
54
+
55
+ return {
56
+ presets,
57
+ plugins
58
+ }
59
+ }
data/package/config.ts ADDED
@@ -0,0 +1,189 @@
1
+ import { resolve } from "path"
2
+ import { load } from "js-yaml"
3
+ import { existsSync, readFileSync } from "fs"
4
+ import { merge } from "webpack-merge"
5
+ const { ensureTrailingSlash } = require("./utils/helpers")
6
+ const { railsEnv } = require("./env")
7
+ const configPath = require("./utils/configPath")
8
+ const defaultConfigPath = require("./utils/defaultConfigPath")
9
+ import { Config, YamlConfig } from "./types"
10
+ const {
11
+ isValidYamlConfig,
12
+ createConfigValidationError,
13
+ isPartialConfig
14
+ } = require("./utils/typeGuards")
15
+ const {
16
+ isFileNotFoundError,
17
+ createFileOperationError
18
+ } = require("./utils/errorHelpers")
19
+
20
+ const loadAndValidateYaml = (path: string): YamlConfig => {
21
+ const fileContent = readFileSync(path, "utf8")
22
+ const yamlContent = load(fileContent)
23
+
24
+ if (!isValidYamlConfig(yamlContent)) {
25
+ throw createConfigValidationError(path, railsEnv, "Invalid YAML structure")
26
+ }
27
+
28
+ return yamlContent as YamlConfig
29
+ }
30
+
31
+ const getDefaultConfig = (): Partial<Config> => {
32
+ try {
33
+ const defaultConfig = loadAndValidateYaml(defaultConfigPath)
34
+ return defaultConfig[railsEnv] || defaultConfig.production || {}
35
+ } catch (error) {
36
+ if (isFileNotFoundError(error)) {
37
+ throw createFileOperationError(
38
+ "read",
39
+ defaultConfigPath,
40
+ `Default configuration not found at ${defaultConfigPath}. Please ensure Shakapacker is properly installed. You may need to run 'yarn add shakapacker' or 'npm install shakapacker'.`
41
+ )
42
+ }
43
+ throw error
44
+ }
45
+ }
46
+
47
+ const defaults = getDefaultConfig()
48
+ let config: Config
49
+
50
+ if (existsSync(configPath)) {
51
+ try {
52
+ const appYmlObject = loadAndValidateYaml(configPath)
53
+
54
+ const envAppConfig = appYmlObject[railsEnv]
55
+
56
+ if (!envAppConfig) {
57
+ console.warn(
58
+ `[SHAKAPACKER WARNING] Environment '${railsEnv}' not found in ${configPath}\n` +
59
+ `Available environments: ${Object.keys(appYmlObject).join(", ")}\n` +
60
+ `Using 'production' configuration as fallback.\n\n` +
61
+ `To fix this, either:\n` +
62
+ ` - Add a '${railsEnv}' section to your shakapacker.yml\n` +
63
+ ` - Set RAILS_ENV to one of the available environments\n` +
64
+ ` - Copy settings from another environment as a starting point`
65
+ )
66
+ }
67
+
68
+ // Merge returns the merged type
69
+ const mergedConfig = merge(defaults, envAppConfig || {})
70
+
71
+ // Validate merged config before type assertion
72
+ if (!isPartialConfig(mergedConfig)) {
73
+ throw createConfigValidationError(
74
+ configPath,
75
+ railsEnv,
76
+ `Invalid configuration structure in ${configPath}. Please check your shakapacker.yml syntax and ensure all required fields are properly defined.`
77
+ )
78
+ }
79
+
80
+ // After merging with defaults, config should be complete
81
+ // Use type assertion only after validation
82
+ config = mergedConfig as Config
83
+ } catch (error) {
84
+ if (isFileNotFoundError(error)) {
85
+ // File not found is OK, use defaults
86
+ if (!isPartialConfig(defaults)) {
87
+ throw createConfigValidationError(
88
+ defaultConfigPath,
89
+ railsEnv,
90
+ `Invalid default configuration. This may indicate a corrupted Shakapacker installation. Try reinstalling with 'yarn add shakapacker --force'.`
91
+ )
92
+ }
93
+ // Using defaults only, might be partial
94
+ config = defaults as Config
95
+ } else {
96
+ throw error
97
+ }
98
+ }
99
+ } else {
100
+ // No user config, use defaults
101
+ if (!isPartialConfig(defaults)) {
102
+ throw createConfigValidationError(
103
+ defaultConfigPath,
104
+ railsEnv,
105
+ `Invalid default configuration. This may indicate a corrupted Shakapacker installation. Try reinstalling with 'yarn add shakapacker --force'.`
106
+ )
107
+ }
108
+ // Using defaults only, might be partial
109
+ config = defaults as Config
110
+ }
111
+
112
+ config.outputPath = resolve(config.public_root_path, config.public_output_path)
113
+
114
+ if (config.private_output_path) {
115
+ config.privateOutputPath = resolve(config.private_output_path)
116
+ }
117
+
118
+ // Ensure that the publicPath includes our asset host so dynamic imports
119
+ // (code-splitting chunks and static assets) load from the CDN instead of a relative path.
120
+ const getPublicPath = (): string => {
121
+ const rootUrl = ensureTrailingSlash(process.env.SHAKAPACKER_ASSET_HOST || "/")
122
+ return `${rootUrl}${config.public_output_path}/`
123
+ }
124
+
125
+ config.publicPath = getPublicPath()
126
+ config.publicPathWithoutCDN = `/${config.public_output_path}/`
127
+
128
+ if (config.manifest_path) {
129
+ config.manifestPath = resolve(config.manifest_path)
130
+ } else {
131
+ config.manifestPath = resolve(config.outputPath, "manifest.json")
132
+ }
133
+ // Ensure no duplicate hash functions exist in the returned config object
134
+ if (config.integrity?.hash_functions) {
135
+ config.integrity.hash_functions = [
136
+ ...new Set(config.integrity.hash_functions)
137
+ ]
138
+ }
139
+
140
+ // Ensure assets_bundler has a default value
141
+ if (!config.assets_bundler) {
142
+ config.assets_bundler = "webpack"
143
+ }
144
+
145
+ // Allow ENV variable to override assets_bundler
146
+ if (process.env.SHAKAPACKER_ASSETS_BUNDLER) {
147
+ config.assets_bundler = process.env.SHAKAPACKER_ASSETS_BUNDLER
148
+ }
149
+
150
+ // Define clear defaults
151
+ // Keep Babel as default for webpack to maintain backward compatibility
152
+ // Use SWC for rspack as it's a newer bundler where we can set modern defaults
153
+ const DEFAULT_JAVASCRIPT_TRANSPILER =
154
+ config.assets_bundler === "rspack" ? "swc" : "babel"
155
+
156
+ // Backward compatibility: Check for webpack_loader using proper type guard
157
+ function hasWebpackLoader(
158
+ obj: unknown
159
+ ): obj is Config & { webpack_loader: string } {
160
+ return (
161
+ typeof obj === "object" &&
162
+ obj !== null &&
163
+ "webpack_loader" in obj &&
164
+ typeof (obj as Record<string, unknown>).webpack_loader === "string"
165
+ )
166
+ }
167
+
168
+ // Allow environment variable to override javascript_transpiler
169
+ if (process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER) {
170
+ config.javascript_transpiler = process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER
171
+ } else if (hasWebpackLoader(config) && !config.javascript_transpiler) {
172
+ console.warn(
173
+ "[SHAKAPACKER DEPRECATION] The 'webpack_loader' configuration option is deprecated.\n" +
174
+ "Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
175
+ )
176
+ config.javascript_transpiler = config.webpack_loader
177
+ } else if (!config.javascript_transpiler) {
178
+ config.javascript_transpiler = DEFAULT_JAVASCRIPT_TRANSPILER
179
+ }
180
+
181
+ // Ensure webpack_loader is always available for backward compatibility
182
+ Object.defineProperty(config, "webpack_loader", {
183
+ value: config.javascript_transpiler,
184
+ writable: true,
185
+ enumerable: true,
186
+ configurable: true
187
+ })
188
+
189
+ export = config