shakapacker 9.3.0.beta.2 → 9.3.0.beta.5
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 +20 -0
- data/CLAUDE.md +13 -0
- data/ESLINT_TECHNICAL_DEBT.md +13 -14
- data/Gemfile.lock +1 -1
- data/README.md +28 -6
- data/docs/configuration.md +136 -0
- data/docs/early_hints.md +440 -0
- data/docs/early_hints_new_api.md +700 -0
- data/docs/feature_testing.md +492 -0
- data/docs/preventing_fouc.md +132 -0
- data/docs/troubleshooting.md +4 -0
- data/eslint.config.js +2 -6
- data/lib/install/config/shakapacker.yml +27 -0
- data/lib/shakapacker/build_config_loader.rb +147 -0
- data/lib/shakapacker/configuration.rb +10 -2
- data/lib/shakapacker/dev_server_runner.rb +73 -0
- data/lib/shakapacker/helper.rb +409 -14
- data/lib/shakapacker/railtie.rb +4 -0
- data/lib/shakapacker/runner.rb +239 -14
- data/lib/shakapacker/version.rb +1 -1
- data/package/configExporter/buildValidator.ts +3 -4
- data/package/configExporter/cli.ts +253 -161
- data/package/configExporter/configFile.ts +182 -46
- data/package/configExporter/fileWriter.ts +8 -1
- data/package/configExporter/types.ts +2 -0
- data/package/utils/pathValidation.ts +3 -3
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/scripts/remove-use-strict.js +0 -1
- data/test/configExporter/configFile.test.js +3 -2
- data/test/configExporter/integration.test.js +14 -27
- data/test/package/rules/babel.test.js +1 -0
- data/test/package/rules/swc.test.js +1 -0
- metadata +9 -4
- /data/bin/{export-bundler-config → shakapacker-config} +0 -0
- /data/lib/install/bin/{export-bundler-config → shakapacker-config} +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
module Shakapacker
|
|
4
|
+
class BuildConfigLoader
|
|
5
|
+
attr_reader :config_file_path
|
|
6
|
+
|
|
7
|
+
def initialize(config_file_path = nil)
|
|
8
|
+
@config_file_path = config_file_path || File.join(Dir.pwd, "config", "shakapacker-builds.yml")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def exists?
|
|
12
|
+
File.exist?(@config_file_path)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def load_build(build_name)
|
|
16
|
+
unless exists?
|
|
17
|
+
raise ArgumentError, "Config file not found: #{@config_file_path}\n" \
|
|
18
|
+
"Run 'bin/shakapacker --init' to generate a sample config file."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
config = load_config
|
|
22
|
+
fetch_build_or_raise(config, build_name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def resolve_build_config(build_name, default_bundler: "webpack")
|
|
26
|
+
config = load_config
|
|
27
|
+
build = fetch_build_or_raise(config, build_name)
|
|
28
|
+
|
|
29
|
+
# Resolve bundler with precedence: build.bundler > config.default_bundler > default_bundler
|
|
30
|
+
bundler = build["bundler"] || config["default_bundler"] || default_bundler
|
|
31
|
+
|
|
32
|
+
# Get environment variables
|
|
33
|
+
environment = build["environment"] || {}
|
|
34
|
+
|
|
35
|
+
# Get config file path if specified
|
|
36
|
+
config_file = build["config"]
|
|
37
|
+
if config_file
|
|
38
|
+
# Expand ${BUNDLER} variable
|
|
39
|
+
config_file = config_file.gsub("${BUNDLER}", bundler)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Get bundler_env for --env flags
|
|
43
|
+
bundler_env = build["bundler_env"] || {}
|
|
44
|
+
|
|
45
|
+
# Get outputs
|
|
46
|
+
outputs = build["outputs"] || []
|
|
47
|
+
|
|
48
|
+
# Validate outputs
|
|
49
|
+
if outputs.empty?
|
|
50
|
+
raise ArgumentError, "Build '#{build_name}' has empty outputs array. " \
|
|
51
|
+
"Please specify at least one output type (client, server, or all)."
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
name: build_name,
|
|
56
|
+
description: build["description"],
|
|
57
|
+
bundler: bundler,
|
|
58
|
+
dev_server: build["dev_server"],
|
|
59
|
+
environment: environment,
|
|
60
|
+
bundler_env: bundler_env,
|
|
61
|
+
outputs: outputs,
|
|
62
|
+
config_file: config_file
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def uses_dev_server?(build_config)
|
|
67
|
+
# Check explicit dev_server flag first (preferred)
|
|
68
|
+
# Only return early if the value is explicitly set (not nil)
|
|
69
|
+
return build_config[:dev_server] unless build_config[:dev_server].nil?
|
|
70
|
+
|
|
71
|
+
# Fallback: check environment variables for backward compatibility
|
|
72
|
+
env = build_config[:environment]
|
|
73
|
+
return false unless env
|
|
74
|
+
|
|
75
|
+
# Handle both string "true" and boolean true from YAML
|
|
76
|
+
%w[WEBPACK_SERVE HMR].any? do |key|
|
|
77
|
+
value = env[key]
|
|
78
|
+
value.to_s.strip.casecmp("true").zero?
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def list_builds
|
|
83
|
+
config = load_config
|
|
84
|
+
builds = config["builds"]
|
|
85
|
+
|
|
86
|
+
puts "\nAvailable builds in #{@config_file_path}:\n\n"
|
|
87
|
+
|
|
88
|
+
builds.each do |name, build|
|
|
89
|
+
bundler = build["bundler"] || config["default_bundler"] || "webpack (default)"
|
|
90
|
+
outputs = build["outputs"] ? build["outputs"].join(", ") : "missing (invalid)"
|
|
91
|
+
|
|
92
|
+
puts " #{name}"
|
|
93
|
+
puts " Description: #{build["description"]}" if build["description"]
|
|
94
|
+
puts " Bundler: #{bundler}"
|
|
95
|
+
puts " Outputs: #{outputs}"
|
|
96
|
+
puts ""
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def fetch_build_or_raise(config, build_name)
|
|
103
|
+
build = config["builds"][build_name]
|
|
104
|
+
unless build
|
|
105
|
+
available = config["builds"].keys.join(", ")
|
|
106
|
+
raise ArgumentError, "Build '#{build_name}' not found in config file.\n" \
|
|
107
|
+
"Available builds: #{available}\n" \
|
|
108
|
+
"Use 'bin/shakapacker --list-builds' to see all available builds."
|
|
109
|
+
end
|
|
110
|
+
build
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Load YAML config file safely with Ruby version compatibility
|
|
114
|
+
# Ruby 3.1+ supports safe_load_file with aliases, older versions need safe_load
|
|
115
|
+
def load_config
|
|
116
|
+
begin
|
|
117
|
+
config = if YAML.respond_to?(:safe_load_file)
|
|
118
|
+
# Ruby 3.1+: Use safe_load_file with aliases enabled
|
|
119
|
+
YAML.safe_load_file(@config_file_path, aliases: true)
|
|
120
|
+
else
|
|
121
|
+
# Ruby 2.7-3.0: Use safe_load with aliases enabled
|
|
122
|
+
YAML.safe_load(
|
|
123
|
+
File.read(@config_file_path),
|
|
124
|
+
permitted_classes: [],
|
|
125
|
+
permitted_symbols: [],
|
|
126
|
+
aliases: true
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
rescue ArgumentError
|
|
130
|
+
# Fallback for older Psych versions without aliases support
|
|
131
|
+
config = YAML.safe_load(
|
|
132
|
+
File.read(@config_file_path),
|
|
133
|
+
permitted_classes: [],
|
|
134
|
+
permitted_symbols: []
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
unless config["builds"]&.is_a?(Hash)
|
|
139
|
+
raise ArgumentError, "Config file must contain a 'builds' object"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
config
|
|
143
|
+
rescue Psych::SyntaxError => e
|
|
144
|
+
raise ArgumentError, "Invalid YAML in config file: #{e.message}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -8,12 +8,13 @@ class Shakapacker::Configuration
|
|
|
8
8
|
attr_accessor :installing
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
attr_reader :root_path, :config_path, :env
|
|
11
|
+
attr_reader :root_path, :config_path, :env, :bundler_override
|
|
12
12
|
|
|
13
|
-
def initialize(root_path:, config_path:, env:)
|
|
13
|
+
def initialize(root_path:, config_path:, env:, bundler_override: nil)
|
|
14
14
|
@root_path = root_path
|
|
15
15
|
@env = env
|
|
16
16
|
@config_path = config_path
|
|
17
|
+
@bundler_override = bundler_override
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def dev_server
|
|
@@ -97,6 +98,9 @@ class Shakapacker::Configuration
|
|
|
97
98
|
end
|
|
98
99
|
|
|
99
100
|
def assets_bundler
|
|
101
|
+
# CLI --bundler flag takes highest precedence
|
|
102
|
+
return @bundler_override if @bundler_override
|
|
103
|
+
|
|
100
104
|
# Show deprecation warning if using old 'bundler' key
|
|
101
105
|
if data.has_key?(:bundler) && !data.has_key?(:assets_bundler)
|
|
102
106
|
$stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is deprecated. Please use 'assets_bundler' instead to avoid confusion with Ruby's Bundler gem manager."
|
|
@@ -231,6 +235,10 @@ class Shakapacker::Configuration
|
|
|
231
235
|
fetch(:integrity)
|
|
232
236
|
end
|
|
233
237
|
|
|
238
|
+
def early_hints
|
|
239
|
+
fetch(:early_hints)
|
|
240
|
+
end
|
|
241
|
+
|
|
234
242
|
private
|
|
235
243
|
def validate_output_paths!
|
|
236
244
|
# Skip validation if already validated to avoid redundant checks
|
|
@@ -18,9 +18,71 @@ module Shakapacker
|
|
|
18
18
|
exit(0)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# Check for --build flag
|
|
22
|
+
build_index = argv.index("--build")
|
|
23
|
+
if build_index
|
|
24
|
+
build_name = argv[build_index + 1]
|
|
25
|
+
|
|
26
|
+
unless build_name
|
|
27
|
+
$stderr.puts "[Shakapacker] Error: --build requires a build name"
|
|
28
|
+
$stderr.puts "Usage: bin/shakapacker-dev-server --build <name>"
|
|
29
|
+
exit(1)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
loader = BuildConfigLoader.new
|
|
33
|
+
|
|
34
|
+
unless loader.exists?
|
|
35
|
+
$stderr.puts "[Shakapacker] Config file not found: #{loader.config_file_path}"
|
|
36
|
+
$stderr.puts "Run 'bin/shakapacker --init' to create one"
|
|
37
|
+
exit(1)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
build_config = loader.resolve_build_config(build_name)
|
|
42
|
+
|
|
43
|
+
# Check if this build is meant for dev server
|
|
44
|
+
unless loader.uses_dev_server?(build_config)
|
|
45
|
+
$stderr.puts "[Shakapacker] Error: Build '#{build_name}' is not configured for dev server (dev_server: false)"
|
|
46
|
+
$stderr.puts "[Shakapacker] Use this command instead:"
|
|
47
|
+
$stderr.puts " bin/shakapacker --build #{build_name}"
|
|
48
|
+
exit(1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Remove --build and build name from argv
|
|
52
|
+
remaining_argv = argv.dup
|
|
53
|
+
remaining_argv.delete_at(build_index + 1)
|
|
54
|
+
remaining_argv.delete_at(build_index)
|
|
55
|
+
|
|
56
|
+
run_with_build_config(remaining_argv, build_config)
|
|
57
|
+
return
|
|
58
|
+
rescue ArgumentError => e
|
|
59
|
+
$stderr.puts "[Shakapacker] #{e.message}"
|
|
60
|
+
exit(1)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
21
64
|
new(argv).run
|
|
22
65
|
end
|
|
23
66
|
|
|
67
|
+
def self.run_with_build_config(argv, build_config)
|
|
68
|
+
# Apply build config environment variables
|
|
69
|
+
build_config[:environment].each do |key, value|
|
|
70
|
+
ENV[key] = value.to_s
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Set SHAKAPACKER_ASSETS_BUNDLER so JS/TS config files use the correct bundler
|
|
74
|
+
# This ensures the bundler override (from --bundler or build config) is respected
|
|
75
|
+
ENV["SHAKAPACKER_ASSETS_BUNDLER"] = build_config[:bundler]
|
|
76
|
+
|
|
77
|
+
puts "[Shakapacker] Running dev server for build: #{build_config[:name]}"
|
|
78
|
+
puts "[Shakapacker] Description: #{build_config[:description]}" if build_config[:description]
|
|
79
|
+
puts "[Shakapacker] Bundler: #{build_config[:bundler]}"
|
|
80
|
+
puts "[Shakapacker] Config file: #{build_config[:config_file]}" if build_config[:config_file]
|
|
81
|
+
|
|
82
|
+
# Pass bundler override so Configuration.assets_bundler reflects the build
|
|
83
|
+
new(argv, build_config, build_config[:bundler]).run
|
|
84
|
+
end
|
|
85
|
+
|
|
24
86
|
def self.print_help
|
|
25
87
|
puts <<~HELP
|
|
26
88
|
================================================================================
|
|
@@ -33,6 +95,17 @@ module Shakapacker
|
|
|
33
95
|
-h, --help Show this help message
|
|
34
96
|
-v, --version Show Shakapacker version
|
|
35
97
|
--debug-shakapacker Enable Node.js debugging (--inspect-brk)
|
|
98
|
+
--build <name> Run a specific build configuration
|
|
99
|
+
|
|
100
|
+
Build configurations (config/shakapacker-builds.yml):
|
|
101
|
+
bin/shakapacker-dev-server --build dev-hmr # Run the 'dev-hmr' build
|
|
102
|
+
|
|
103
|
+
To manage builds:
|
|
104
|
+
bin/shakapacker --init # Create config file
|
|
105
|
+
bin/shakapacker --list-builds # List available builds
|
|
106
|
+
|
|
107
|
+
Note: You can also use bin/shakapacker --build with a build that has
|
|
108
|
+
WEBPACK_SERVE=true, and it will automatically use the dev server.
|
|
36
109
|
|
|
37
110
|
Examples:
|
|
38
111
|
bin/shakapacker-dev-server # Start dev server
|