shakapacker 9.2.0 → 9.3.0.beta.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/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
- data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
- data/.github/workflows/claude-code-review.yml +4 -5
- data/.github/workflows/claude.yml +1 -2
- data/.github/workflows/dummy.yml +4 -4
- data/.github/workflows/generator.yml +9 -9
- data/.github/workflows/node.yml +11 -2
- data/.github/workflows/ruby.yml +16 -16
- data/.github/workflows/test-bundlers.yml +9 -9
- data/.gitignore +4 -0
- data/CHANGELOG.md +19 -4
- data/CLAUDE.md +6 -1
- data/CONTRIBUTING.md +0 -1
- data/Gemfile.lock +1 -1
- data/README.md +14 -14
- data/TODO.md +10 -2
- data/TODO_v9.md +13 -3
- data/bin/export-bundler-config +1 -1
- data/conductor-setup.sh +1 -1
- data/conductor.json +1 -1
- data/docs/cdn_setup.md +13 -8
- data/docs/common-upgrades.md +2 -1
- data/docs/configuration.md +630 -0
- data/docs/css-modules-export-mode.md +120 -100
- data/docs/customizing_babel_config.md +16 -16
- data/docs/deployment.md +18 -0
- data/docs/developing_shakapacker.md +6 -0
- data/docs/optional-peer-dependencies.md +9 -4
- data/docs/peer-dependencies.md +17 -6
- data/docs/precompile_hook.md +342 -0
- data/docs/react.md +57 -47
- data/docs/releasing.md +0 -2
- data/docs/rspack.md +25 -21
- data/docs/rspack_migration_guide.md +335 -8
- data/docs/sprockets.md +1 -0
- data/docs/style_loader_vs_mini_css.md +12 -12
- data/docs/subresource_integrity.md +13 -7
- data/docs/transpiler-performance.md +40 -19
- data/docs/troubleshooting.md +0 -2
- data/docs/typescript-migration.md +48 -39
- data/docs/typescript.md +12 -8
- data/docs/using_esbuild_loader.md +10 -10
- data/docs/v6_upgrade.md +33 -20
- data/docs/v7_upgrade.md +8 -6
- data/docs/v8_upgrade.md +13 -12
- data/docs/v9_upgrade.md +2 -1
- data/eslint.config.fast.js +134 -0
- data/eslint.config.js +140 -0
- data/knip.ts +54 -0
- data/lib/install/bin/export-bundler-config +1 -1
- data/lib/install/config/shakapacker.yml +16 -5
- data/lib/shakapacker/compiler.rb +80 -0
- data/lib/shakapacker/configuration.rb +33 -5
- data/lib/shakapacker/dev_server_runner.rb +140 -1
- data/lib/shakapacker/doctor.rb +294 -65
- data/lib/shakapacker/instance.rb +8 -3
- data/lib/shakapacker/runner.rb +244 -8
- data/lib/shakapacker/version.rb +1 -1
- data/lib/tasks/shakapacker/doctor.rake +42 -2
- data/package/babel/preset.ts +7 -4
- data/package/config.ts +42 -30
- data/package/configExporter/cli.ts +799 -208
- data/package/configExporter/configFile.ts +520 -0
- data/package/configExporter/fileWriter.ts +12 -8
- data/package/configExporter/index.ts +9 -1
- data/package/configExporter/types.ts +36 -2
- data/package/configExporter/yamlSerializer.ts +22 -8
- data/package/dev_server.ts +1 -1
- data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +11 -5
- data/package/environments/base.ts +18 -13
- data/package/environments/development.ts +1 -1
- data/package/environments/production.ts +4 -1
- data/package/index.d.ts +50 -3
- data/package/index.d.ts.template +50 -0
- data/package/index.ts +7 -7
- data/package/loaders.d.ts +2 -2
- data/package/optimization/rspack.ts +1 -1
- data/package/plugins/rspack.ts +15 -4
- data/package/plugins/webpack.ts +7 -3
- data/package/rspack/index.ts +10 -2
- data/package/rules/raw.ts +3 -2
- data/package/rules/sass.ts +1 -1
- data/package/types/README.md +15 -13
- data/package/types/index.ts +5 -5
- data/package/types.ts +0 -1
- data/package/utils/defaultConfigPath.ts +4 -1
- data/package/utils/errorCodes.ts +129 -100
- data/package/utils/errorHelpers.ts +34 -29
- data/package/utils/getStyleRule.ts +5 -2
- data/package/utils/helpers.ts +21 -11
- data/package/utils/pathValidation.ts +43 -35
- data/package/utils/requireOrError.ts +1 -1
- data/package/utils/snakeToCamelCase.ts +1 -1
- data/package/utils/typeGuards.ts +132 -83
- data/package/utils/validateDependencies.ts +1 -1
- data/package/webpack-types.d.ts +3 -3
- data/package/webpackDevServerConfig.ts +22 -10
- data/package-lock.json +2 -2
- data/package.json +36 -28
- data/scripts/type-check-no-emit.js +1 -1
- data/test/configExporter/configFile.test.js +392 -0
- data/test/configExporter/integration.test.js +275 -0
- data/test/helpers.js +1 -1
- data/test/package/configExporter.test.js +154 -0
- data/test/package/helpers.test.js +2 -2
- data/test/package/rules/sass-version-parsing.test.js +71 -0
- data/test/package/rules/sass.test.js +2 -4
- data/test/package/rules/sass1.test.js +1 -3
- data/test/package/rules/sass16.test.js +23 -0
- data/tools/README.md +15 -5
- data/tsconfig.eslint.json +2 -9
- data/yarn.lock +1894 -1492
- metadata +19 -3
- data/.eslintignore +0 -5
data/lib/shakapacker/doctor.rb
CHANGED
@@ -5,17 +5,28 @@ require "semantic_range"
|
|
5
5
|
|
6
6
|
module Shakapacker
|
7
7
|
class Doctor
|
8
|
-
attr_reader :config, :root_path, :issues, :warnings, :info
|
8
|
+
attr_reader :config, :root_path, :issues, :warnings, :info, :options
|
9
9
|
|
10
|
-
|
10
|
+
# Warning categories for better organization
|
11
|
+
CATEGORY_ACTION_REQUIRED = :action_required
|
12
|
+
CATEGORY_RECOMMENDED = :recommended
|
13
|
+
CATEGORY_INFO = :info
|
14
|
+
|
15
|
+
def initialize(config = nil, root_path = nil, options = {})
|
11
16
|
@config = config || Shakapacker.config
|
12
17
|
@root_path = root_path || (defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd))
|
13
18
|
@issues = []
|
14
|
-
@warnings = []
|
19
|
+
@warnings = [] # Now stores hashes: { category: :symbol, message: "..." }
|
15
20
|
@info = []
|
21
|
+
@options = options
|
16
22
|
end
|
17
23
|
|
18
24
|
def run
|
25
|
+
if options[:help]
|
26
|
+
print_help
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
19
30
|
perform_checks
|
20
31
|
report_results
|
21
32
|
end
|
@@ -26,6 +37,49 @@ module Shakapacker
|
|
26
37
|
|
27
38
|
private
|
28
39
|
|
40
|
+
def add_warning(message, category = CATEGORY_RECOMMENDED)
|
41
|
+
@warnings << { category: category, message: message }
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_action_required(message)
|
45
|
+
add_warning(message, CATEGORY_ACTION_REQUIRED)
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_info_warning(message)
|
49
|
+
add_warning(message, CATEGORY_INFO)
|
50
|
+
end
|
51
|
+
|
52
|
+
def print_help
|
53
|
+
puts <<~HELP
|
54
|
+
Shakapacker Doctor - Diagnostic tool for Shakapacker configuration
|
55
|
+
|
56
|
+
Usage:
|
57
|
+
bin/rails shakapacker:doctor [options]
|
58
|
+
|
59
|
+
Options:
|
60
|
+
--help Show this help message
|
61
|
+
--verbose Show detailed information about all checks
|
62
|
+
|
63
|
+
Description:
|
64
|
+
The doctor command checks for common configuration issues and missing
|
65
|
+
dependencies in your Shakapacker setup, including:
|
66
|
+
|
67
|
+
• Configuration file validity
|
68
|
+
• Entry points and output paths
|
69
|
+
• Node.js and package manager installation
|
70
|
+
• Required npm dependencies
|
71
|
+
• JavaScript transpiler configuration
|
72
|
+
• CSS and CSS Modules setup
|
73
|
+
• Binstubs presence
|
74
|
+
• Version consistency
|
75
|
+
• Legacy file detection
|
76
|
+
|
77
|
+
Exit codes:
|
78
|
+
0 - No issues found
|
79
|
+
1 - Issues detected (see output for details)
|
80
|
+
HELP
|
81
|
+
end
|
82
|
+
|
29
83
|
def perform_checks
|
30
84
|
# Core configuration checks
|
31
85
|
check_config_file
|
@@ -80,7 +134,7 @@ module Shakapacker
|
|
80
134
|
# Check for at least one entry point
|
81
135
|
entry_files = Dir.glob(File.join(source_entry_path, "**/*.{js,jsx,ts,tsx,coffee}"))
|
82
136
|
if entry_files.empty?
|
83
|
-
|
137
|
+
add_warning("No entry point files found in #{source_entry_path}")
|
84
138
|
end
|
85
139
|
end
|
86
140
|
|
@@ -109,7 +163,7 @@ module Shakapacker
|
|
109
163
|
begin
|
110
164
|
manifest_content = JSON.parse(File.read(manifest_path))
|
111
165
|
if manifest_content.empty?
|
112
|
-
|
166
|
+
add_warning("Manifest file is empty - you may need to run 'bin/rails assets:precompile'")
|
113
167
|
end
|
114
168
|
rescue JSON::ParserError
|
115
169
|
@issues << "Manifest file #{manifest_path} contains invalid JSON"
|
@@ -125,13 +179,17 @@ module Shakapacker
|
|
125
179
|
|
126
180
|
def check_deprecated_config
|
127
181
|
config_file = File.read(config.config_path)
|
182
|
+
config_relative_path = config.config_path.relative_path_from(root_path)
|
128
183
|
|
129
184
|
if config_file.include?("webpack_loader:")
|
130
|
-
|
185
|
+
add_action_required("Deprecated config: 'webpack_loader' should be renamed to 'javascript_transpiler' in #{config_relative_path}")
|
131
186
|
end
|
132
187
|
|
133
|
-
|
134
|
-
|
188
|
+
# Check for standalone "bundler:" but not "assets_bundler:"
|
189
|
+
# Match "bundler:" at start of line or preceded by non-underscore character
|
190
|
+
if config_file.match?(/^\s*bundler:/m) || config_file.match?(/[^_]bundler:/)
|
191
|
+
add_action_required("Deprecated config: 'bundler' should be renamed to 'assets_bundler' in #{config_relative_path}.")
|
192
|
+
add_action_required(" Fix: Open #{config_relative_path} and change 'bundler:' to 'assets_bundler:'.")
|
135
193
|
end
|
136
194
|
rescue => e
|
137
195
|
# Ignore read errors as config file check already handles missing file
|
@@ -146,15 +204,18 @@ module Shakapacker
|
|
146
204
|
package_json.dig("devDependencies", "shakapacker")
|
147
205
|
|
148
206
|
if npm_version
|
207
|
+
# Skip version check for github/file references
|
208
|
+
return if npm_version.start_with?("github:", "git+", "file:", "link:", "./", "../", "/")
|
209
|
+
|
149
210
|
gem_version = Shakapacker::VERSION rescue nil
|
150
211
|
if gem_version && !versions_compatible?(gem_version, npm_version)
|
151
|
-
|
212
|
+
add_info_warning("Version mismatch: shakapacker gem is #{gem_version} but npm package is #{npm_version}")
|
152
213
|
end
|
153
214
|
end
|
154
215
|
|
155
|
-
# Check if ensure_consistent_versioning is enabled
|
216
|
+
# Check if ensure_consistent_versioning is enabled
|
156
217
|
if config.ensure_consistent_versioning?
|
157
|
-
@info << "Version consistency checking
|
218
|
+
@info << "Version consistency checking: enabled (ensures shakapacker gem and npm package versions match at runtime)"
|
158
219
|
end
|
159
220
|
end
|
160
221
|
|
@@ -163,7 +224,7 @@ module Shakapacker
|
|
163
224
|
node_env = ENV["NODE_ENV"]
|
164
225
|
|
165
226
|
if rails_env && node_env && rails_env != node_env
|
166
|
-
|
227
|
+
add_warning("Environment mismatch: Rails.env is '#{rails_env}' but NODE_ENV is '#{node_env}'")
|
167
228
|
end
|
168
229
|
|
169
230
|
# Check SHAKAPACKER_ASSET_HOST for production
|
@@ -206,7 +267,7 @@ module Shakapacker
|
|
206
267
|
|
207
268
|
# Check for conflicting installations
|
208
269
|
if package_installed?("webpack") && package_installed?("@rspack/core")
|
209
|
-
|
270
|
+
add_warning("Both webpack and rspack are installed - ensure assets_bundler is set correctly")
|
210
271
|
end
|
211
272
|
end
|
212
273
|
|
@@ -242,7 +303,7 @@ module Shakapacker
|
|
242
303
|
|
243
304
|
# Check for case sensitivity issues
|
244
305
|
if File.exist?(root_path.join("App")) || File.exist?(root_path.join("APP"))
|
245
|
-
|
306
|
+
add_warning("Potential case sensitivity issue detected on Windows filesystem")
|
246
307
|
end
|
247
308
|
end
|
248
309
|
end
|
@@ -254,7 +315,8 @@ module Shakapacker
|
|
254
315
|
# Check if manifest is recent (within last 24 hours)
|
255
316
|
manifest_age_hours = (Time.now - File.mtime(manifest_path)) / 3600
|
256
317
|
|
257
|
-
if manifest_age_hours > 24
|
318
|
+
if manifest_age_hours > 24 && options[:verbose]
|
319
|
+
# Only show age in verbose mode - it's not actionable information
|
258
320
|
@info << "Assets were last compiled #{manifest_age_hours.round} hours ago. Consider recompiling if you've made changes."
|
259
321
|
end
|
260
322
|
|
@@ -263,15 +325,16 @@ module Shakapacker
|
|
263
325
|
if source_files.any?
|
264
326
|
newest_source = source_files.map { |f| File.mtime(f) }.max
|
265
327
|
if newest_source > File.mtime(manifest_path)
|
266
|
-
|
328
|
+
add_warning("Source files have been modified after last asset compilation. Run 'bin/rails assets:precompile'")
|
267
329
|
end
|
268
330
|
end
|
269
331
|
else
|
270
332
|
rails_env = defined?(Rails) ? Rails.env : ENV["RAILS_ENV"]
|
271
333
|
if rails_env == "production"
|
272
|
-
@issues << "No compiled assets found (manifest.json missing). Run 'rails assets:precompile'"
|
273
|
-
|
274
|
-
|
334
|
+
@issues << "No compiled assets found (manifest.json missing). Run 'bin/rails assets:precompile'"
|
335
|
+
elsif options[:verbose]
|
336
|
+
# Only show in verbose mode for non-production environments
|
337
|
+
@info << "Assets not yet compiled. Run 'bin/rails assets:precompile' or start the dev server"
|
275
338
|
end
|
276
339
|
end
|
277
340
|
end
|
@@ -287,7 +350,7 @@ module Shakapacker
|
|
287
350
|
legacy_files.each do |file|
|
288
351
|
file_path = root_path.join(file)
|
289
352
|
if file_path.exist?
|
290
|
-
|
353
|
+
add_warning("Legacy webpacker file found: #{file} - consider removing after migration")
|
291
354
|
end
|
292
355
|
end
|
293
356
|
end
|
@@ -302,7 +365,7 @@ module Shakapacker
|
|
302
365
|
if version_match
|
303
366
|
major = version_match[1].to_i
|
304
367
|
if major < 14
|
305
|
-
|
368
|
+
add_warning("Node.js version #{node_version} is outdated. Recommend upgrading to v14 or higher")
|
306
369
|
end
|
307
370
|
end
|
308
371
|
else
|
@@ -313,7 +376,7 @@ module Shakapacker
|
|
313
376
|
rescue Errno::EACCES
|
314
377
|
@issues << "Permission denied when checking Node.js version"
|
315
378
|
rescue StandardError => e
|
316
|
-
|
379
|
+
add_warning("Unable to check Node.js version: #{e.message}")
|
317
380
|
end
|
318
381
|
|
319
382
|
def check_package_manager
|
@@ -323,14 +386,23 @@ module Shakapacker
|
|
323
386
|
end
|
324
387
|
|
325
388
|
def check_binstub
|
326
|
-
|
327
|
-
|
328
|
-
|
389
|
+
missing_binstubs = []
|
390
|
+
|
391
|
+
expected_binstubs = {
|
392
|
+
"bin/shakapacker" => "Main Shakapacker binstub",
|
393
|
+
"bin/shakapacker-dev-server" => "Development server binstub",
|
394
|
+
"bin/export-bundler-config" => "Config export binstub"
|
395
|
+
}
|
396
|
+
|
397
|
+
expected_binstubs.each do |path, description|
|
398
|
+
unless root_path.join(path).exist?
|
399
|
+
missing_binstubs << "#{path} (#{description})"
|
400
|
+
end
|
329
401
|
end
|
330
402
|
|
331
|
-
|
332
|
-
|
333
|
-
|
403
|
+
unless missing_binstubs.empty?
|
404
|
+
add_action_required("Missing binstubs: #{missing_binstubs.join(', ')}.")
|
405
|
+
add_action_required(" Fix: Run 'bin/rails shakapacker:binstubs' to create them.")
|
334
406
|
end
|
335
407
|
end
|
336
408
|
|
@@ -394,7 +466,16 @@ module Shakapacker
|
|
394
466
|
# Rspack has built-in SWC support
|
395
467
|
@info << "Rspack has built-in SWC support - no additional loaders needed"
|
396
468
|
if package_installed?("swc-loader")
|
397
|
-
|
469
|
+
package_manager = detect_package_manager
|
470
|
+
remove_cmd = case package_manager
|
471
|
+
when "yarn" then "yarn remove swc-loader"
|
472
|
+
when "npm" then "npm uninstall swc-loader"
|
473
|
+
when "pnpm" then "pnpm remove swc-loader"
|
474
|
+
when "bun" then "bun remove swc-loader"
|
475
|
+
else "npm uninstall swc-loader"
|
476
|
+
end
|
477
|
+
add_warning("swc-loader is not needed with Rspack (SWC is built-in). Rspack includes SWC transpilation natively, so this package is redundant.")
|
478
|
+
add_warning(" Fix: Remove it with: #{remove_cmd}.")
|
398
479
|
end
|
399
480
|
end
|
400
481
|
end
|
@@ -418,26 +499,32 @@ module Shakapacker
|
|
418
499
|
]
|
419
500
|
|
420
501
|
babel_config_exists = babel_configs.any?(&:exist?)
|
502
|
+
babel_in_package_json = false
|
421
503
|
|
422
504
|
# Check if package.json has babel config
|
423
505
|
if package_json_exists?
|
424
506
|
package_json = read_package_json
|
425
|
-
|
507
|
+
babel_in_package_json = package_json.key?("babel")
|
508
|
+
babel_config_exists ||= babel_in_package_json
|
426
509
|
end
|
427
510
|
|
428
511
|
transpiler = config.javascript_transpiler
|
429
512
|
|
430
513
|
if babel_config_exists && transpiler != "babel"
|
431
|
-
|
514
|
+
babel_files = babel_configs.select(&:exist?).map { |f| f.relative_path_from(root_path) }
|
515
|
+
babel_files << "package.json" if babel_in_package_json
|
516
|
+
babel_files_str = babel_files.join(", ")
|
517
|
+
add_warning("Babel configuration files found (#{babel_files_str}) but javascript_transpiler is '#{transpiler}'. These Babel configs are ignored by Shakapacker (though they may still be used by ESLint or other tools).")
|
518
|
+
add_warning(" Fix: Remove Babel config files if not needed, or set javascript_transpiler: 'babel' in shakapacker.yml to use Babel for transpilation.")
|
432
519
|
end
|
433
520
|
|
434
521
|
# Check for redundant dependencies
|
435
522
|
if transpiler == "swc" && package_installed?("babel-loader")
|
436
|
-
|
523
|
+
add_warning("Both SWC and Babel dependencies are installed. Consider removing Babel dependencies to reduce node_modules size")
|
437
524
|
end
|
438
525
|
|
439
526
|
if transpiler == "esbuild" && package_installed?("babel-loader")
|
440
|
-
|
527
|
+
add_warning("Both esbuild and Babel dependencies are installed. Consider removing Babel dependencies to reduce node_modules size")
|
441
528
|
end
|
442
529
|
|
443
530
|
# Check for SWC configuration conflicts
|
@@ -451,10 +538,10 @@ module Shakapacker
|
|
451
538
|
swc_config_path = root_path.join("config/swc.config.js")
|
452
539
|
|
453
540
|
if swcrc_path.exist?
|
454
|
-
|
541
|
+
add_warning("SWC configuration: .swcrc file detected. This file completely overrides Shakapacker's default SWC settings and may cause build failures. " \
|
455
542
|
"Please migrate to config/swc.config.js which properly merges with Shakapacker defaults. " \
|
456
543
|
"To migrate: Move your custom settings from .swcrc to config/swc.config.js (see docs for format). " \
|
457
|
-
"See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md"
|
544
|
+
"See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md")
|
458
545
|
end
|
459
546
|
|
460
547
|
if swc_config_path.exist?
|
@@ -468,18 +555,18 @@ module Shakapacker
|
|
468
555
|
|
469
556
|
# Check for loose: true (deprecated default)
|
470
557
|
if config_content.match?(/loose\s*:\s*true/)
|
471
|
-
|
558
|
+
add_warning("SWC configuration: 'loose: true' detected in config/swc.config.js. " \
|
472
559
|
"This can cause silent failures with Stimulus controllers and incorrect spread operator behavior. " \
|
473
560
|
"Consider removing this setting to use Shakapacker's default 'loose: false' (spec-compliant). " \
|
474
|
-
"See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md#using-swc-with-stimulus"
|
561
|
+
"See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md#using-swc-with-stimulus")
|
475
562
|
end
|
476
563
|
|
477
564
|
# Check for missing keepClassNames with Stimulus
|
478
565
|
if stimulus_likely_used? && !config_content.match?(/keepClassNames\s*:\s*true/)
|
479
|
-
|
566
|
+
add_warning("SWC configuration: Stimulus appears to be in use, but 'keepClassNames: true' is not set in config/swc.config.js. " \
|
480
567
|
"Without this setting, Stimulus controllers will fail silently. " \
|
481
568
|
"Add 'keepClassNames: true' to jsc config. " \
|
482
|
-
"See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md#using-swc-with-stimulus"
|
569
|
+
"See: https://github.com/shakacode/shakapacker/blob/main/docs/using_swc_loader.md#using-swc-with-stimulus")
|
483
570
|
elsif config_content.match?(/keepClassNames\s*:\s*true/)
|
484
571
|
@info << "SWC configuration: 'keepClassNames: true' is set (good for Stimulus compatibility)"
|
485
572
|
end
|
@@ -493,7 +580,7 @@ module Shakapacker
|
|
493
580
|
end
|
494
581
|
rescue => e
|
495
582
|
# Don't fail doctor if SWC config check has issues
|
496
|
-
|
583
|
+
add_warning("Unable to validate SWC configuration: #{e.message}")
|
497
584
|
end
|
498
585
|
|
499
586
|
def stimulus_likely_used?
|
@@ -543,19 +630,13 @@ module Shakapacker
|
|
543
630
|
@issues << " Using exportLocalsConvention: 'camelCase' with namedExport: true will cause build errors"
|
544
631
|
@issues << " Change to 'camelCaseOnly' or 'dashesOnly'. See docs/v9_upgrade.md for details"
|
545
632
|
end
|
546
|
-
|
547
|
-
# Warn if CSS modules are used but no configuration is found
|
548
|
-
if !config_content.match(/namedExport/) && !config_content.match(/exportLocalsConvention/)
|
549
|
-
@info << "CSS module files found but no explicit CSS modules configuration detected"
|
550
|
-
@info << " v9 defaults: namedExport: true, exportLocalsConvention: 'camelCaseOnly'"
|
551
|
-
end
|
552
633
|
end
|
553
634
|
|
554
635
|
# Check for common v8 to v9 migration issues
|
555
636
|
check_css_modules_import_patterns
|
556
637
|
rescue => e
|
557
638
|
# Don't fail doctor if CSS modules check has issues
|
558
|
-
|
639
|
+
add_warning("Unable to validate CSS modules configuration: #{e.message}")
|
559
640
|
end
|
560
641
|
|
561
642
|
def check_css_modules_import_patterns
|
@@ -572,10 +653,10 @@ module Shakapacker
|
|
572
653
|
|
573
654
|
# Check for v8 default import pattern with .module.css
|
574
655
|
if v8_pattern.match?(content)
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
656
|
+
add_warning("Potential v8-style CSS module imports detected (using default import)")
|
657
|
+
add_warning(" v9 uses named exports. Update to: import { className } from './styles.module.css'")
|
658
|
+
add_warning(" Or use: import * as styles from './styles.module.css' (TypeScript)")
|
659
|
+
add_warning(" See docs/v9_upgrade.md for migration guide")
|
579
660
|
break # Stop after finding first occurrence
|
580
661
|
end
|
581
662
|
end
|
@@ -643,7 +724,7 @@ module Shakapacker
|
|
643
724
|
|
644
725
|
def check_optional_dependency(package_name, warnings_array, description)
|
645
726
|
unless package_installed?(package_name)
|
646
|
-
|
727
|
+
add_warning("Optional dependency '#{package_name}' for #{description} is not installed")
|
647
728
|
end
|
648
729
|
end
|
649
730
|
|
@@ -759,25 +840,91 @@ module Shakapacker
|
|
759
840
|
|
760
841
|
private
|
761
842
|
|
843
|
+
def verbose?
|
844
|
+
doctor.options[:verbose]
|
845
|
+
end
|
846
|
+
|
762
847
|
def print_header
|
763
848
|
puts "Running Shakapacker doctor..."
|
764
849
|
puts "=" * 60
|
850
|
+
puts ""
|
851
|
+
if verbose?
|
852
|
+
puts "Mode: Verbose (showing all checks)"
|
853
|
+
puts ""
|
854
|
+
end
|
765
855
|
end
|
766
856
|
|
767
857
|
def print_checks
|
768
858
|
if doctor.config.config_path.exist?
|
769
|
-
|
859
|
+
config_relative_path = doctor.config.config_path.relative_path_from(doctor.root_path)
|
860
|
+
puts "✓ Configuration file found (#{config_relative_path})"
|
861
|
+
if verbose?
|
862
|
+
puts " Assets bundler: #{doctor.config.assets_bundler}"
|
863
|
+
puts " Source path: #{doctor.config.source_path.relative_path_from(doctor.root_path)}"
|
864
|
+
puts " Public output path: #{doctor.config.public_output_path.relative_path_from(doctor.root_path)}"
|
865
|
+
end
|
770
866
|
print_transpiler_status
|
771
867
|
print_bundler_status
|
772
868
|
print_css_status
|
869
|
+
elsif verbose?
|
870
|
+
puts "✗ Configuration file not found"
|
773
871
|
end
|
774
872
|
|
775
873
|
print_node_status
|
776
874
|
print_package_manager_status
|
777
875
|
print_binstub_status
|
876
|
+
print_verbose_checks if verbose?
|
778
877
|
print_info_messages
|
779
878
|
end
|
780
879
|
|
880
|
+
def print_verbose_checks
|
881
|
+
puts "\nVerbose diagnostics:"
|
882
|
+
|
883
|
+
# Show environment info
|
884
|
+
rails_env = defined?(Rails) ? Rails.env : ENV["RAILS_ENV"]
|
885
|
+
node_env = ENV["NODE_ENV"]
|
886
|
+
puts " • Rails environment: #{rails_env || 'not set'}"
|
887
|
+
puts " • Node environment: #{node_env || 'not set'}"
|
888
|
+
|
889
|
+
# Show gem/npm versions
|
890
|
+
if doctor.send(:package_json_exists?)
|
891
|
+
package_json = doctor.send(:read_package_json)
|
892
|
+
npm_version = package_json.dig("dependencies", "shakapacker") ||
|
893
|
+
package_json.dig("devDependencies", "shakapacker")
|
894
|
+
puts " • Shakapacker gem version: #{Shakapacker::VERSION}"
|
895
|
+
puts " • Shakapacker npm version: #{npm_version || 'not installed'}"
|
896
|
+
end
|
897
|
+
|
898
|
+
# Show paths
|
899
|
+
puts " • Root path: #{doctor.root_path}"
|
900
|
+
if doctor.config.config_path.exist?
|
901
|
+
puts " • Cache path: #{doctor.config.cache_path}"
|
902
|
+
puts " • Manifest path: #{doctor.config.manifest_path}"
|
903
|
+
end
|
904
|
+
|
905
|
+
# Show environment-specific shakapacker.yml configuration values
|
906
|
+
if doctor.config.config_path.exist?
|
907
|
+
puts "\nConfiguration values for '#{doctor.config.env}' environment:"
|
908
|
+
config_data = doctor.config.send(:data)
|
909
|
+
if config_data.any?
|
910
|
+
config_data.each do |key, value|
|
911
|
+
# Format the value nicely - truncate long arrays/hashes
|
912
|
+
formatted_value = case value
|
913
|
+
when Array
|
914
|
+
value.length > 3 ? "#{value.first(3).inspect}... (#{value.length} items)" : value.inspect
|
915
|
+
when Hash
|
916
|
+
value.length > 3 ? "{...} (#{value.length} keys)" : value.inspect
|
917
|
+
else
|
918
|
+
value.inspect
|
919
|
+
end
|
920
|
+
puts " • #{key}: #{formatted_value}"
|
921
|
+
end
|
922
|
+
else
|
923
|
+
puts " (using bundled defaults - no environment-specific config found)"
|
924
|
+
end
|
925
|
+
end
|
926
|
+
end
|
927
|
+
|
781
928
|
def print_transpiler_status
|
782
929
|
transpiler = doctor.config.javascript_transpiler
|
783
930
|
return if transpiler.nil? || transpiler == "none"
|
@@ -831,14 +978,20 @@ module Shakapacker
|
|
831
978
|
end
|
832
979
|
|
833
980
|
def print_binstub_status
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
981
|
+
binstubs = [
|
982
|
+
"bin/shakapacker",
|
983
|
+
"bin/shakapacker-dev-server",
|
984
|
+
"bin/export-bundler-config"
|
985
|
+
]
|
986
|
+
|
987
|
+
existing_binstubs = binstubs.select { |b| doctor.root_path.join(b).exist? }
|
988
|
+
|
989
|
+
if existing_binstubs.length == binstubs.length
|
990
|
+
puts "✓ All Shakapacker binstubs found (#{existing_binstubs.join(', ')})"
|
991
|
+
elsif existing_binstubs.any?
|
992
|
+
existing_binstubs.each do |binstub|
|
993
|
+
puts "✓ #{binstub} found"
|
994
|
+
end
|
842
995
|
end
|
843
996
|
end
|
844
997
|
|
@@ -853,13 +1006,14 @@ module Shakapacker
|
|
853
1006
|
|
854
1007
|
def print_summary
|
855
1008
|
puts "=" * 60
|
1009
|
+
puts ""
|
856
1010
|
|
857
1011
|
if doctor.issues.empty? && doctor.warnings.empty?
|
858
1012
|
puts "✅ No issues found! Shakapacker appears to be configured correctly."
|
859
1013
|
else
|
860
1014
|
print_issues if doctor.issues.any?
|
861
1015
|
print_warnings if doctor.warnings.any?
|
862
|
-
print_fix_instructions
|
1016
|
+
print_fix_instructions if has_dependency_issues?
|
863
1017
|
end
|
864
1018
|
end
|
865
1019
|
|
@@ -872,13 +1026,88 @@ module Shakapacker
|
|
872
1026
|
end
|
873
1027
|
|
874
1028
|
def print_warnings
|
875
|
-
|
876
|
-
doctor.warnings.
|
877
|
-
|
1029
|
+
# Count only main items (not sub-items)
|
1030
|
+
main_item_count = doctor.warnings.count { |w| !w[:message].start_with?(" ") }
|
1031
|
+
puts "⚠️ Warnings (#{main_item_count}):"
|
1032
|
+
puts ""
|
1033
|
+
|
1034
|
+
item_number = 0
|
1035
|
+
doctor.warnings.each do |warning|
|
1036
|
+
category_prefix = case warning[:category]
|
1037
|
+
when :action_required then "[REQUIRED]"
|
1038
|
+
when :info then "[INFO]"
|
1039
|
+
when :recommended then "[RECOMMENDED]"
|
1040
|
+
else ""
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
# Sub-items start with whitespace (indented fix instructions)
|
1044
|
+
is_subitem = warning[:message].start_with?(" ")
|
1045
|
+
|
1046
|
+
if is_subitem
|
1047
|
+
# Fix instructions align at column 16 (length of "N. [RECOMMENDED] ")
|
1048
|
+
# This ensures all Fix lines align vertically regardless of category
|
1049
|
+
subitem_prefix = " " * 15
|
1050
|
+
wrapped = wrap_text(warning[:message], 100, subitem_prefix)
|
1051
|
+
puts wrapped
|
1052
|
+
else
|
1053
|
+
item_number += 1
|
1054
|
+
# Format: N. [CATEGORY] Message
|
1055
|
+
prefix = "#{item_number}. #{category_prefix} "
|
1056
|
+
wrapped = wrap_text(warning[:message], 100, prefix)
|
1057
|
+
puts wrapped
|
1058
|
+
end
|
878
1059
|
end
|
879
1060
|
puts ""
|
880
1061
|
end
|
881
1062
|
|
1063
|
+
def wrap_text(text, max_width, prefix)
|
1064
|
+
# Strip leading whitespace from sub-items
|
1065
|
+
text = text.strip
|
1066
|
+
|
1067
|
+
# Calculate available width for text
|
1068
|
+
available_width = max_width - prefix.length
|
1069
|
+
return prefix + text if text.length <= available_width
|
1070
|
+
|
1071
|
+
# Wrap long lines
|
1072
|
+
words = text.split(" ")
|
1073
|
+
lines = []
|
1074
|
+
current_line = []
|
1075
|
+
current_length = 0
|
1076
|
+
|
1077
|
+
words.each do |word|
|
1078
|
+
word_length = word.length + (current_line.empty? ? 0 : 1) # +1 for space
|
1079
|
+
|
1080
|
+
if current_length + word_length <= available_width
|
1081
|
+
current_line << word
|
1082
|
+
current_length += word_length
|
1083
|
+
else
|
1084
|
+
lines << current_line.join(" ") unless current_line.empty?
|
1085
|
+
current_line = [word]
|
1086
|
+
current_length = word.length
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
lines << current_line.join(" ") unless current_line.empty?
|
1090
|
+
|
1091
|
+
# Format output
|
1092
|
+
result = prefix + lines[0]
|
1093
|
+
lines[1..].each do |line|
|
1094
|
+
result += "\n" + (" " * prefix.length) + line
|
1095
|
+
end
|
1096
|
+
result
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
def has_dependency_issues?
|
1100
|
+
# Check if any issues or warnings are about missing npm/package dependencies
|
1101
|
+
# Exclude optional dependencies - only show install instructions for required dependencies
|
1102
|
+
all_messages = doctor.issues + doctor.warnings.map { |w| w[:message] }
|
1103
|
+
all_messages.any? do |msg|
|
1104
|
+
next if msg.include?("Optional")
|
1105
|
+
(msg.include?("Missing") && msg.include?("dependency")) ||
|
1106
|
+
msg.include?("not installed") ||
|
1107
|
+
msg.include?("is not installed")
|
1108
|
+
end
|
1109
|
+
end
|
1110
|
+
|
882
1111
|
def print_fix_instructions
|
883
1112
|
package_manager = doctor.send(:package_manager)
|
884
1113
|
puts "To fix missing dependencies, run:"
|
data/lib/shakapacker/instance.rb
CHANGED
@@ -5,9 +5,14 @@ class Shakapacker::Instance
|
|
5
5
|
|
6
6
|
attr_reader :root_path, :config_path
|
7
7
|
|
8
|
-
def initialize(root_path:
|
9
|
-
|
10
|
-
@
|
8
|
+
def initialize(root_path: nil, config_path: nil)
|
9
|
+
# Use Rails.root if Rails is defined and no root_path is provided
|
10
|
+
@root_path = root_path || (defined?(Rails) && Rails&.root) || Pathname.new(Dir.pwd)
|
11
|
+
|
12
|
+
# Use the determined root_path to construct the default config path
|
13
|
+
default_config_path = @root_path.join("config/shakapacker.yml")
|
14
|
+
|
15
|
+
@config_path = Pathname.new(ENV["SHAKAPACKER_CONFIG"] || config_path || default_config_path)
|
11
16
|
end
|
12
17
|
|
13
18
|
def env
|