shakapacker 8.4.0 → 9.0.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/.eslintignore +1 -0
- data/.eslintrc.fast.js +40 -0
- data/.eslintrc.js +48 -0
- data/.github/STATUS.md +1 -0
- data/.github/workflows/claude-code-review.yml +54 -0
- data/.github/workflows/claude.yml +50 -0
- data/.github/workflows/dummy.yml +8 -4
- data/.github/workflows/generator.yml +17 -14
- data/.github/workflows/node.yml +23 -1
- data/.github/workflows/ruby.yml +11 -0
- data/.github/workflows/test-bundlers.yml +170 -0
- data/.gitignore +17 -0
- data/.husky/pre-commit +2 -0
- data/.npmignore +56 -0
- data/.prettierignore +3 -0
- data/.rubocop.yml +1 -0
- data/.yalcignore +26 -0
- data/CHANGELOG.md +156 -18
- data/CLAUDE.md +29 -0
- data/CONTRIBUTING.md +138 -20
- data/Gemfile.lock +3 -3
- data/README.md +130 -5
- data/Rakefile +39 -4
- data/TODO.md +50 -0
- data/TODO_v9.md +87 -0
- data/conductor-setup.sh +70 -0
- data/conductor.json +7 -0
- data/docs/cdn_setup.md +379 -0
- data/docs/css-modules-export-mode.md +512 -0
- data/docs/deployment.md +10 -1
- data/docs/optional-peer-dependencies.md +198 -0
- data/docs/peer-dependencies.md +60 -0
- data/docs/rspack.md +190 -0
- data/docs/rspack_migration_guide.md +202 -0
- data/docs/transpiler-migration.md +188 -0
- data/docs/transpiler-performance.md +179 -0
- data/docs/troubleshooting.md +5 -0
- data/docs/typescript-migration.md +378 -0
- data/docs/typescript.md +99 -0
- data/docs/using_esbuild_loader.md +3 -3
- data/docs/using_swc_loader.md +5 -3
- data/docs/v6_upgrade.md +10 -0
- data/docs/v9_upgrade.md +413 -0
- data/lib/install/bin/shakapacker +3 -5
- data/lib/install/config/rspack/rspack.config.js +6 -0
- data/lib/install/config/rspack/rspack.config.ts +7 -0
- data/lib/install/config/shakapacker.yml +12 -2
- data/lib/install/config/webpack/webpack.config.ts +7 -0
- data/lib/install/package.json +38 -0
- data/lib/install/template.rb +194 -44
- data/lib/shakapacker/configuration.rb +141 -0
- data/lib/shakapacker/dev_server_runner.rb +25 -5
- data/lib/shakapacker/doctor.rb +844 -0
- data/lib/shakapacker/manifest.rb +4 -2
- data/lib/shakapacker/rspack_runner.rb +19 -0
- data/lib/shakapacker/runner.rb +144 -4
- data/lib/shakapacker/swc_migrator.rb +376 -0
- data/lib/shakapacker/utils/manager.rb +2 -0
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker/version_checker.rb +1 -1
- data/lib/shakapacker/webpack_runner.rb +4 -42
- data/lib/shakapacker.rb +2 -1
- data/lib/tasks/shakapacker/doctor.rake +8 -0
- data/lib/tasks/shakapacker/install.rake +12 -2
- data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
- data/lib/tasks/shakapacker.rake +1 -0
- data/package/.npmignore +4 -0
- data/package/babel/preset.ts +56 -0
- data/package/config.ts +175 -0
- data/package/{dev_server.js → dev_server.ts} +8 -5
- data/package/env.ts +92 -0
- data/package/environments/base.ts +138 -0
- data/package/environments/development.ts +90 -0
- data/package/environments/production.ts +80 -0
- data/package/environments/test.ts +53 -0
- data/package/environments/types.ts +90 -0
- data/package/esbuild/index.ts +42 -0
- data/package/index.d.ts +3 -97
- data/package/index.ts +52 -0
- data/package/loaders.d.ts +28 -0
- data/package/optimization/rspack.ts +36 -0
- data/package/optimization/webpack.ts +57 -0
- data/package/plugins/rspack.ts +103 -0
- data/package/plugins/webpack.ts +62 -0
- data/package/rspack/index.ts +64 -0
- data/package/rules/{babel.js → babel.ts} +2 -2
- data/package/rules/{coffee.js → coffee.ts} +1 -1
- data/package/rules/css.ts +3 -0
- data/package/rules/{erb.js → erb.ts} +1 -1
- data/package/rules/esbuild.ts +10 -0
- data/package/rules/file.ts +40 -0
- data/package/rules/{jscommon.js → jscommon.ts} +4 -4
- data/package/rules/{less.js → less.ts} +4 -4
- data/package/rules/raw.ts +25 -0
- data/package/rules/rspack.ts +176 -0
- data/package/rules/{sass.js → sass.ts} +7 -3
- data/package/rules/{stylus.js → stylus.ts} +4 -8
- data/package/rules/swc.ts +10 -0
- data/package/rules/{index.js → webpack.ts} +1 -1
- data/package/swc/index.ts +54 -0
- data/package/types/README.md +87 -0
- data/package/types/index.ts +60 -0
- data/package/types.ts +108 -0
- data/package/utils/configPath.ts +6 -0
- data/package/utils/debug.ts +49 -0
- data/package/utils/defaultConfigPath.ts +4 -0
- data/package/utils/errorCodes.ts +219 -0
- data/package/utils/errorHelpers.ts +143 -0
- data/package/utils/getStyleRule.ts +64 -0
- data/package/utils/helpers.ts +85 -0
- data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
- data/package/utils/pathValidation.ts +139 -0
- data/package/utils/requireOrError.ts +15 -0
- data/package/utils/snakeToCamelCase.ts +5 -0
- data/package/utils/typeGuards.ts +342 -0
- data/package/utils/validateDependencies.ts +61 -0
- data/package/webpack-types.d.ts +33 -0
- data/package/webpackDevServerConfig.ts +117 -0
- data/package.json +134 -9
- data/scripts/remove-use-strict.js +45 -0
- data/scripts/type-check-no-emit.js +27 -0
- data/test/package/config.test.js +3 -0
- data/test/package/env.test.js +42 -7
- data/test/package/environments/base.test.js +5 -1
- data/test/package/rules/babel.test.js +16 -0
- data/test/package/rules/esbuild.test.js +1 -1
- data/test/package/rules/raw.test.js +40 -7
- data/test/package/rules/swc.test.js +1 -1
- data/test/package/rules/webpack.test.js +35 -0
- data/test/package/staging.test.js +4 -3
- data/test/package/transpiler-defaults.test.js +127 -0
- data/test/peer-dependencies.sh +85 -0
- data/test/scripts/remove-use-strict.test.js +125 -0
- data/test/typescript/build.test.js +118 -0
- data/test/typescript/environments.test.js +107 -0
- data/test/typescript/pathValidation.test.js +142 -0
- data/test/typescript/securityValidation.test.js +182 -0
- data/tools/README.md +124 -0
- data/tools/css-modules-v9-codemod.js +179 -0
- data/tsconfig.eslint.json +16 -0
- data/tsconfig.json +38 -0
- data/yarn.lock +2704 -767
- metadata +111 -41
- data/package/babel/preset.js +0 -48
- data/package/config.js +0 -56
- data/package/env.js +0 -48
- data/package/environments/base.js +0 -171
- data/package/environments/development.js +0 -13
- data/package/environments/production.js +0 -88
- data/package/environments/test.js +0 -3
- data/package/esbuild/index.js +0 -40
- data/package/index.js +0 -40
- data/package/rules/css.js +0 -3
- data/package/rules/esbuild.js +0 -10
- data/package/rules/file.js +0 -29
- data/package/rules/raw.js +0 -5
- data/package/rules/swc.js +0 -10
- data/package/swc/index.js +0 -50
- data/package/utils/configPath.js +0 -4
- data/package/utils/defaultConfigPath.js +0 -2
- data/package/utils/getStyleRule.js +0 -40
- data/package/utils/helpers.js +0 -62
- data/package/utils/snakeToCamelCase.js +0 -5
- data/package/webpackDevServerConfig.js +0 -71
- data/test/package/rules/index.test.js +0 -16
data/lib/install/template.rb
CHANGED
|
@@ -2,26 +2,72 @@ require "shakapacker/utils/misc"
|
|
|
2
2
|
require "shakapacker/utils/manager"
|
|
3
3
|
require "shakapacker/utils/version_syntax_converter"
|
|
4
4
|
require "package_json"
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "json"
|
|
5
7
|
|
|
6
8
|
# Install Shakapacker
|
|
7
9
|
|
|
8
10
|
force_option = ENV["FORCE"] ? { force: true } : {}
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
# Initialize variables for use throughout the template
|
|
13
|
+
# Using instance variable to avoid method definition issues in Rails templates
|
|
14
|
+
@package_json ||= PackageJson.new
|
|
15
|
+
install_dir = File.expand_path(File.dirname(__FILE__))
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
# Installation strategy:
|
|
18
|
+
# - USE_BABEL_PACKAGES installs both babel AND swc for compatibility
|
|
19
|
+
# - Otherwise install only the specified transpiler
|
|
20
|
+
if ENV["USE_BABEL_PACKAGES"] == "true" || ENV["USE_BABEL_PACKAGES"] == "1"
|
|
21
|
+
@transpiler_to_install = "babel"
|
|
22
|
+
say "📦 Installing Babel packages (USE_BABEL_PACKAGES is set)", :yellow
|
|
23
|
+
say "✨ Also installing SWC packages for default config compatibility", :green
|
|
24
|
+
elsif ENV["JAVASCRIPT_TRANSPILER"]
|
|
25
|
+
@transpiler_to_install = ENV["JAVASCRIPT_TRANSPILER"]
|
|
26
|
+
say "📦 Installing #{@transpiler_to_install} packages", :blue
|
|
27
|
+
else
|
|
28
|
+
# Default to swc (matches the default in shakapacker.yml)
|
|
29
|
+
@transpiler_to_install = "swc"
|
|
30
|
+
say "✨ Installing SWC packages (20x faster than Babel)", :green
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Copy config file
|
|
34
|
+
copy_file "#{install_dir}/config/shakapacker.yml", "config/shakapacker.yml", force_option
|
|
35
|
+
|
|
36
|
+
# Update config if USE_BABEL_PACKAGES is set to ensure babel is used at runtime
|
|
37
|
+
if @transpiler_to_install == "babel" && !ENV["JAVASCRIPT_TRANSPILER"]
|
|
38
|
+
# When USE_BABEL_PACKAGES is set, update the config to use babel
|
|
39
|
+
gsub_file "config/shakapacker.yml", "javascript_transpiler: 'swc'", "javascript_transpiler: 'babel'"
|
|
40
|
+
say " 📝 Updated config/shakapacker.yml to use Babel transpiler", :green
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Detect TypeScript usage
|
|
44
|
+
# Auto-detect from tsconfig.json or explicit via SHAKAPACKER_USE_TYPESCRIPT env var
|
|
45
|
+
@use_typescript = File.exist?(Rails.root.join("tsconfig.json")) ||
|
|
46
|
+
ENV["SHAKAPACKER_USE_TYPESCRIPT"] == "true"
|
|
47
|
+
assets_bundler = ENV["SHAKAPACKER_ASSETS_BUNDLER"] || "webpack"
|
|
48
|
+
config_extension = @use_typescript ? "ts" : "js"
|
|
49
|
+
|
|
50
|
+
say "Copying #{assets_bundler} core config (#{config_extension.upcase})"
|
|
51
|
+
config_file = "#{assets_bundler}.config.#{config_extension}"
|
|
52
|
+
source_config = "#{install_dir}/config/#{assets_bundler}/#{config_file}"
|
|
53
|
+
dest_config = "config/#{assets_bundler}/#{config_file}"
|
|
54
|
+
|
|
55
|
+
empty_directory "config/#{assets_bundler}"
|
|
56
|
+
copy_file source_config, dest_config, force_option
|
|
57
|
+
|
|
58
|
+
if @use_typescript
|
|
59
|
+
say " ✨ Using TypeScript config for enhanced type safety", :green
|
|
60
|
+
end
|
|
15
61
|
|
|
16
62
|
if Dir.exist?(Shakapacker.config.source_path)
|
|
17
63
|
say "The packs app source directory already exists"
|
|
18
64
|
else
|
|
19
65
|
say "Creating packs app source directory"
|
|
20
66
|
empty_directory "app/javascript/packs"
|
|
21
|
-
copy_file "#{
|
|
67
|
+
copy_file "#{install_dir}/application.js", "app/javascript/packs/application.js"
|
|
22
68
|
end
|
|
23
69
|
|
|
24
|
-
apply "#{
|
|
70
|
+
apply "#{install_dir}/binstubs.rb"
|
|
25
71
|
|
|
26
72
|
git_ignore_path = Rails.root.join(".gitignore")
|
|
27
73
|
if File.exist?(git_ignore_path)
|
|
@@ -44,17 +90,8 @@ else
|
|
|
44
90
|
say %( Add <%= javascript_pack_tag "application" %> within the <head> tag in your custom layout.)
|
|
45
91
|
end
|
|
46
92
|
|
|
47
|
-
def package_json
|
|
48
|
-
@package_json ||= PackageJson.new
|
|
49
|
-
end
|
|
50
|
-
|
|
51
93
|
# setup the package manager with default values
|
|
52
|
-
package_json.merge! do |pj|
|
|
53
|
-
babel = pj.fetch("babel", {})
|
|
54
|
-
|
|
55
|
-
babel["presets"] ||= []
|
|
56
|
-
babel["presets"].push("./node_modules/shakapacker/package/babel/preset.js")
|
|
57
|
-
|
|
94
|
+
@package_json.merge! do |pj|
|
|
58
95
|
package_manager = pj.fetch("packageManager") do
|
|
59
96
|
"#{Shakapacker::Utils::Manager.guess_binary}@#{Shakapacker::Utils::Manager.guess_version}"
|
|
60
97
|
end
|
|
@@ -63,7 +100,6 @@ package_json.merge! do |pj|
|
|
|
63
100
|
"name" => "app",
|
|
64
101
|
"private" => true,
|
|
65
102
|
"version" => "0.1.0",
|
|
66
|
-
"babel" => babel,
|
|
67
103
|
"browserslist" => [
|
|
68
104
|
"defaults"
|
|
69
105
|
],
|
|
@@ -75,7 +111,7 @@ Shakapacker::Utils::Manager.error_unless_package_manager_is_obvious!
|
|
|
75
111
|
|
|
76
112
|
# Ensure there is `system!("bin/yarn")` command in `./bin/setup` file
|
|
77
113
|
if (setup_path = Rails.root.join("bin/setup")).exist?
|
|
78
|
-
native_install_command = package_json.manager.native_install_command.join(" ")
|
|
114
|
+
native_install_command = @package_json.manager.native_install_command.join(" ")
|
|
79
115
|
|
|
80
116
|
say "Run #{native_install_command} during bin/setup"
|
|
81
117
|
|
|
@@ -104,40 +140,126 @@ if (setup_path = Rails.root.join("bin/setup")).exist?
|
|
|
104
140
|
end
|
|
105
141
|
end
|
|
106
142
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
exit 1
|
|
112
|
-
end
|
|
143
|
+
Dir.chdir(Rails.root) do
|
|
144
|
+
# In CI, use the pre-packed tarball if available
|
|
145
|
+
if ENV["SHAKAPACKER_NPM_PACKAGE"]
|
|
146
|
+
package_path = ENV["SHAKAPACKER_NPM_PACKAGE"]
|
|
113
147
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
148
|
+
# Validate package path to prevent directory traversal and invalid file types
|
|
149
|
+
begin
|
|
150
|
+
# Resolve to absolute path
|
|
151
|
+
absolute_path = File.expand_path(package_path)
|
|
117
152
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
153
|
+
# Reject paths containing directory traversal
|
|
154
|
+
if package_path.include?("..") || absolute_path.include?("..")
|
|
155
|
+
say "❌ Security Error: Package path contains directory traversal: #{package_path}", :red
|
|
156
|
+
exit 1
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Ensure filename ends with .tgz or .tar.gz
|
|
160
|
+
unless absolute_path.end_with?(".tgz", ".tar.gz")
|
|
161
|
+
say "❌ Security Error: Package must be a .tgz or .tar.gz file: #{package_path}", :red
|
|
162
|
+
exit 1
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Check existence only after validation
|
|
166
|
+
if File.exist?(absolute_path)
|
|
167
|
+
say "📦 Installing shakapacker from local package: #{absolute_path}", :cyan
|
|
168
|
+
begin
|
|
169
|
+
@package_json.manager.add!([absolute_path], type: :production)
|
|
170
|
+
rescue PackageJson::Error
|
|
171
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
172
|
+
exit 1
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
say "⚠️ SHAKAPACKER_NPM_PACKAGE set but file not found: #{absolute_path}", :yellow
|
|
176
|
+
say "Falling back to npm registry...", :yellow
|
|
177
|
+
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
|
178
|
+
begin
|
|
179
|
+
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
|
180
|
+
rescue PackageJson::Error
|
|
181
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
182
|
+
exit 1
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
rescue => e
|
|
186
|
+
say "❌ Error validating package path: #{e.message}", :red
|
|
187
|
+
exit 1
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
|
191
|
+
say "Installing shakapacker@#{npm_version}"
|
|
192
|
+
begin
|
|
193
|
+
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
|
194
|
+
rescue PackageJson::Error
|
|
195
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
196
|
+
exit 1
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
@package_json.merge! do |pj|
|
|
201
|
+
if pj["dependencies"] && pj["dependencies"]["shakapacker"]
|
|
202
|
+
{
|
|
203
|
+
"dependencies" => pj["dependencies"].merge({
|
|
204
|
+
# TODO: workaround for test suite - long-run need to actually account for diff pkg manager behaviour
|
|
205
|
+
"shakapacker" => pj["dependencies"]["shakapacker"].delete_prefix("^")
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
else
|
|
209
|
+
pj
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Inline fetch_peer_dependencies and fetch_common_dependencies
|
|
214
|
+
peers = PackageJson.read(install_dir).fetch(ENV["SHAKAPACKER_ASSETS_BUNDLER"] || "webpack")
|
|
215
|
+
common_deps = ENV["SKIP_COMMON_LOADERS"] ? {} : PackageJson.read(install_dir).fetch("common")
|
|
216
|
+
peers = peers.merge(common_deps)
|
|
217
|
+
|
|
218
|
+
# Add transpiler-specific dependencies based on detected/configured transpiler
|
|
219
|
+
# Inline the logic here since methods can't be called before they're defined in Rails templates
|
|
220
|
+
|
|
221
|
+
# Install transpiler-specific dependencies
|
|
222
|
+
# When USE_BABEL_PACKAGES is set, install both babel AND swc
|
|
223
|
+
# This ensures backward compatibility while supporting the default config
|
|
224
|
+
if @transpiler_to_install == "babel"
|
|
225
|
+
# Install babel packages
|
|
226
|
+
babel_deps = PackageJson.read(install_dir).fetch("babel")
|
|
227
|
+
peers = peers.merge(babel_deps)
|
|
228
|
+
|
|
229
|
+
# Also install SWC since that's what the default config uses
|
|
230
|
+
# This ensures the runtime works regardless of config
|
|
231
|
+
swc_deps = PackageJson.read(install_dir).fetch("swc")
|
|
232
|
+
peers = peers.merge(swc_deps)
|
|
233
|
+
|
|
234
|
+
say "ℹ️ Installing both Babel and SWC packages for compatibility:", :blue
|
|
235
|
+
say " - Babel packages are installed as requested via USE_BABEL_PACKAGES", :blue
|
|
236
|
+
say " - SWC packages are also installed to ensure the default config works", :blue
|
|
237
|
+
say " - Your actual transpiler will be determined by your shakapacker.yml configuration", :blue
|
|
238
|
+
elsif @transpiler_to_install == "swc"
|
|
239
|
+
swc_deps = PackageJson.read(install_dir).fetch("swc")
|
|
240
|
+
peers = peers.merge(swc_deps)
|
|
241
|
+
elsif @transpiler_to_install == "esbuild"
|
|
242
|
+
esbuild_deps = PackageJson.read(install_dir).fetch("esbuild")
|
|
243
|
+
peers = peers.merge(esbuild_deps)
|
|
130
244
|
end
|
|
131
245
|
|
|
132
|
-
peers = fetch_peer_dependencies
|
|
133
246
|
dev_dependency_packages = ["webpack-dev-server"]
|
|
134
247
|
|
|
135
248
|
dependencies_to_add = []
|
|
136
249
|
dev_dependencies_to_add = []
|
|
137
250
|
|
|
138
251
|
peers.each do |(package, version)|
|
|
139
|
-
|
|
140
|
-
|
|
252
|
+
# Handle versions like "^1.3.0" or ">= 4 || 5"
|
|
253
|
+
if version.start_with?("^", "~") || version.match?(/^\d+\.\d+/)
|
|
254
|
+
# Already has proper version format, use as-is
|
|
255
|
+
entry = "#{package}@#{version}"
|
|
256
|
+
else
|
|
257
|
+
# Extract major version from complex version strings like ">= 4 || 5"
|
|
258
|
+
# Fallback to "latest" if no version number found
|
|
259
|
+
version_match = version.split("||").last.match(/(\d+)/)
|
|
260
|
+
major_version = version_match ? version_match[1] : "latest"
|
|
261
|
+
entry = "#{package}@#{major_version}"
|
|
262
|
+
end
|
|
141
263
|
|
|
142
264
|
if dev_dependency_packages.include? package
|
|
143
265
|
dev_dependencies_to_add << entry
|
|
@@ -147,8 +269,36 @@ Dir.chdir(Rails.root) do
|
|
|
147
269
|
end
|
|
148
270
|
|
|
149
271
|
say "Adding shakapacker peerDependencies"
|
|
150
|
-
|
|
272
|
+
begin
|
|
273
|
+
@package_json.manager.add!(dependencies_to_add, type: :production)
|
|
274
|
+
rescue PackageJson::Error
|
|
275
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
276
|
+
exit 1
|
|
277
|
+
end
|
|
151
278
|
|
|
152
279
|
say "Installing webpack-dev-server for live reloading as a development dependency"
|
|
153
|
-
|
|
280
|
+
begin
|
|
281
|
+
@package_json.manager.add!(dev_dependencies_to_add, type: :dev)
|
|
282
|
+
rescue PackageJson::Error
|
|
283
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
284
|
+
exit 1
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Configure babel preset in package.json if babel is being used
|
|
288
|
+
if @transpiler_to_install == "babel"
|
|
289
|
+
@package_json.merge! do |pj|
|
|
290
|
+
babel = pj.fetch("babel", {})
|
|
291
|
+
babel["presets"] ||= []
|
|
292
|
+
unless babel["presets"].include?("./node_modules/shakapacker/package/babel/preset.js")
|
|
293
|
+
babel["presets"].push("./node_modules/shakapacker/package/babel/preset.js")
|
|
294
|
+
end
|
|
295
|
+
{ "babel" => babel }
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Helper methods defined at the end (Rails template convention)
|
|
301
|
+
|
|
302
|
+
def package_json
|
|
303
|
+
@package_json
|
|
154
304
|
end
|
|
@@ -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
|
|
@@ -104,6 +213,38 @@ class Shakapacker::Configuration
|
|
|
104
213
|
end
|
|
105
214
|
|
|
106
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
|
+
|
|
107
248
|
def data
|
|
108
249
|
@data ||= load
|
|
109
250
|
end
|
|
@@ -7,6 +7,10 @@ require_relative "runner"
|
|
|
7
7
|
|
|
8
8
|
module Shakapacker
|
|
9
9
|
class DevServerRunner < Shakapacker::Runner
|
|
10
|
+
def self.run(argv)
|
|
11
|
+
new(argv).run
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def run
|
|
11
15
|
load_config
|
|
12
16
|
detect_unsupported_switches!
|
|
@@ -75,12 +79,19 @@ module Shakapacker
|
|
|
75
79
|
env["NODE_OPTIONS"] = "#{env["NODE_OPTIONS"]} --inspect-brk --trace-warnings"
|
|
76
80
|
end
|
|
77
81
|
|
|
82
|
+
# Add config file
|
|
78
83
|
cmd += ["--config", @webpack_config]
|
|
79
|
-
cmd += ["--progress", "--color"] if @pretty
|
|
80
84
|
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
84
95
|
|
|
85
96
|
cmd += @argv
|
|
86
97
|
|
|
@@ -90,7 +101,16 @@ module Shakapacker
|
|
|
90
101
|
end
|
|
91
102
|
|
|
92
103
|
def build_cmd
|
|
93
|
-
|
|
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?
|
|
94
114
|
end
|
|
95
115
|
end
|
|
96
116
|
end
|