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
661 662 663 664 665 666 |
# File 'lib/shakapacker/configuration.rb', line 661 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)
311 312 313 314 315 316 317 318 319 320 |
# File 'lib/shakapacker/configuration.rb', line 311 def assets_bundler # CLI --bundler flag takes highest precedence return @bundler_override if @bundler_override # Show deprecation warning if using old 'bundler' key if data.has_key?(:bundler) && !data.has_key?(:assets_bundler) $stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is deprecated. Please use 'assets_bundler' instead to avoid confusion with Ruby's Bundler gem manager." end ENV["SHAKAPACKER_ASSETS_BUNDLER"] || fetch(:assets_bundler) || fetch(:bundler) || "webpack" end |
#assets_bundler_config_path ⇒ String
Returns the path to the bundler configuration directory
457 458 459 460 461 462 463 |
# File 'lib/shakapacker/configuration.rb', line 457 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
327 328 329 |
# File 'lib/shakapacker/configuration.rb', line 327 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)
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/shakapacker/configuration.rb', line 434 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
480 481 482 |
# File 'lib/shakapacker/configuration.rb', line 480 def data @data ||= load.freeze end |
#default_javascript_transpiler ⇒ String
496 497 498 499 |
# File 'lib/shakapacker/configuration.rb', line 496 def default_javascript_transpiler # RSpack has built-in SWC support, use it by default rspack? ? "swc" : "babel" end |
#defaults ⇒ HashWithIndifferentAccess
758 759 760 761 762 763 764 765 766 767 768 769 770 771 |
# File 'lib/shakapacker/configuration.rb', line 758 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
684 685 686 |
# File 'lib/shakapacker/configuration.rb', line 684 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
647 648 649 |
# File 'lib/shakapacker/configuration.rb', line 647 def fetch(key) data.fetch(key, defaults[key]) end |
#integrity ⇒ bool, Hash[String | Symbol, untyped]
Returns whether subresource integrity (SRI) is enabled
674 675 676 |
# File 'lib/shakapacker/configuration.rb', line 674 def integrity fetch(:integrity) end |
#javascript_transpiler ⇒ String
Returns the JavaScript transpiler to use (babel, swc, or esbuild)
373 374 375 376 377 378 379 380 381 382 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 |
# File 'lib/shakapacker/configuration.rb', line 373 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]
721 722 723 724 725 726 727 728 729 730 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 |
# File 'lib/shakapacker/configuration.rb', line 721 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.
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 |
# File 'lib/shakapacker/configuration.rb', line 779 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
351 352 353 354 355 356 357 358 359 360 |
# File 'lib/shakapacker/configuration.rb', line 351 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
773 774 775 776 777 |
# File 'lib/shakapacker/configuration.rb', line 773 def relative_path(path) return ".#{path}" if path.start_with?("/") path end |
#resolve_paths_for_comparison ⇒ [String, String]
706 707 708 709 710 711 712 713 714 715 716 717 718 719 |
# File 'lib/shakapacker/configuration.rb', line 706 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
334 335 336 |
# File 'lib/shakapacker/configuration.rb', line 334 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.
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
# File 'lib/shakapacker/configuration.rb', line 689 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.
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 |
# File 'lib/shakapacker/configuration.rb', line 591 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.
622 623 624 625 626 627 628 629 630 631 632 633 634 635 |
# File 'lib/shakapacker/configuration.rb', line 622 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
341 342 343 |
# File 'lib/shakapacker/configuration.rb', line 341 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
422 423 424 |
# File 'lib/shakapacker/configuration.rb', line 422 def webpack_loader javascript_transpiler end |