shakapacker 8.0.2 → 9.2.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintignore +1 -0
  3. data/.eslintrc.fast.js +40 -0
  4. data/.eslintrc.js +48 -0
  5. data/.github/STATUS.md +1 -0
  6. data/.github/workflows/claude-code-review.yml +54 -0
  7. data/.github/workflows/claude.yml +50 -0
  8. data/.github/workflows/dummy.yml +9 -4
  9. data/.github/workflows/generator.yml +32 -10
  10. data/.github/workflows/node.yml +23 -1
  11. data/.github/workflows/ruby.yml +33 -2
  12. data/.github/workflows/test-bundlers.yml +170 -0
  13. data/.gitignore +20 -0
  14. data/.husky/pre-commit +2 -0
  15. data/.npmignore +56 -0
  16. data/.prettierignore +3 -0
  17. data/.rubocop.yml +1 -0
  18. data/.yalcignore +26 -0
  19. data/CHANGELOG.md +302 -16
  20. data/CLAUDE.md +29 -0
  21. data/CONTRIBUTING.md +138 -20
  22. data/Gemfile.lock +83 -89
  23. data/README.md +343 -105
  24. data/Rakefile +39 -4
  25. data/TODO.md +50 -0
  26. data/TODO_v9.md +87 -0
  27. data/bin/export-bundler-config +11 -0
  28. data/conductor-setup.sh +70 -0
  29. data/conductor.json +7 -0
  30. data/docs/cdn_setup.md +379 -0
  31. data/docs/common-upgrades.md +615 -0
  32. data/docs/css-modules-export-mode.md +512 -0
  33. data/docs/deployment.md +62 -9
  34. data/docs/optional-peer-dependencies.md +198 -0
  35. data/docs/peer-dependencies.md +60 -0
  36. data/docs/react.md +6 -14
  37. data/docs/releasing.md +197 -0
  38. data/docs/rspack.md +190 -0
  39. data/docs/rspack_migration_guide.md +305 -0
  40. data/docs/subresource_integrity.md +54 -0
  41. data/docs/transpiler-migration.md +209 -0
  42. data/docs/transpiler-performance.md +179 -0
  43. data/docs/troubleshooting.md +157 -22
  44. data/docs/typescript-migration.md +379 -0
  45. data/docs/typescript.md +99 -0
  46. data/docs/using_esbuild_loader.md +3 -3
  47. data/docs/using_swc_loader.md +112 -10
  48. data/docs/v6_upgrade.md +10 -0
  49. data/docs/v8_upgrade.md +3 -5
  50. data/docs/v9_upgrade.md +458 -0
  51. data/gemfiles/Gemfile-rails.6.0.x +2 -1
  52. data/gemfiles/Gemfile-rails.6.1.x +1 -1
  53. data/gemfiles/Gemfile-rails.7.0.x +2 -2
  54. data/gemfiles/Gemfile-rails.7.1.x +1 -2
  55. data/gemfiles/Gemfile-rails.7.2.x +11 -0
  56. data/gemfiles/Gemfile-rails.8.0.x +11 -0
  57. data/lib/install/bin/export-bundler-config +11 -0
  58. data/lib/install/bin/shakapacker +4 -6
  59. data/lib/install/bin/shakapacker-dev-server +1 -1
  60. data/lib/install/config/rspack/rspack.config.js +6 -0
  61. data/lib/install/config/rspack/rspack.config.ts +7 -0
  62. data/lib/install/config/shakapacker.yml +25 -5
  63. data/lib/install/config/webpack/webpack.config.ts +7 -0
  64. data/lib/install/package.json +38 -0
  65. data/lib/install/template.rb +194 -44
  66. data/lib/shakapacker/bundler_switcher.rb +329 -0
  67. data/lib/shakapacker/compiler.rb +2 -1
  68. data/lib/shakapacker/compiler_strategy.rb +2 -2
  69. data/lib/shakapacker/configuration.rb +173 -2
  70. data/lib/shakapacker/dev_server_runner.rb +29 -8
  71. data/lib/shakapacker/digest_strategy.rb +2 -1
  72. data/lib/shakapacker/doctor.rb +905 -0
  73. data/lib/shakapacker/helper.rb +64 -16
  74. data/lib/shakapacker/manifest.rb +10 -3
  75. data/lib/shakapacker/mtime_strategy.rb +1 -1
  76. data/lib/shakapacker/railtie.rb +4 -4
  77. data/lib/shakapacker/rspack_runner.rb +19 -0
  78. data/lib/shakapacker/runner.rb +159 -10
  79. data/lib/shakapacker/swc_migrator.rb +384 -0
  80. data/lib/shakapacker/utils/manager.rb +15 -2
  81. data/lib/shakapacker/version.rb +1 -1
  82. data/lib/shakapacker/version_checker.rb +2 -2
  83. data/lib/shakapacker/webpack_runner.rb +6 -43
  84. data/lib/shakapacker.rb +22 -11
  85. data/lib/tasks/shakapacker/doctor.rake +8 -0
  86. data/lib/tasks/shakapacker/export_bundler_config.rake +72 -0
  87. data/lib/tasks/shakapacker/install.rake +12 -2
  88. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  89. data/lib/tasks/shakapacker/switch_bundler.rake +82 -0
  90. data/lib/tasks/shakapacker.rake +2 -0
  91. data/package/.npmignore +4 -0
  92. data/package/babel/preset.ts +56 -0
  93. data/package/config.ts +175 -0
  94. data/package/configExporter/cli.ts +683 -0
  95. data/package/configExporter/configDocs.ts +102 -0
  96. data/package/configExporter/fileWriter.ts +92 -0
  97. data/package/configExporter/index.ts +5 -0
  98. data/package/configExporter/types.ts +36 -0
  99. data/package/configExporter/yamlSerializer.ts +266 -0
  100. data/package/{dev_server.js → dev_server.ts} +8 -5
  101. data/package/env.ts +92 -0
  102. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +30 -0
  103. data/package/environments/{base.js → base.ts} +56 -60
  104. data/package/environments/development.ts +90 -0
  105. data/package/environments/production.ts +80 -0
  106. data/package/environments/test.ts +53 -0
  107. data/package/environments/types.ts +98 -0
  108. data/package/esbuild/index.ts +42 -0
  109. data/package/index.d.ts +3 -60
  110. data/package/index.ts +55 -0
  111. data/package/loaders.d.ts +28 -0
  112. data/package/optimization/rspack.ts +36 -0
  113. data/package/optimization/webpack.ts +57 -0
  114. data/package/plugins/rspack.ts +103 -0
  115. data/package/plugins/webpack.ts +62 -0
  116. data/package/rspack/index.ts +64 -0
  117. data/package/rules/{babel.js → babel.ts} +2 -2
  118. data/package/rules/{coffee.js → coffee.ts} +1 -1
  119. data/package/rules/css.ts +3 -0
  120. data/package/rules/{erb.js → erb.ts} +1 -1
  121. data/package/rules/esbuild.ts +10 -0
  122. data/package/rules/file.ts +40 -0
  123. data/package/rules/{jscommon.js → jscommon.ts} +4 -4
  124. data/package/rules/{less.js → less.ts} +4 -4
  125. data/package/rules/raw.ts +25 -0
  126. data/package/rules/rspack.ts +176 -0
  127. data/package/rules/{sass.js → sass.ts} +7 -3
  128. data/package/rules/{stylus.js → stylus.ts} +4 -8
  129. data/package/rules/swc.ts +10 -0
  130. data/package/rules/webpack.ts +16 -0
  131. data/package/swc/index.ts +56 -0
  132. data/package/types/README.md +88 -0
  133. data/package/types/index.ts +61 -0
  134. data/package/types.ts +108 -0
  135. data/package/utils/configPath.ts +6 -0
  136. data/package/utils/debug.ts +49 -0
  137. data/package/utils/defaultConfigPath.ts +4 -0
  138. data/package/utils/errorCodes.ts +219 -0
  139. data/package/utils/errorHelpers.ts +143 -0
  140. data/package/utils/getStyleRule.ts +64 -0
  141. data/package/utils/helpers.ts +85 -0
  142. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  143. data/package/utils/pathValidation.ts +139 -0
  144. data/package/utils/requireOrError.ts +15 -0
  145. data/package/utils/snakeToCamelCase.ts +5 -0
  146. data/package/utils/typeGuards.ts +342 -0
  147. data/package/utils/validateDependencies.ts +61 -0
  148. data/package/webpack-types.d.ts +33 -0
  149. data/package/webpackDevServerConfig.ts +117 -0
  150. data/package-lock.json +13047 -0
  151. data/package.json +154 -18
  152. data/scripts/remove-use-strict.js +45 -0
  153. data/scripts/type-check-no-emit.js +27 -0
  154. data/test/helpers.js +1 -1
  155. data/test/package/config.test.js +43 -0
  156. data/test/package/env.test.js +42 -7
  157. data/test/package/environments/base.test.js +5 -1
  158. data/test/package/rules/babel.test.js +16 -0
  159. data/test/package/rules/esbuild.test.js +1 -1
  160. data/test/package/rules/raw.test.js +40 -7
  161. data/test/package/rules/swc.test.js +1 -1
  162. data/test/package/rules/webpack.test.js +35 -0
  163. data/test/package/staging.test.js +4 -3
  164. data/test/package/transpiler-defaults.test.js +127 -0
  165. data/test/peer-dependencies.sh +85 -0
  166. data/test/scripts/remove-use-strict.test.js +125 -0
  167. data/test/typescript/build.test.js +118 -0
  168. data/test/typescript/environments.test.js +107 -0
  169. data/test/typescript/pathValidation.test.js +142 -0
  170. data/test/typescript/securityValidation.test.js +182 -0
  171. data/tools/README.md +124 -0
  172. data/tools/css-modules-v9-codemod.js +179 -0
  173. data/tsconfig.eslint.json +16 -0
  174. data/tsconfig.json +38 -0
  175. data/yarn.lock +4165 -2706
  176. metadata +129 -41
  177. data/package/babel/preset.js +0 -37
  178. data/package/config.js +0 -54
  179. data/package/env.js +0 -48
  180. data/package/environments/development.js +0 -13
  181. data/package/environments/production.js +0 -88
  182. data/package/environments/test.js +0 -3
  183. data/package/esbuild/index.js +0 -40
  184. data/package/index.js +0 -40
  185. data/package/rules/css.js +0 -3
  186. data/package/rules/esbuild.js +0 -10
  187. data/package/rules/file.js +0 -29
  188. data/package/rules/index.js +0 -20
  189. data/package/rules/raw.js +0 -5
  190. data/package/rules/swc.js +0 -10
  191. data/package/swc/index.js +0 -50
  192. data/package/utils/configPath.js +0 -4
  193. data/package/utils/defaultConfigPath.js +0 -2
  194. data/package/utils/getStyleRule.js +0 -40
  195. data/package/utils/helpers.js +0 -58
  196. data/package/utils/snakeToCamelCase.js +0 -5
  197. data/package/webpackDevServerConfig.js +0 -71
  198. data/test/package/rules/index.test.js +0 -16
@@ -1,7 +1,8 @@
1
1
  require "open3"
2
- require "shakapacker/compiler_strategy"
3
2
  require "fileutils"
4
3
 
4
+ require_relative "compiler_strategy"
5
+
5
6
  class Shakapacker::Compiler
6
7
  # Additional environment variables that the compiler is being run with
7
8
  # Shakapacker::Compiler.env['FRONTEND_API_KEY'] = 'your_secret_key'
@@ -1,5 +1,5 @@
1
- require "shakapacker/mtime_strategy"
2
- require "shakapacker/digest_strategy"
1
+ require_relative "mtime_strategy"
2
+ require_relative "digest_strategy"
3
3
 
4
4
  module Shakapacker
5
5
  class CompilerStrategy
@@ -1,4 +1,5 @@
1
1
  require "yaml"
2
+ require "json"
2
3
  require "active_support/core_ext/hash/keys"
3
4
  require "active_support/core_ext/hash/indifferent_access"
4
5
 
@@ -68,6 +69,13 @@ class Shakapacker::Configuration
68
69
  root_path.join(fetch(:public_root_path))
69
70
  end
70
71
 
72
+ def private_output_path
73
+ private_path = fetch(:private_output_path)
74
+ return nil unless private_path
75
+ validate_output_paths!
76
+ root_path.join(private_path)
77
+ end
78
+
71
79
  def public_output_path
72
80
  public_path.join(fetch(:public_output_path))
73
81
  end
@@ -88,6 +96,107 @@ class Shakapacker::Configuration
88
96
  fetch(:compiler_strategy)
89
97
  end
90
98
 
99
+ def assets_bundler
100
+ # Show deprecation warning if using old 'bundler' key
101
+ if data.has_key?(:bundler) && !data.has_key?(:assets_bundler)
102
+ $stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is deprecated. Please use 'assets_bundler' instead to avoid confusion with Ruby's Bundler gem manager."
103
+ end
104
+ ENV["SHAKAPACKER_ASSETS_BUNDLER"] || fetch(:assets_bundler) || fetch(:bundler) || "webpack"
105
+ end
106
+
107
+ # Deprecated: Use assets_bundler instead
108
+ def bundler
109
+ assets_bundler
110
+ end
111
+
112
+ def rspack?
113
+ assets_bundler == "rspack"
114
+ end
115
+
116
+ def webpack?
117
+ assets_bundler == "webpack"
118
+ end
119
+
120
+ def javascript_transpiler
121
+ # Show deprecation warning if using old 'webpack_loader' key
122
+ if data.has_key?(:webpack_loader) && !data.has_key?(:javascript_transpiler)
123
+ $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."
124
+ end
125
+
126
+ # Use explicit config if set, otherwise default based on bundler
127
+ transpiler = fetch(:javascript_transpiler) || fetch(:webpack_loader) || default_javascript_transpiler
128
+
129
+ # Validate transpiler configuration
130
+ validate_transpiler_configuration(transpiler) unless self.class.installing
131
+
132
+ transpiler
133
+ end
134
+
135
+ # Deprecated: Use javascript_transpiler instead
136
+ def webpack_loader
137
+ javascript_transpiler
138
+ end
139
+
140
+ private
141
+
142
+ def default_javascript_transpiler
143
+ # RSpack has built-in SWC support, use it by default
144
+ rspack? ? "swc" : "babel"
145
+ end
146
+
147
+ def validate_transpiler_configuration(transpiler)
148
+ return unless ENV["NODE_ENV"] != "test" # Skip validation in test environment
149
+
150
+ # Check if package.json exists
151
+ package_json_path = root_path.join("package.json")
152
+ return unless package_json_path.exist?
153
+
154
+ begin
155
+ package_json = JSON.parse(File.read(package_json_path))
156
+ all_deps = (package_json["dependencies"] || {}).merge(package_json["devDependencies"] || {})
157
+
158
+ # Check for transpiler mismatch
159
+ has_babel = all_deps.keys.any? { |pkg| pkg.start_with?("@babel/", "babel-") }
160
+ has_swc = all_deps.keys.any? { |pkg| pkg.include?("swc") }
161
+ has_esbuild = all_deps.keys.any? { |pkg| pkg.include?("esbuild") }
162
+
163
+ case transpiler
164
+ when "babel"
165
+ if !has_babel && has_swc
166
+ warn_transpiler_mismatch("Babel", "SWC packages found but Babel is configured")
167
+ end
168
+ when "swc"
169
+ if !has_swc && has_babel
170
+ warn_transpiler_mismatch("SWC", "Babel packages found but SWC is configured")
171
+ end
172
+ when "esbuild"
173
+ if !has_esbuild && (has_babel || has_swc)
174
+ other = has_babel ? "Babel" : "SWC"
175
+ warn_transpiler_mismatch("esbuild", "#{other} packages found but esbuild is configured")
176
+ end
177
+ end
178
+ rescue JSON::ParserError
179
+ # Ignore if package.json is malformed
180
+ end
181
+ end
182
+
183
+ def warn_transpiler_mismatch(configured, message)
184
+ $stderr.puts <<~WARNING
185
+ ⚠️ Transpiler Configuration Mismatch Detected:
186
+ #{message}
187
+ Configured transpiler: #{configured}
188
+ #{' '}
189
+ This might cause unexpected behavior or build failures.
190
+ #{' '}
191
+ To fix this:
192
+ 1. Run 'rails shakapacker:migrate_to_swc' to migrate to SWC (recommended for 20x faster builds)
193
+ 2. Or install the correct packages for #{configured}
194
+ 3. Or update your shakapacker.yml to match your installed packages
195
+ WARNING
196
+ end
197
+
198
+ public
199
+
91
200
  def fetch(key)
92
201
  data.fetch(key, defaults[key])
93
202
  end
@@ -99,7 +208,43 @@ class Shakapacker::Configuration
99
208
  )
100
209
  end
101
210
 
211
+ def integrity
212
+ fetch(:integrity)
213
+ end
214
+
102
215
  private
216
+ def validate_output_paths!
217
+ # Skip validation if already validated to avoid redundant checks
218
+ return if @validated_output_paths
219
+ @validated_output_paths = true
220
+
221
+ # Only validate when both paths are configured
222
+ return unless fetch(:private_output_path) && fetch(:public_output_path)
223
+
224
+ private_path_str, public_path_str = resolve_paths_for_comparison
225
+
226
+ if private_path_str == public_path_str
227
+ raise "Shakapacker configuration error: private_output_path and public_output_path must be different. " \
228
+ "Both paths resolve to '#{private_path_str}'. " \
229
+ "The private_output_path is for server-side bundles (e.g., SSR) that should not be served publicly."
230
+ end
231
+ end
232
+
233
+ def resolve_paths_for_comparison
234
+ private_full_path = root_path.join(fetch(:private_output_path))
235
+ public_full_path = root_path.join(fetch(:public_root_path), fetch(:public_output_path))
236
+
237
+ # Create directories if they don't exist (for testing)
238
+ private_full_path.mkpath unless private_full_path.exist?
239
+ public_full_path.mkpath unless public_full_path.exist?
240
+
241
+ # Use realpath to resolve symbolic links and relative paths
242
+ [private_full_path.realpath.to_s, public_full_path.realpath.to_s]
243
+ rescue Errno::ENOENT
244
+ # If paths don't exist yet, fall back to cleanpath for comparison
245
+ [private_full_path.cleanpath.to_s, public_full_path.cleanpath.to_s]
246
+ end
247
+
103
248
  def data
104
249
  @data ||= load
105
250
  end
@@ -110,7 +255,21 @@ class Shakapacker::Configuration
110
255
  rescue ArgumentError
111
256
  YAML.load_file(config_path.to_s)
112
257
  end
113
- symbolized_config = config[env].deep_symbolize_keys
258
+
259
+ # Try to find environment-specific configuration with fallback
260
+ # Fallback order: requested env → production
261
+ if config[env]
262
+ env_config = config[env]
263
+ elsif config["production"]
264
+ log_fallback(env, "production")
265
+ env_config = config["production"]
266
+ else
267
+ # No suitable configuration found - rely on bundled defaults
268
+ log_fallback(env, "none (will use bundled defaults)")
269
+ env_config = nil
270
+ end
271
+
272
+ symbolized_config = env_config&.deep_symbolize_keys || {}
114
273
 
115
274
  return symbolized_config
116
275
  rescue Errno::ENOENT => e
@@ -135,7 +294,10 @@ class Shakapacker::Configuration
135
294
  rescue ArgumentError
136
295
  YAML.load_file(path)
137
296
  end
138
- HashWithIndifferentAccess.new(config[env] || config[Shakapacker::DEFAULT_ENV])
297
+ # Load defaults from bundled shakapacker.yml (always has all environments)
298
+ # Note: This differs from load() which reads user's config and may be missing environments
299
+ # Fallback to production ensures staging and other custom envs get production-like defaults
300
+ HashWithIndifferentAccess.new(config[env] || config["production"])
139
301
  end
140
302
  end
141
303
 
@@ -144,4 +306,13 @@ class Shakapacker::Configuration
144
306
 
145
307
  path
146
308
  end
309
+
310
+ def log_fallback(requested_env, fallback_env)
311
+ return unless Shakapacker.logger
312
+
313
+ Shakapacker.logger.info(
314
+ "Shakapacker environment '#{requested_env}' not found in #{config_path}, " \
315
+ "falling back to '#{fallback_env}'"
316
+ )
317
+ end
147
318
  end
@@ -1,11 +1,16 @@
1
1
  require "shellwords"
2
2
  require "socket"
3
- require "shakapacker/configuration"
4
- require "shakapacker/dev_server"
5
- require "shakapacker/runner"
3
+
4
+ require_relative "configuration"
5
+ require_relative "dev_server"
6
+ require_relative "runner"
6
7
 
7
8
  module Shakapacker
8
9
  class DevServerRunner < Shakapacker::Runner
10
+ def self.run(argv)
11
+ new(argv).run
12
+ end
13
+
9
14
  def run
10
15
  load_config
11
16
  detect_unsupported_switches!
@@ -74,12 +79,19 @@ module Shakapacker
74
79
  env["NODE_OPTIONS"] = "#{env["NODE_OPTIONS"]} --inspect-brk --trace-warnings"
75
80
  end
76
81
 
82
+ # Add config file
77
83
  cmd += ["--config", @webpack_config]
78
- cmd += ["--progress", "--color"] if @pretty
79
84
 
80
- # Default behavior of webpack-dev-server is @hot = true
81
- cmd += ["--hot", "only"] if @hot == "only"
82
- cmd += ["--no-hot"] if !@hot
85
+ # Add assets bundler-specific flags
86
+ if webpack?
87
+ cmd += ["--progress", "--color"] if @pretty
88
+ # Default behavior of webpack-dev-server is @hot = true
89
+ cmd += ["--hot", "only"] if @hot == "only"
90
+ cmd += ["--no-hot"] if !@hot
91
+ elsif rspack?
92
+ # Rspack supports --hot but not --no-hot or --progress/--color
93
+ cmd += ["--hot"] if @hot && @hot != false
94
+ end
83
95
 
84
96
  cmd += @argv
85
97
 
@@ -89,7 +101,16 @@ module Shakapacker
89
101
  end
90
102
 
91
103
  def build_cmd
92
- package_json.manager.native_exec_command("webpack", ["serve"])
104
+ command = @config.rspack? ? "rspack" : "webpack"
105
+ package_json.manager.native_exec_command(command, ["serve"])
106
+ end
107
+
108
+ def webpack?
109
+ @config.webpack?
110
+ end
111
+
112
+ def rspack?
113
+ @config.rspack?
93
114
  end
94
115
  end
95
116
  end
@@ -1,5 +1,6 @@
1
1
  require "digest/sha1"
2
- require "shakapacker/base_strategy"
2
+
3
+ require_relative "base_strategy"
3
4
 
4
5
  module Shakapacker
5
6
  class DigestStrategy < BaseStrategy