shakapacker 10.1.0 → 10.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -1
- data/README.md +116 -1368
- data/lib/install/bin/diff-bundler-config +89 -18
- data/lib/install/bin/shakapacker-config +89 -18
- data/lib/install/config/shakapacker.yml +7 -1
- data/lib/install/package.json +5 -3
- data/lib/install/template.rb +91 -12
- data/lib/shakapacker/base_strategy.rb +10 -4
- data/lib/shakapacker/bundler_switcher.rb +22 -6
- data/lib/shakapacker/compiler.rb +38 -8
- data/lib/shakapacker/compiler_strategy.rb +4 -4
- data/lib/shakapacker/configuration.rb +57 -0
- data/lib/shakapacker/dev_server_runner.rb +44 -19
- data/lib/shakapacker/digest_strategy.rb +3 -3
- data/lib/shakapacker/doctor.rb +446 -76
- data/lib/shakapacker/helper.rb +3 -3
- data/lib/shakapacker/install/env.rb +50 -0
- data/lib/shakapacker/instance.rb +1 -1
- data/lib/shakapacker/mtime_strategy.rb +1 -1
- data/lib/shakapacker/runner.rb +93 -55
- data/lib/shakapacker/utils/misc.rb +38 -0
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker/version_checker.rb +16 -4
- data/lib/tasks/shakapacker/export_bundler_config.rake +30 -21
- data/lib/tasks/shakapacker/install.rake +5 -3
- data/lib/tasks/shakapacker.rake +1 -1
- data/package.json +15 -11
- data/sig/shakapacker/configuration.rbs +3 -0
- metadata +3 -3
data/lib/shakapacker/helper.rb
CHANGED
|
@@ -112,7 +112,7 @@ module Shakapacker::Helper
|
|
|
112
112
|
def javascript_pack_tag(*names, defer: true, async: false, early_hints: nil, **options)
|
|
113
113
|
if @javascript_pack_tag_loaded
|
|
114
114
|
raise "To prevent duplicated chunks on the page, you should call javascript_pack_tag only once on the page. " \
|
|
115
|
-
"Please refer to https://github.com/shakacode/shakapacker/blob/main/
|
|
115
|
+
"Please refer to https://github.com/shakacode/shakapacker/blob/main/docs/api-reference.md#view-helpers-javascript_pack_tag-and-stylesheet_pack_tag for the usage guide"
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
# Collect all packs (queue + direct args)
|
|
@@ -330,7 +330,7 @@ module Shakapacker::Helper
|
|
|
330
330
|
def append_stylesheet_pack_tag(*names)
|
|
331
331
|
if @stylesheet_pack_tag_loaded
|
|
332
332
|
raise "You can only call append_stylesheet_pack_tag before stylesheet_pack_tag helper. " \
|
|
333
|
-
"Please refer to https://github.com/shakacode/shakapacker/blob/main/
|
|
333
|
+
"Please refer to https://github.com/shakacode/shakapacker/blob/main/docs/api-reference.md#view-helper-append_javascript_pack_tag-prepend_javascript_pack_tag-and-append_stylesheet_pack_tag for the usage guide"
|
|
334
334
|
end
|
|
335
335
|
|
|
336
336
|
@stylesheet_pack_tag_queue ||= []
|
|
@@ -357,7 +357,7 @@ module Shakapacker::Helper
|
|
|
357
357
|
def update_javascript_pack_tag_queue(defer:, async:)
|
|
358
358
|
if @javascript_pack_tag_loaded
|
|
359
359
|
raise "You can only call #{caller_locations(1..1).first.base_label} before javascript_pack_tag helper. " \
|
|
360
|
-
"Please refer to https://github.com/shakacode/shakapacker/blob/main/
|
|
360
|
+
"Please refer to https://github.com/shakacode/shakapacker/blob/main/docs/api-reference.md#view-helper-append_javascript_pack_tag-prepend_javascript_pack_tag-and-append_stylesheet_pack_tag for the usage guide"
|
|
361
361
|
end
|
|
362
362
|
|
|
363
363
|
# When both async and defer are specified, async takes precedence per HTML5 spec
|
|
@@ -2,6 +2,7 @@ module Shakapacker
|
|
|
2
2
|
module Install
|
|
3
3
|
module Env
|
|
4
4
|
TRUTHY_VALUES = %w[true 1 yes].freeze
|
|
5
|
+
VALID_BUNDLERS = %w[webpack rspack].freeze
|
|
5
6
|
|
|
6
7
|
module_function
|
|
7
8
|
|
|
@@ -28,6 +29,55 @@ module Shakapacker
|
|
|
28
29
|
|
|
29
30
|
!config_preexisting
|
|
30
31
|
end
|
|
32
|
+
|
|
33
|
+
# Keep bundled runtime defaults backward compatible while allowing the
|
|
34
|
+
# installer to write an explicit new-project bundler choice. Takes the same
|
|
35
|
+
# inputs as update_transpiler_config? for consistency, but is deliberately
|
|
36
|
+
# more conservative: switching an existing app's bundler is impactful, so we
|
|
37
|
+
# only rewrite a config the installer owns (a fresh install, or an explicit
|
|
38
|
+
# FORCE overwrite) and never a pre-existing one (interactive or SKIP mode).
|
|
39
|
+
def update_assets_bundler_config?(assets_bundler_to_install:, conflict_option:, config_preexisting:)
|
|
40
|
+
# The bundled shakapacker.yml already ships assets_bundler: "webpack", so
|
|
41
|
+
# rewriting it to "webpack" would be a no-op. This relies on that shipped default.
|
|
42
|
+
return false if assets_bundler_to_install == "webpack"
|
|
43
|
+
return true if conflict_option[:force]
|
|
44
|
+
|
|
45
|
+
!config_preexisting
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Resolve which bundler the installer should set up. Precedence: an explicit
|
|
49
|
+
# SHAKAPACKER_ASSETS_BUNDLER env var (or task argument, which sets that env var)
|
|
50
|
+
# always wins; otherwise a FORCE overwrite installs the new-project default;
|
|
51
|
+
# otherwise an existing app keeps its current bundler (when it is a recognized
|
|
52
|
+
# value) so a re-install never silently switches it; brand-new installs fall
|
|
53
|
+
# back to rspack. Returning the env var verbatim lets the caller's strict
|
|
54
|
+
# VALID_BUNDLERS check still reject a misspelled value.
|
|
55
|
+
def resolve_assets_bundler(env_value:, existing_bundler:, force:)
|
|
56
|
+
return env_value if env_value
|
|
57
|
+
return "rspack" if force
|
|
58
|
+
return existing_bundler if VALID_BUNDLERS.include?(existing_bundler)
|
|
59
|
+
|
|
60
|
+
"rspack"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Apply an optional `shakapacker:install[bundler]` task argument. A
|
|
64
|
+
# recognized bundler (webpack or rspack) is written to
|
|
65
|
+
# SHAKAPACKER_ASSETS_BUNDLER so the install template picks it up,
|
|
66
|
+
# overriding any value already set in that env var (the explicit argument
|
|
67
|
+
# wins). An unrecognized value returns an error message (and leaves the
|
|
68
|
+
# env var unset) so the caller can abort, mirroring the template's strict
|
|
69
|
+
# validation of SHAKAPACKER_ASSETS_BUNDLER. Returns nil when the argument
|
|
70
|
+
# is valid or absent.
|
|
71
|
+
def apply_bundler_arg(bundler_arg)
|
|
72
|
+
return nil if bundler_arg.nil? || bundler_arg.empty?
|
|
73
|
+
|
|
74
|
+
if VALID_BUNDLERS.include?(bundler_arg)
|
|
75
|
+
ENV["SHAKAPACKER_ASSETS_BUNDLER"] = bundler_arg
|
|
76
|
+
nil
|
|
77
|
+
else
|
|
78
|
+
"Unknown bundler '#{bundler_arg}'. Valid values: #{VALID_BUNDLERS.join(", ")}."
|
|
79
|
+
end
|
|
80
|
+
end
|
|
31
81
|
end
|
|
32
82
|
end
|
|
33
83
|
end
|
data/lib/shakapacker/instance.rb
CHANGED
|
@@ -81,7 +81,7 @@ class Shakapacker::Instance
|
|
|
81
81
|
# @return [Shakapacker::CompilerStrategy] the compiler strategy
|
|
82
82
|
# @api private
|
|
83
83
|
def strategy
|
|
84
|
-
@strategy ||= Shakapacker::CompilerStrategy.from_config
|
|
84
|
+
@strategy ||= Shakapacker::CompilerStrategy.from_config(self)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
# Returns the compiler for this instance
|
data/lib/shakapacker/runner.rb
CHANGED
|
@@ -26,6 +26,23 @@ module Shakapacker
|
|
|
26
26
|
"i"
|
|
27
27
|
].freeze
|
|
28
28
|
|
|
29
|
+
SHAKAPACKER_NODE_FLAGS = Configuration::SHAKAPACKER_NODE_FLAGS
|
|
30
|
+
private_constant :SHAKAPACKER_NODE_FLAGS
|
|
31
|
+
|
|
32
|
+
RSPACK_RUNNER_EXTENSION = Module.new do
|
|
33
|
+
def build_cmd
|
|
34
|
+
package_json.manager.native_exec_command("rspack")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
private_constant :RSPACK_RUNNER_EXTENSION
|
|
38
|
+
|
|
39
|
+
WEBPACK_RUNNER_EXTENSION = Module.new do
|
|
40
|
+
def build_cmd
|
|
41
|
+
package_json.manager.native_exec_command("webpack")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
private_constant :WEBPACK_RUNNER_EXTENSION
|
|
45
|
+
|
|
29
46
|
def self.json_output?(argv)
|
|
30
47
|
argv.include?("--json") || argv.include?("-j")
|
|
31
48
|
end
|
|
@@ -34,31 +51,39 @@ module Shakapacker
|
|
|
34
51
|
json_output?(argv) ? $stderr : $stdout
|
|
35
52
|
end
|
|
36
53
|
|
|
54
|
+
def self.split_passthrough_argv(argv)
|
|
55
|
+
separator_index = argv.index("--")
|
|
56
|
+
return [argv.dup, []] unless separator_index
|
|
57
|
+
|
|
58
|
+
[argv[0...separator_index], argv[(separator_index + 1)..]]
|
|
59
|
+
end
|
|
60
|
+
|
|
37
61
|
def self.run(argv)
|
|
38
62
|
$stdout.sync = true
|
|
63
|
+
runner_argv, passthrough_argv = split_passthrough_argv(argv)
|
|
39
64
|
|
|
40
65
|
# Show Shakapacker help and exit (don't call bundler)
|
|
41
66
|
# Support --help, -h, and --help=verbose formats
|
|
42
|
-
help_verbose =
|
|
43
|
-
if
|
|
67
|
+
help_verbose = runner_argv.any? { |arg| arg == "--help=verbose" }
|
|
68
|
+
if runner_argv.include?("--help") || runner_argv.include?("-h") || help_verbose
|
|
44
69
|
print_help(verbose: help_verbose)
|
|
45
70
|
exit(0)
|
|
46
|
-
elsif
|
|
71
|
+
elsif runner_argv.include?("--version") || runner_argv.include?("-v")
|
|
47
72
|
print_version
|
|
48
73
|
exit(0)
|
|
49
|
-
elsif
|
|
74
|
+
elsif runner_argv.include?("--init")
|
|
50
75
|
init_config_file
|
|
51
76
|
exit(0)
|
|
52
|
-
elsif
|
|
77
|
+
elsif runner_argv.include?("--list-builds")
|
|
53
78
|
list_builds
|
|
54
79
|
exit(0)
|
|
55
80
|
end
|
|
56
81
|
|
|
57
82
|
# Check for --bundler flag
|
|
58
83
|
bundler_override = nil
|
|
59
|
-
bundler_index =
|
|
84
|
+
bundler_index = runner_argv.index("--bundler")
|
|
60
85
|
if bundler_index
|
|
61
|
-
bundler_value =
|
|
86
|
+
bundler_value = runner_argv[bundler_index + 1]
|
|
62
87
|
unless bundler_value && %w[webpack rspack].include?(bundler_value)
|
|
63
88
|
$stderr.puts "[Shakapacker] Error: --bundler requires 'webpack' or 'rspack'"
|
|
64
89
|
$stderr.puts "Usage: bin/shakapacker --bundler <webpack|rspack>"
|
|
@@ -68,9 +93,9 @@ module Shakapacker
|
|
|
68
93
|
end
|
|
69
94
|
|
|
70
95
|
# Check for --build flag
|
|
71
|
-
build_index =
|
|
96
|
+
build_index = runner_argv.index("--build")
|
|
72
97
|
if build_index
|
|
73
|
-
build_name =
|
|
98
|
+
build_name = runner_argv[build_index + 1]
|
|
74
99
|
|
|
75
100
|
unless build_name
|
|
76
101
|
$stderr.puts "[Shakapacker] Error: --build requires a build name"
|
|
@@ -93,7 +118,7 @@ module Shakapacker
|
|
|
93
118
|
build_config = loader.resolve_build_config(build_name, **resolve_opts)
|
|
94
119
|
|
|
95
120
|
# Remove --build and build name from argv
|
|
96
|
-
remaining_argv =
|
|
121
|
+
remaining_argv = runner_argv.dup
|
|
97
122
|
remaining_argv.delete_at(build_index + 1)
|
|
98
123
|
remaining_argv.delete_at(build_index)
|
|
99
124
|
|
|
@@ -108,17 +133,18 @@ module Shakapacker
|
|
|
108
133
|
|
|
109
134
|
# If this build uses dev server, delegate to DevServerRunner
|
|
110
135
|
if loader.uses_dev_server?(build_config)
|
|
111
|
-
|
|
136
|
+
# Include passthrough --json so Shakapacker logs stay off stdout when bundler JSON is requested.
|
|
137
|
+
log = log_output_for(runner_argv + passthrough_argv)
|
|
112
138
|
log.puts "[Shakapacker] Build '#{build_name}' requires dev server"
|
|
113
139
|
log.puts "[Shakapacker] Running: bin/shakapacker-dev-server --build #{build_name}"
|
|
114
140
|
log.puts ""
|
|
115
141
|
require_relative "dev_server_runner"
|
|
116
|
-
DevServerRunner.run_with_build_config(remaining_argv, build_config)
|
|
142
|
+
DevServerRunner.run_with_build_config(remaining_argv, build_config, passthrough_argv)
|
|
117
143
|
return
|
|
118
144
|
end
|
|
119
145
|
|
|
120
146
|
# Otherwise run with this build config
|
|
121
|
-
run_with_build_config(remaining_argv, build_config)
|
|
147
|
+
run_with_build_config(remaining_argv, build_config, passthrough_argv)
|
|
122
148
|
return
|
|
123
149
|
rescue ArgumentError => e
|
|
124
150
|
$stderr.puts "[Shakapacker] #{e.message}"
|
|
@@ -129,7 +155,7 @@ module Shakapacker
|
|
|
129
155
|
Shakapacker.ensure_node_env!
|
|
130
156
|
|
|
131
157
|
# Remove --bundler flag from argv if present (not using --build)
|
|
132
|
-
remaining_argv =
|
|
158
|
+
remaining_argv = runner_argv.dup
|
|
133
159
|
if bundler_index
|
|
134
160
|
bundler_idx = remaining_argv.index("--bundler")
|
|
135
161
|
if bundler_idx
|
|
@@ -144,39 +170,26 @@ module Shakapacker
|
|
|
144
170
|
|
|
145
171
|
# Create a single runner instance to avoid loading configuration twice.
|
|
146
172
|
# We extend it with the appropriate build command based on the bundler type.
|
|
147
|
-
runner = new(remaining_argv, nil, bundler_override)
|
|
173
|
+
runner = new(remaining_argv, nil, bundler_override, passthrough_argv)
|
|
148
174
|
|
|
149
175
|
# Determine which bundler to use (override takes precedence)
|
|
150
176
|
use_rspack = bundler_override ? (bundler_override == "rspack") : runner.config.rspack?
|
|
151
177
|
|
|
152
178
|
if use_rspack
|
|
153
179
|
require_relative "rspack_runner"
|
|
154
|
-
# Extend the runner instance
|
|
155
|
-
|
|
156
|
-
runner.extend(Module.new do
|
|
157
|
-
def build_cmd
|
|
158
|
-
package_json.manager.native_exec_command("rspack")
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def assets_bundler_commands
|
|
162
|
-
BASE_COMMANDS + %w[build watch]
|
|
163
|
-
end
|
|
164
|
-
end)
|
|
180
|
+
# Extend the runner instance to avoid creating a new RspackRunner and reloading configuration.
|
|
181
|
+
runner.extend(RSPACK_RUNNER_EXTENSION)
|
|
165
182
|
runner.run
|
|
166
183
|
else
|
|
167
184
|
require_relative "webpack_runner"
|
|
168
185
|
# Extend the runner instance with webpack-specific methods
|
|
169
186
|
# This avoids creating a new WebpackRunner which would reload the configuration
|
|
170
|
-
runner.extend(
|
|
171
|
-
def build_cmd
|
|
172
|
-
package_json.manager.native_exec_command("webpack")
|
|
173
|
-
end
|
|
174
|
-
end)
|
|
187
|
+
runner.extend(WEBPACK_RUNNER_EXTENSION)
|
|
175
188
|
runner.run
|
|
176
189
|
end
|
|
177
190
|
end
|
|
178
191
|
|
|
179
|
-
def self.run_with_build_config(argv, build_config)
|
|
192
|
+
def self.run_with_build_config(argv, build_config, passthrough_argv = [])
|
|
180
193
|
$stdout.sync = true
|
|
181
194
|
Shakapacker.ensure_node_env!
|
|
182
195
|
|
|
@@ -189,7 +202,8 @@ module Shakapacker
|
|
|
189
202
|
# This ensures the bundler override (from --bundler or build config) is respected
|
|
190
203
|
ENV["SHAKAPACKER_ASSETS_BUNDLER"] = build_config[:bundler]
|
|
191
204
|
|
|
192
|
-
|
|
205
|
+
# Include passthrough --json so Shakapacker logs stay off stdout when bundler JSON is requested.
|
|
206
|
+
log = log_output_for(argv + passthrough_argv)
|
|
193
207
|
log.puts "[Shakapacker] Running build: #{build_config[:name]}"
|
|
194
208
|
log.puts "[Shakapacker] Description: #{build_config[:description]}" if build_config[:description]
|
|
195
209
|
log.puts "[Shakapacker] Bundler: #{build_config[:bundler]}"
|
|
@@ -197,37 +211,31 @@ module Shakapacker
|
|
|
197
211
|
|
|
198
212
|
# Create runner with modified argv and bundler from build_config
|
|
199
213
|
# The build_config[:bundler] already has any CLI --bundler override applied
|
|
200
|
-
runner = new(argv, build_config, build_config[:bundler])
|
|
214
|
+
runner = new(argv, build_config, build_config[:bundler], passthrough_argv)
|
|
201
215
|
|
|
202
216
|
# Use bundler from build_config (which includes CLI override)
|
|
203
217
|
if build_config[:bundler] == "rspack"
|
|
204
218
|
require_relative "rspack_runner"
|
|
205
|
-
runner.extend(
|
|
206
|
-
def build_cmd
|
|
207
|
-
package_json.manager.native_exec_command("rspack")
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def assets_bundler_commands
|
|
211
|
-
BASE_COMMANDS + %w[build watch]
|
|
212
|
-
end
|
|
213
|
-
end)
|
|
219
|
+
runner.extend(RSPACK_RUNNER_EXTENSION)
|
|
214
220
|
runner.run
|
|
215
221
|
else
|
|
216
222
|
require_relative "webpack_runner"
|
|
217
|
-
runner.extend(
|
|
218
|
-
def build_cmd
|
|
219
|
-
package_json.manager.native_exec_command("webpack")
|
|
220
|
-
end
|
|
221
|
-
end)
|
|
223
|
+
runner.extend(WEBPACK_RUNNER_EXTENSION)
|
|
222
224
|
runner.run
|
|
223
225
|
end
|
|
224
226
|
end
|
|
225
227
|
|
|
226
|
-
def initialize(argv, build_config = nil, bundler_override = nil)
|
|
227
|
-
@argv =
|
|
228
|
+
def initialize(argv, build_config = nil, bundler_override = nil, passthrough_argv = nil)
|
|
229
|
+
@argv, @passthrough_argv =
|
|
230
|
+
unless passthrough_argv.nil?
|
|
231
|
+
[argv, passthrough_argv]
|
|
232
|
+
else
|
|
233
|
+
self.class.split_passthrough_argv(argv)
|
|
234
|
+
end
|
|
228
235
|
@build_config = build_config
|
|
229
236
|
@bundler_override = bundler_override
|
|
230
|
-
|
|
237
|
+
# Bundler --json writes machine-readable output to stdout; route Shakapacker logs to stderr too.
|
|
238
|
+
@json_output = self.class.json_output?(bundler_argv)
|
|
231
239
|
|
|
232
240
|
@app_path = File.expand_path(".", Dir.pwd)
|
|
233
241
|
@shakapacker_config = ENV["SHAKAPACKER_CONFIG"] || File.join(@app_path, "config/shakapacker.yml")
|
|
@@ -260,6 +268,9 @@ module Shakapacker
|
|
|
260
268
|
cmd = build_cmd
|
|
261
269
|
log_output.puts "[Shakapacker] Base command: #{cmd.join(" ")}"
|
|
262
270
|
|
|
271
|
+
detect_shakapacker_flags_in_passthrough!
|
|
272
|
+
|
|
273
|
+
# Shakapacker-owned Node flags must appear before --; passthrough args belong to the bundler.
|
|
263
274
|
if @argv.delete("--debug-shakapacker")
|
|
264
275
|
log_output.puts "[Shakapacker] Debug mode enabled (--debug-shakapacker)"
|
|
265
276
|
env["NODE_OPTIONS"] = "#{env["NODE_OPTIONS"]} --inspect-brk"
|
|
@@ -276,18 +287,19 @@ module Shakapacker
|
|
|
276
287
|
end
|
|
277
288
|
|
|
278
289
|
# Commands are not compatible with --config option.
|
|
279
|
-
|
|
290
|
+
incompatible_args = config_incompatible_args
|
|
291
|
+
if incompatible_args.empty?
|
|
280
292
|
log_output.puts "[Shakapacker] Adding config file: #{@webpack_config}"
|
|
281
293
|
cmd += ["--config", @webpack_config]
|
|
282
294
|
else
|
|
283
|
-
log_output.puts "[Shakapacker] Skipping config file (running assets bundler command: #{
|
|
295
|
+
log_output.puts "[Shakapacker] Skipping config file (running assets bundler command: #{incompatible_args.join(", ")})"
|
|
284
296
|
end
|
|
285
297
|
|
|
286
|
-
cmd +=
|
|
298
|
+
cmd += bundler_argv
|
|
287
299
|
log_output.puts "[Shakapacker] Final command: #{cmd.join(" ")}"
|
|
288
300
|
log_output.puts "[Shakapacker] Working directory: #{@app_path}"
|
|
289
301
|
|
|
290
|
-
watch_mode =
|
|
302
|
+
watch_mode = bundler_argv.include?("--watch") || bundler_argv.include?("-w")
|
|
291
303
|
start_time = Time.now unless watch_mode
|
|
292
304
|
|
|
293
305
|
Dir.chdir(@app_path) do
|
|
@@ -319,10 +331,33 @@ module Shakapacker
|
|
|
319
331
|
|
|
320
332
|
protected
|
|
321
333
|
|
|
334
|
+
def bundler_argv
|
|
335
|
+
@argv + @passthrough_argv
|
|
336
|
+
end
|
|
337
|
+
|
|
322
338
|
def assets_bundler_commands
|
|
323
339
|
BASE_COMMANDS
|
|
324
340
|
end
|
|
325
341
|
|
|
342
|
+
def config_incompatible_args
|
|
343
|
+
bundler_argv & assets_bundler_commands
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def detect_shakapacker_flags_in_passthrough!(flags = SHAKAPACKER_NODE_FLAGS)
|
|
347
|
+
misplaced_flags = @passthrough_argv & flags
|
|
348
|
+
return if misplaced_flags.empty?
|
|
349
|
+
|
|
350
|
+
log_output.puts(
|
|
351
|
+
"The following Shakapacker-specific options must appear before --: #{misplaced_flags.join(' ')}."
|
|
352
|
+
)
|
|
353
|
+
exit_after_shakapacker_flag_error(1)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def exit_after_shakapacker_flag_error(status)
|
|
357
|
+
log_output.flush
|
|
358
|
+
exit(status)
|
|
359
|
+
end
|
|
360
|
+
|
|
326
361
|
def print_config_not_found_error(bundler_type, config_path, config_dir)
|
|
327
362
|
$stderr.puts "[Shakapacker] ERROR: #{bundler_type} config #{config_path} not found."
|
|
328
363
|
$stderr.puts ""
|
|
@@ -354,6 +389,9 @@ module Shakapacker
|
|
|
354
389
|
--bundler <webpack|rspack>
|
|
355
390
|
Override bundler (defaults to shakapacker.yml)
|
|
356
391
|
|
|
392
|
+
Put Shakapacker-specific options before --. Arguments after -- are
|
|
393
|
+
passed directly to the selected bundler.
|
|
394
|
+
|
|
357
395
|
Build configurations (config/shakapacker-builds.yml):
|
|
358
396
|
--init Create config/shakapacker-builds.yml
|
|
359
397
|
--list-builds List available builds
|
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
require "English"
|
|
4
4
|
require "rake/file_utils"
|
|
5
|
+
require "shellwords"
|
|
5
6
|
|
|
6
7
|
module Shakapacker
|
|
7
8
|
module Utils
|
|
8
9
|
class Misc
|
|
9
10
|
extend FileUtils
|
|
10
11
|
|
|
12
|
+
NODE_BINSTUB_EXECUTABLES = %w[node nodejs].freeze
|
|
13
|
+
ENV_FLAGS_WITH_ARGUMENTS = %w[-u --unset -C --chdir -P --path -a --argv0].freeze
|
|
14
|
+
private_constant :NODE_BINSTUB_EXECUTABLES
|
|
15
|
+
private_constant :ENV_FLAGS_WITH_ARGUMENTS
|
|
16
|
+
|
|
11
17
|
def self.uncommitted_changes?(message_handler)
|
|
12
18
|
return false if ENV["COVERAGE"] == "true"
|
|
13
19
|
|
|
@@ -31,6 +37,38 @@ module Shakapacker
|
|
|
31
37
|
def self.sh_in_dir(dir, *shell_commands)
|
|
32
38
|
shell_commands.flatten.each { |shell_command| sh %(cd '#{dir}' && #{shell_command.strip}) }
|
|
33
39
|
end
|
|
40
|
+
|
|
41
|
+
def self.js_binstub_executable(path)
|
|
42
|
+
return nil unless File.file?(path)
|
|
43
|
+
|
|
44
|
+
shebang = File.open(path, "rb") { |f| f.gets }.to_s.chomp
|
|
45
|
+
return nil unless shebang.start_with?("#!")
|
|
46
|
+
|
|
47
|
+
shebang_tokens = begin
|
|
48
|
+
Shellwords.split(shebang.delete_prefix("#!"))
|
|
49
|
+
rescue ArgumentError
|
|
50
|
+
return nil
|
|
51
|
+
end
|
|
52
|
+
executable = shebang_tokens.first.to_s
|
|
53
|
+
|
|
54
|
+
if File.basename(executable) == "env"
|
|
55
|
+
shebang_tokens = shebang_tokens.drop(1)
|
|
56
|
+
while (env_token = shebang_tokens.first)
|
|
57
|
+
if env_token.start_with?("-")
|
|
58
|
+
env_flag = shebang_tokens.shift
|
|
59
|
+
shebang_tokens.shift if ENV_FLAGS_WITH_ARGUMENTS.include?(env_flag)
|
|
60
|
+
elsif env_token.match?(/\A[A-Za-z_][A-Za-z0-9_]*=/)
|
|
61
|
+
shebang_tokens.shift
|
|
62
|
+
else
|
|
63
|
+
break
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
executable = shebang_tokens.first.to_s
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Preserve direct interpreter paths so stale absolute Node shebangs fail with actionable binstub guidance.
|
|
70
|
+
NODE_BINSTUB_EXECUTABLES.include?(File.basename(executable)) ? executable : nil
|
|
71
|
+
end
|
|
34
72
|
end
|
|
35
73
|
end
|
|
36
74
|
end
|
data/lib/shakapacker/version.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require "yaml"
|
|
3
|
+
|
|
2
4
|
require_relative "version"
|
|
3
5
|
|
|
4
6
|
module Shakapacker
|
|
@@ -224,13 +226,19 @@ module Shakapacker
|
|
|
224
226
|
|
|
225
227
|
def from_pnpm_lock
|
|
226
228
|
require "yaml"
|
|
229
|
+
require "date"
|
|
230
|
+
|
|
231
|
+
# pnpm >= 10.16 writes a `time:` section; permit Time/Date so Psych 4+ doesn't raise DisallowedClass.
|
|
232
|
+
content = safe_load_pnpm_lock(File.read(@pnpm_lock))
|
|
227
233
|
|
|
228
|
-
|
|
234
|
+
packages = content.is_a?(Hash) ? (content["packages"] || {}) : {}
|
|
229
235
|
|
|
230
|
-
|
|
236
|
+
packages.each do |key, value|
|
|
231
237
|
# git-based constraints will include a "version" key with their pseudo semantic version
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
if value.is_a?(Hash)
|
|
239
|
+
return value["version"] if key.start_with?("shakapacker") && value.key?("version")
|
|
240
|
+
return value["version"] if value["name"] == "shakapacker"
|
|
241
|
+
end
|
|
234
242
|
|
|
235
243
|
# v9+ uses the same key format just without the leading slash, so we just add one in
|
|
236
244
|
key = "/#{key}" unless key.start_with?("/")
|
|
@@ -248,6 +256,10 @@ module Shakapacker
|
|
|
248
256
|
|
|
249
257
|
nil
|
|
250
258
|
end
|
|
259
|
+
|
|
260
|
+
def safe_load_pnpm_lock(lockfile)
|
|
261
|
+
YAML.safe_load(lockfile, permitted_classes: [Time, Date])
|
|
262
|
+
end
|
|
251
263
|
end
|
|
252
264
|
end
|
|
253
265
|
end
|
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
def shakapacker_config_binstub_command(bin_path)
|
|
3
|
-
# Read in binary mode so Windows CRLF line endings do not leak \r into the shebang.
|
|
4
|
-
shebang = File.open(bin_path, "rb", &:gets).to_s
|
|
5
|
-
command = shebang.delete_prefix("#!").strip.split(/\s+/)
|
|
6
|
-
executable = File.basename(command.first.to_s)
|
|
7
|
-
|
|
8
|
-
if executable == "env"
|
|
9
|
-
executable = File.basename(command.drop(1).find { |part| !part.start_with?("-") }.to_s)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Legacy JS binstubs are dispatched via PATH lookup; Kernel#exec resolves
|
|
13
|
-
# "node" through the shell's PATH. The Ruby binstubs perform their own
|
|
14
|
-
# explicit lookup via shakapacker_find_executable, which matters more on
|
|
15
|
-
# Windows where PATHEXT is involved.
|
|
16
|
-
executable == "node" ? ["node", bin_path.to_s] : [RbConfig.ruby, bin_path.to_s]
|
|
17
|
-
end
|
|
1
|
+
require "shakapacker/utils/misc"
|
|
18
2
|
|
|
3
|
+
namespace :shakapacker do
|
|
19
4
|
desc <<~DESC
|
|
20
5
|
Export webpack or rspack configuration for debugging and analysis
|
|
21
6
|
|
|
@@ -62,7 +47,7 @@ namespace :shakapacker do
|
|
|
62
47
|
# Try to use the binstub if it exists, otherwise use the gem's version
|
|
63
48
|
bin_path = Rails.root.join("bin/shakapacker-config")
|
|
64
49
|
|
|
65
|
-
unless File.
|
|
50
|
+
unless File.file?(bin_path)
|
|
66
51
|
# Binstub not installed, use the gem's version directly
|
|
67
52
|
gem_bin_path = File.expand_path("../../install/bin/shakapacker-config", __dir__)
|
|
68
53
|
|
|
@@ -75,10 +60,34 @@ namespace :shakapacker do
|
|
|
75
60
|
end
|
|
76
61
|
else
|
|
77
62
|
# Pass through command-line arguments after the task name.
|
|
78
|
-
#
|
|
79
|
-
#
|
|
63
|
+
#
|
|
64
|
+
# Modern Ruby binstubs (the current default) are invoked with
|
|
65
|
+
# RbConfig.ruby so they run under the same Ruby as Rake — this avoids
|
|
66
|
+
# version-manager/shebang mismatches and works on Windows.
|
|
67
|
+
#
|
|
68
|
+
# Legacy JavaScript binstubs left over from earlier Shakapacker
|
|
69
|
+
# versions (`#!/usr/bin/env node`) are invoked through Node until
|
|
70
|
+
# users refresh them with `rake shakapacker:binstubs`.
|
|
71
|
+
js_binstub_executable = Shakapacker::Utils::Misc.js_binstub_executable(bin_path)
|
|
72
|
+
|
|
80
73
|
Dir.chdir(Rails.root) do
|
|
81
|
-
|
|
74
|
+
if js_binstub_executable
|
|
75
|
+
$stderr.puts "Note: bin/shakapacker-config is a legacy JavaScript binstub."
|
|
76
|
+
$stderr.puts "Run `bundle exec rake shakapacker:binstubs` to upgrade to the Ruby binstub."
|
|
77
|
+
$stderr.puts ""
|
|
78
|
+
begin
|
|
79
|
+
Kernel.exec(js_binstub_executable, bin_path.to_s, *ARGV[1..])
|
|
80
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
81
|
+
abort "Error: could not execute '#{js_binstub_executable}' because it was not found or is not executable.\n" \
|
|
82
|
+
"Run `bundle exec rake shakapacker:binstubs` to upgrade to the Ruby binstub."
|
|
83
|
+
end
|
|
84
|
+
else
|
|
85
|
+
begin
|
|
86
|
+
Kernel.exec(RbConfig.ruby, bin_path.to_s, *ARGV[1..])
|
|
87
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
88
|
+
abort "Error: Ruby interpreter '#{RbConfig.ruby}' was not found or is not executable."
|
|
89
|
+
end
|
|
90
|
+
end
|
|
82
91
|
end
|
|
83
92
|
end
|
|
84
93
|
end
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
require "shakapacker/install/env"
|
|
2
|
+
|
|
1
3
|
install_template_path = File.expand_path("../../install/template.rb", __dir__).freeze
|
|
2
4
|
bin_path = ENV["BUNDLE_BIN"] || Rails.root.join("bin")
|
|
3
5
|
|
|
4
6
|
namespace :shakapacker do
|
|
5
|
-
desc "Install Shakapacker in this application (
|
|
7
|
+
desc "Install Shakapacker in this application (defaults to Rspack; pass webpack or SHAKAPACKER_ASSETS_BUNDLER=webpack for Webpack)"
|
|
6
8
|
task :install, [:bundler, :typescript] => [:check_node] do |task, args|
|
|
7
9
|
Shakapacker::Configuration.installing = true
|
|
8
10
|
|
|
9
|
-
if args[:bundler]
|
|
10
|
-
|
|
11
|
+
if (bundler_error = Shakapacker::Install::Env.apply_bundler_arg(args[:bundler]))
|
|
12
|
+
abort bundler_error
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
# Set typescript flag if passed as argument
|
data/lib/tasks/shakapacker.rake
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
tasks = {
|
|
2
2
|
"shakapacker:info" => "Provides information on Shakapacker's environment",
|
|
3
|
-
"shakapacker:install" => "Installs and
|
|
3
|
+
"shakapacker:install" => "Installs and sets up Shakapacker",
|
|
4
4
|
"shakapacker:compile" => "Compiles webpack bundles based on environment",
|
|
5
5
|
"shakapacker:clean" => "Remove old compiled bundles",
|
|
6
6
|
"shakapacker:clobber" => "Removes the webpack compiled output directory",
|