Class: Shakapacker::Configuration
- Inherits:
-
Object
- Object
- Shakapacker::Configuration
- Defined in:
- lib/shakapacker/configuration.rb,
sig/shakapacker/configuration.rbs
Overview
Configuration management for Shakapacker
Loads and provides access to settings from config/shakapacker.yml
Constant Summary collapse
- SHAKAPACKER_NODE_FLAGS =
Shared Ruby source of truth for Shakapacker-owned Node flags; Runner aliases this constant.
%w[--debug-shakapacker --trace-deprecation --no-deprecation].freeze
Class Attribute Summary collapse
-
.installing ⇒ Boolean
Flag indicating whether Shakapacker is currently being installed.
Instance Attribute Summary collapse
-
#bundler_override ⇒ String?
readonly
Override for the assets bundler (set via CLI flag).
-
#config_path ⇒ Pathname
readonly
The path to the shakapacker.yml configuration file.
-
#env ⇒ ActiveSupport::StringInquirer
readonly
The current Rails environment.
-
#root_path ⇒ Pathname
readonly
The application root path.
Instance Method Summary collapse
-
#additional_paths ⇒ Array[String]
Returns additional paths to include in compilation.
-
#asset_host ⇒ String?
Returns the asset host URL for serving assets.
-
#assets_bundler ⇒ String
Returns the assets bundler to use (webpack or rspack).
-
#assets_bundler_config_path ⇒ String
Returns the path to the bundler configuration directory.
-
#bundler ⇒ String
Deprecated alias for assets_bundler.
-
#cache_manifest? ⇒ Boolean
Returns whether manifest caching is enabled.
-
#cache_path ⇒ Pathname
Returns the absolute path to the compilation cache directory.
-
#compile? ⇒ Boolean
Returns whether automatic compilation is enabled.
-
#compiler_strategy ⇒ String
Returns the compiler strategy for determining staleness.
-
#css_modules_export_mode ⇒ String
Returns the CSS Modules export mode configuration.
-
#data ⇒ Hash[Symbol, untyped]
Returns the raw configuration data hash.
- #default_javascript_transpiler ⇒ String
- #defaults ⇒ HashWithIndifferentAccess
-
#dev_server ⇒ Hash[String | Symbol, untyped]
Returns the dev server configuration hash.
-
#early_hints ⇒ Boolean
Returns whether HTTP/2 Early Hints are enabled.
-
#ensure_consistent_versioning? ⇒ Boolean
Returns whether consistent versioning check is enabled.
-
#fetch(key) ⇒ Object
Fetches a configuration value.
-
#initialize(root_path:, config_path:, env:, bundler_override: nil) ⇒ Object
constructor
Creates a new configuration instance.
-
#integrity ⇒ bool, Hash[String | Symbol, untyped]
Returns whether subresource integrity (SRI) is enabled.
-
#javascript_transpiler ⇒ String
Returns the JavaScript transpiler to use (babel, swc, or esbuild).
- #load ⇒ Hash[Symbol, untyped]
- #log_fallback(requested_env, fallback_env) ⇒ void
-
#manifest_path ⇒ Pathname
Returns the absolute path to the manifest.json file.
-
#nested_entries? ⇒ Boolean
Returns whether nested entries are enabled.
-
#precompile_hook ⇒ String?
Returns the precompile hook command to run after compilation.
-
#private_output_path ⇒ Pathname?
Returns the absolute path to the private output directory.
-
#public_manifest_path ⇒ Pathname
Alias for manifest_path.
-
#public_output_path ⇒ Pathname
Returns the absolute path to the public output directory.
-
#public_path ⇒ Pathname
Returns the absolute path to the public root directory.
- #relative_path(path) ⇒ String
- #resolve_paths_for_comparison ⇒ [String, String]
-
#rspack? ⇒ Boolean
Returns whether rspack is the configured bundler.
-
#shakapacker_precompile? ⇒ Boolean
Returns whether Shakapacker should precompile assets.
-
#source_entry_path ⇒ Pathname
Returns the absolute path to the source entry directory.
-
#source_path ⇒ Pathname
Returns the absolute path to the source directory.
- #validate_output_paths! ⇒ void
- #validate_transpiler_configuration(transpiler) ⇒ void
- #warn_transpiler_mismatch(configured, message) ⇒ void
-
#webpack? ⇒ Boolean
Returns whether webpack is the configured bundler.
-
#webpack_compile_flags ⇒ Array[String]
Returns extra command-line flags passed to the webpack/rspack compile command.
-
#webpack_compile_output? ⇒ Boolean
Returns whether webpack/rspack compilation output should be shown.
-
#webpack_loader ⇒ String
Deprecated alias for javascript_transpiler.
Constructor Details
#initialize(root_path:, config_path:, env:, bundler_override: nil) ⇒ Object
Creates a new configuration instance
97 98 99 100 101 102 |
# File 'lib/shakapacker/configuration.rb', line 97 def initialize(root_path:, config_path:, env:, bundler_override: nil) @root_path = root_path @env = env @config_path = config_path @bundler_override = bundler_override end |
Class Attribute Details
.installing ⇒ Boolean
Flag indicating whether Shakapacker is currently being installed
70 71 72 |
# File 'lib/shakapacker/configuration.rb', line 70 def installing @installing end |
Instance Attribute Details
#bundler_override ⇒ String? (readonly)
Override for the assets bundler (set via CLI flag)
88 89 90 |
# File 'lib/shakapacker/configuration.rb', line 88 def bundler_override @bundler_override end |
#config_path ⇒ Pathname (readonly)
The path to the shakapacker.yml configuration file
79 80 81 |
# File 'lib/shakapacker/configuration.rb', line 79 def config_path @config_path end |
#env ⇒ ActiveSupport::StringInquirer (readonly)
The current Rails environment
83 84 85 |
# File 'lib/shakapacker/configuration.rb', line 83 def env @env end |
#root_path ⇒ Pathname (readonly)
The application root path
75 76 77 |
# File 'lib/shakapacker/configuration.rb', line 75 def root_path @root_path end |
Instance Method Details
#additional_paths ⇒ Array[String]
Returns additional paths to include in compilation
174 175 176 |
# File 'lib/shakapacker/configuration.rb', line 174 def additional_paths fetch(:additional_paths) end |
#asset_host ⇒ String?
Returns the asset host URL for serving assets
671 672 673 674 675 676 |
# File 'lib/shakapacker/configuration.rb', line 671 def asset_host ENV.fetch( "SHAKAPACKER_ASSET_HOST", fetch(:asset_host) || ActionController::Base.helpers.compute_asset_host ) end |
#assets_bundler ⇒ String
Returns the assets bundler to use (webpack or rspack)
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/shakapacker/configuration.rb', line 313 def assets_bundler # CLI --bundler flag takes highest precedence return @bundler_override if @bundler_override resolved = ENV["SHAKAPACKER_ASSETS_BUNDLER"] || fetch(:assets_bundler) || "webpack" # The legacy 'bundler' key is never read, so warn that its value is ignored if data.has_key?(:bundler) && !data.has_key?(:assets_bundler) legacy_value = data[:bundler].to_s.strip described_value = legacy_value.empty? ? "empty" : legacy_value # An empty SHAKAPACKER_ASSETS_BUNDLER resolves to an empty string; name that # state rather than printing "using ''". Doctor reports it as its own issue. described_resolved = resolved.to_s.empty? ? "no bundler (SHAKAPACKER_ASSETS_BUNDLER is set but empty)" : "'#{resolved}'" $stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is no longer supported and its value (#{described_value}) is IGNORED. Shakapacker is using #{described_resolved}. Set 'assets_bundler' in your shakapacker.yml instead — it was renamed to avoid confusion with Ruby's Bundler gem manager." end resolved end |
#assets_bundler_config_path ⇒ String
Returns the path to the bundler configuration directory
467 468 469 470 471 472 473 |
# File 'lib/shakapacker/configuration.rb', line 467 def assets_bundler_config_path custom_path = fetch(:assets_bundler_config_path) return custom_path if custom_path # Default paths based on bundler type rspack? ? "config/rspack" : "config/webpack" end |
#bundler ⇒ String
Deprecated alias for assets_bundler
337 338 339 |
# File 'lib/shakapacker/configuration.rb', line 337 def bundler assets_bundler end |
#cache_manifest? ⇒ Boolean
Returns whether manifest caching is enabled
248 249 250 |
# File 'lib/shakapacker/configuration.rb', line 248 def cache_manifest? fetch(:cache_manifest) end |
#cache_path ⇒ Pathname
Returns the absolute path to the compilation cache directory
258 259 260 |
# File 'lib/shakapacker/configuration.rb', line 258 def cache_path root_path.join(fetch(:cache_path)) end |
#compile? ⇒ Boolean
Returns whether automatic compilation is enabled
119 120 121 |
# File 'lib/shakapacker/configuration.rb', line 119 def compile? fetch(:compile) end |
#compiler_strategy ⇒ String
Returns the compiler strategy for determining staleness
297 298 299 |
# File 'lib/shakapacker/configuration.rb', line 297 def compiler_strategy fetch(:compiler_strategy) end |
#css_modules_export_mode ⇒ String
Returns the CSS Modules export mode configuration
Controls how CSS Module class names are exported in JavaScript:
- "named" (default): Use named exports with camelCase conversion (v9 behavior)
- "default": Use default export with both original and camelCase names (v8 behavior)
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/shakapacker/configuration.rb', line 444 def css_modules_export_mode @css_modules_export_mode ||= begin mode = fetch(:css_modules_export_mode) || "named" # Validate the configuration value valid_modes = ["named", "default"] unless valid_modes.include?(mode) raise ArgumentError, "Invalid css_modules_export_mode: '#{mode}'. " \ "Valid values are: #{valid_modes.map { |m| "'#{m}'" }.join(', ')}. " \ "See https://github.com/shakacode/shakapacker/blob/main/docs/css-modules-export-mode.md" end mode end end |
#data ⇒ Hash[Symbol, untyped]
Returns the raw configuration data hash
490 491 492 |
# File 'lib/shakapacker/configuration.rb', line 490 def data @data ||= load.freeze end |
#default_javascript_transpiler ⇒ String
506 507 508 509 |
# File 'lib/shakapacker/configuration.rb', line 506 def default_javascript_transpiler # RSpack has built-in SWC support, use it by default rspack? ? "swc" : "babel" end |
#defaults ⇒ HashWithIndifferentAccess
768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
# File 'lib/shakapacker/configuration.rb', line 768 def defaults @defaults ||= begin path = File.("../../install/config/shakapacker.yml", __FILE__) config = begin YAML.load_file(path, aliases: true) rescue ArgumentError YAML.load_file(path) end # Load defaults from bundled shakapacker.yml (always has all environments) # Note: This differs from load() which reads user's config and may be missing environments # Fallback to production ensures staging and other custom envs get production-like defaults HashWithIndifferentAccess.new(config[env] || config["production"]) end end |
#dev_server ⇒ Hash[String | Symbol, untyped]
Returns the dev server configuration hash
109 110 111 |
# File 'lib/shakapacker/configuration.rb', line 109 def dev_server fetch(:dev_server) end |
#early_hints ⇒ Boolean
Returns whether HTTP/2 Early Hints are enabled
694 695 696 |
# File 'lib/shakapacker/configuration.rb', line 694 def early_hints fetch(:early_hints) end |
#ensure_consistent_versioning? ⇒ Boolean
Returns whether consistent versioning check is enabled
138 139 140 |
# File 'lib/shakapacker/configuration.rb', line 138 def ensure_consistent_versioning? fetch(:ensure_consistent_versioning) end |
#fetch(key) ⇒ Object
Fetches a configuration value
657 658 659 |
# File 'lib/shakapacker/configuration.rb', line 657 def fetch(key) data.fetch(key, defaults[key]) end |
#integrity ⇒ bool, Hash[String | Symbol, untyped]
Returns whether subresource integrity (SRI) is enabled
684 685 686 |
# File 'lib/shakapacker/configuration.rb', line 684 def integrity fetch(:integrity) end |
#javascript_transpiler ⇒ String
Returns the JavaScript transpiler to use (babel, swc, or esbuild)
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/shakapacker/configuration.rb', line 383 def javascript_transpiler return @javascript_transpiler if defined?(@javascript_transpiler) @javascript_transpiler = begin javascript_transpiler_configured = config_value_configured?(:javascript_transpiler) webpack_loader_configured = config_value_present?(:webpack_loader) # Show deprecation warning if using old 'webpack_loader' key if webpack_loader_configured && !javascript_transpiler_configured $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." end # Use explicit config if set, otherwise default based on bundler current_javascript_transpiler = if javascript_transpiler_configured fetch(:javascript_transpiler) elsif data.has_key?(:javascript_transpiler) defaults[:javascript_transpiler] else fetch(:javascript_transpiler) end transpiler = if webpack_loader_configured && !javascript_transpiler_configured fetch(:webpack_loader) || default_javascript_transpiler else current_javascript_transpiler || fetch(:webpack_loader) || default_javascript_transpiler end if !javascript_transpiler_configured && !webpack_loader_configured && implicit_swc_to_babel_fallback?(transpiler) $stderr.puts IMPLICIT_SWC_BABEL_FALLBACK_WARNING transpiler = "babel" end # Validate transpiler configuration validate_transpiler_configuration(transpiler) unless self.class.installing transpiler end end |
#load ⇒ Hash[Symbol, untyped]
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 |
# File 'lib/shakapacker/configuration.rb', line 731 def load config = begin YAML.load_file(config_path.to_s, aliases: true) rescue ArgumentError YAML.load_file(config_path.to_s) end # Try to find environment-specific configuration with fallback # Fallback order: requested env → production if config[env] env_config = config[env] elsif config["production"] log_fallback(env, "production") env_config = config["production"] else # No suitable configuration found - rely on bundled defaults log_fallback(env, "none (will use bundled defaults)") env_config = nil end symbolized_config = env_config&.deep_symbolize_keys || {} return symbolized_config rescue Errno::ENOENT => e if self.class.installing {} else raise "Shakapacker configuration file not found #{config_path}. " \ "Please run rails shakapacker:install " \ "Error: #{e.}" end rescue Psych::SyntaxError => e raise "YAML syntax error occurred while parsing #{config_path}. " \ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \ "Error: #{e.}" end |
#log_fallback(requested_env, fallback_env) ⇒ void
This method returns an undefined value.
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 |
# File 'lib/shakapacker/configuration.rb', line 789 def log_fallback(requested_env, fallback_env) = "Shakapacker environment '#{requested_env}' not found in #{config_path}, " \ "falling back to '#{fallback_env}'" # Try to use the logger if available, otherwise fall back to stdout begin if Shakapacker.respond_to?(:logger) && Shakapacker.logger Shakapacker.logger.info() else puts end rescue NameError, NoMethodError # If logger access fails (e.g., circular dependency in standalone runner context), # fall back to stdout so the message still gets displayed puts end end |
#manifest_path ⇒ Pathname
Returns the absolute path to the manifest.json file
193 194 195 196 197 198 199 |
# File 'lib/shakapacker/configuration.rb', line 193 def manifest_path if data.has_key?(:manifest_path) root_path.join(fetch(:manifest_path)) else public_output_path.join("manifest.json") end end |
#nested_entries? ⇒ Boolean
Returns whether nested entries are enabled
129 130 131 |
# File 'lib/shakapacker/configuration.rb', line 129 def nested_entries? fetch(:nested_entries) end |
#precompile_hook ⇒ String?
Returns the precompile hook command to run after compilation
361 362 363 364 365 366 367 368 369 370 |
# File 'lib/shakapacker/configuration.rb', line 361 def precompile_hook hook = fetch(:precompile_hook) return nil if hook.nil? || (hook.is_a?(String) && hook.strip.empty?) unless hook.is_a?(String) raise "Shakapacker configuration error: precompile_hook must be a string, got #{hook.class}" end hook.strip end |
#private_output_path ⇒ Pathname?
Returns the absolute path to the private output directory
225 226 227 228 229 230 |
# File 'lib/shakapacker/configuration.rb', line 225 def private_output_path private_path = fetch(:private_output_path) return nil if private_path.blank? validate_output_paths! root_path.join(private_path) end |
#public_manifest_path ⇒ Pathname
Alias for manifest_path
205 206 207 |
# File 'lib/shakapacker/configuration.rb', line 205 def public_manifest_path manifest_path end |
#public_output_path ⇒ Pathname
Returns the absolute path to the public output directory
238 239 240 |
# File 'lib/shakapacker/configuration.rb', line 238 def public_output_path public_path.join(fetch(:public_output_path)) end |
#public_path ⇒ Pathname
Returns the absolute path to the public root directory
215 216 217 |
# File 'lib/shakapacker/configuration.rb', line 215 def public_path root_path.join(fetch(:public_root_path)) end |
#relative_path(path) ⇒ String
783 784 785 786 787 |
# File 'lib/shakapacker/configuration.rb', line 783 def relative_path(path) return ".#{path}" if path.start_with?("/") path end |
#resolve_paths_for_comparison ⇒ [String, String]
716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
# File 'lib/shakapacker/configuration.rb', line 716 def resolve_paths_for_comparison private_full_path = root_path.join(fetch(:private_output_path)) public_full_path = root_path.join(fetch(:public_root_path), fetch(:public_output_path)) # Create directories if they don't exist (for testing) private_full_path.mkpath unless private_full_path.exist? public_full_path.mkpath unless public_full_path.exist? # Use realpath to resolve symbolic links and relative paths [private_full_path.realpath.to_s, public_full_path.realpath.to_s] rescue Errno::ENOENT # If paths don't exist yet, fall back to cleanpath for comparison [private_full_path.cleanpath.to_s, public_full_path.cleanpath.to_s] end |
#rspack? ⇒ Boolean
Returns whether rspack is the configured bundler
344 345 346 |
# File 'lib/shakapacker/configuration.rb', line 344 def rspack? assets_bundler == "rspack" end |
#shakapacker_precompile? ⇒ Boolean
Returns whether Shakapacker should precompile assets
150 151 152 153 154 155 156 157 |
# File 'lib/shakapacker/configuration.rb', line 150 def shakapacker_precompile? # ENV of false takes precedence return false if %w(no false n f).include?(ENV["SHAKAPACKER_PRECOMPILE"]) return true if %w(yes true y t).include?(ENV["SHAKAPACKER_PRECOMPILE"]) return false unless config_path.exist? fetch(:shakapacker_precompile) end |
#source_entry_path ⇒ Pathname
Returns the absolute path to the source entry directory
183 184 185 |
# File 'lib/shakapacker/configuration.rb', line 183 def source_entry_path source_path.join(relative_path(fetch(:source_entry_path))) end |
#source_path ⇒ Pathname
Returns the absolute path to the source directory
164 165 166 |
# File 'lib/shakapacker/configuration.rb', line 164 def source_path root_path.join(fetch(:source_path)) end |
#validate_output_paths! ⇒ void
This method returns an undefined value.
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 |
# File 'lib/shakapacker/configuration.rb', line 699 def validate_output_paths! # Skip validation if already validated to avoid redundant checks return if @validated_output_paths @validated_output_paths = true # Only validate when both paths are configured return unless fetch(:private_output_path) && fetch(:public_output_path) private_path_str, public_path_str = resolve_paths_for_comparison if private_path_str == public_path_str raise "Shakapacker configuration error: private_output_path and public_output_path must be different. " \ "Both paths resolve to '#{private_path_str}'. " \ "The private_output_path is for server-side bundles (e.g., SSR) that should not be served publicly." end end |
#validate_transpiler_configuration(transpiler) ⇒ void
This method returns an undefined value.
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
# File 'lib/shakapacker/configuration.rb', line 601 def validate_transpiler_configuration(transpiler) return unless ENV["NODE_ENV"] != "test" # Skip validation in test environment # Skip validation if transpiler is set to 'none' (custom webpack config) return if transpiler == "none" all_deps = declared_package_dependencies return if all_deps.empty? # Check for transpiler mismatch has_babel = all_deps.keys.any? { |pkg| pkg.start_with?("@babel/", "babel-") } has_swc = all_deps.keys.any? { |pkg| pkg.include?("swc") } has_esbuild = all_deps.keys.any? { |pkg| pkg.include?("esbuild") } case transpiler when "babel" if !has_babel && has_swc warn_transpiler_mismatch("Babel", "SWC packages found but Babel is configured") end when "swc" if !has_swc && has_babel warn_transpiler_mismatch("SWC", "Babel packages found but SWC is configured") end when "esbuild" if !has_esbuild && (has_babel || has_swc) other = has_babel ? "Babel" : "SWC" warn_transpiler_mismatch("esbuild", "#{other} packages found but esbuild is configured") end end end |
#warn_transpiler_mismatch(configured, message) ⇒ void
This method returns an undefined value.
632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
# File 'lib/shakapacker/configuration.rb', line 632 def warn_transpiler_mismatch(configured, ) $stderr.puts <<~WARNING ⚠️ Transpiler Configuration Mismatch Detected: #{} Configured transpiler: #{configured} #{' '} This might cause unexpected behavior or build failures. #{' '} To fix this: 1. Run 'rails shakapacker:migrate_to_swc' to migrate to SWC (recommended for 20x faster builds) 2. Or install the correct packages for #{configured} 3. Or update your shakapacker.yml to match your installed packages WARNING end |
#webpack? ⇒ Boolean
Returns whether webpack is the configured bundler
351 352 353 |
# File 'lib/shakapacker/configuration.rb', line 351 def webpack? assets_bundler == "webpack" end |
#webpack_compile_flags ⇒ Array[String]
Returns extra command-line flags passed to the webpack/rspack compile command
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/shakapacker/configuration.rb', line 274 def webpack_compile_flags flags = fetch(:webpack_compile_flags) return [] if flags.nil? valid_flags = flags.is_a?(Array) && flags.all? { |flag| valid_webpack_compile_flag?(flag) } unless valid_flags disallowed_flags = DISALLOWED_WEBPACK_COMPILE_FLAGS.join(", ") raise "Shakapacker configuration error: compile flags (webpack_compile_flags) must be an array of " \ "non-empty strings and must not include \"--\" or Shakapacker-specific " \ "wrapper/short-circuit/watch/managed flags (#{disallowed_flags})" end flags end |
#webpack_compile_output? ⇒ Boolean
Returns whether webpack/rspack compilation output should be shown
267 268 269 |
# File 'lib/shakapacker/configuration.rb', line 267 def webpack_compile_output? fetch(:webpack_compile_output) end |
#webpack_loader ⇒ String
Deprecated alias for javascript_transpiler
432 433 434 |
# File 'lib/shakapacker/configuration.rb', line 432 def webpack_loader javascript_transpiler end |