vite_ruby-roda 3.9.2.1

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.
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require "json"
5
+
6
+ class ViteRuby::CLI::Install < Dry::CLI::Command
7
+ desc "Performs the initial configuration setup to get started with Vite Ruby."
8
+
9
+ option(:package_manager, values: %w[npm pnpm yarn bun], aliases: %w[package-manager with], desc: "The package manager to use when installing JS dependencies.")
10
+
11
+ def call(package_manager: nil, **)
12
+ ENV["VITE_RUBY_PACKAGE_MANAGER"] ||= package_manager if package_manager
13
+
14
+ $stdout.sync = true
15
+
16
+ say "Creating binstub"
17
+ ViteRuby.commands.install_binstubs
18
+
19
+ say "Creating configuration files"
20
+ create_configuration_files
21
+
22
+ say "Installing sample files"
23
+ install_sample_files
24
+
25
+ say "Installing js dependencies"
26
+ install_js_dependencies
27
+
28
+ say "Adding files to .gitignore"
29
+ install_gitignore
30
+
31
+ say "\nVite ⚡️ Ruby successfully installed! 🎉"
32
+ end
33
+
34
+ protected
35
+
36
+ # Internal: The JS packages that should be added to the app.
37
+ def js_dependencies
38
+ [
39
+ "vite@#{ViteRuby::DEFAULT_VITE_VERSION}",
40
+ "vite-plugin-ruby@#{ViteRuby::DEFAULT_PLUGIN_VERSION}",
41
+ ]
42
+ end
43
+
44
+ # Internal: Setup for a plain Rack application.
45
+ def setup_app_files
46
+ copy_template "config/vite.json", to: config.config_path
47
+
48
+ if (rackup_file = root.join("config.ru")).exist?
49
+ inject_line_after_last rackup_file, "require", "use(ViteRuby::DevServerProxy, ssl_verify_none: true) if ViteRuby.run_proxy?"
50
+ end
51
+ end
52
+
53
+ # Internal: Create a sample JS file and attempt to inject it in an HTML template.
54
+ def install_sample_files
55
+ copy_template "entrypoints/application.js", to: config.resolved_entrypoints_dir.join("application.js")
56
+ end
57
+
58
+ private
59
+
60
+ extend Forwardable
61
+
62
+ def_delegators "ViteRuby", :config
63
+
64
+ %i[append cp inject_line_after inject_line_after_last inject_line_before replace_first_line write].each do |util|
65
+ define_method(util) { |*args|
66
+ ViteRuby::CLI::FileUtils.send(util, *args) rescue nil
67
+ }
68
+ end
69
+
70
+ TEMPLATES_PATH = Pathname.new(File.expand_path("../../../templates", __dir__))
71
+
72
+ def copy_template(path, to:)
73
+ cp TEMPLATES_PATH.join(path), to
74
+ end
75
+
76
+ # Internal: Creates the Vite and vite-plugin-ruby configuration files.
77
+ def create_configuration_files
78
+ copy_template "config/vite.config.ts", to: root.join("vite.config.ts")
79
+ append root.join("Procfile.dev"), "vite: bin/vite dev"
80
+ setup_app_files
81
+ ViteRuby.reload_with(config_path: config.config_path)
82
+ end
83
+
84
+ # Internal: Installs vite and vite-plugin-ruby at the project level.
85
+ def install_js_dependencies
86
+ package_json = root.join("package.json")
87
+ unless package_json.exist?
88
+ write package_json, <<~JSON
89
+ {
90
+ "private": true,
91
+ "type": "module"
92
+ }
93
+ JSON
94
+ end
95
+
96
+ if (JSON.parse(package_json.read)["type"] != "module" rescue nil)
97
+ FileUtils.mv root.join("vite.config.ts"), root.join("vite.config.mts"), force: true, verbose: true
98
+ end
99
+
100
+ install_js_packages js_dependencies.join(" ")
101
+ end
102
+
103
+ # Internal: Adds compilation output dirs to git ignore.
104
+ def install_gitignore
105
+ return unless (gitignore_file = root.join(".gitignore")).exist?
106
+
107
+ append(gitignore_file, <<~GITIGNORE)
108
+
109
+ # Vite Ruby
110
+ /public/vite*
111
+ node_modules
112
+ # Vite uses dotenv and suggests to ignore local-only env files. See
113
+ # https://vitejs.dev/guide/env-and-mode.html#env-files
114
+ *.local
115
+ GITIGNORE
116
+ end
117
+
118
+ # Internal: The root path for the Ruby application.
119
+ def root
120
+ @root ||= silent_warnings { config.root }
121
+ end
122
+
123
+ def say(*args)
124
+ $stdout.puts(*args)
125
+ end
126
+
127
+ def run_with_capture(*args, **options)
128
+ Dir.chdir(root) do
129
+ _, stderr, status = ViteRuby::IO.capture(*args, **options)
130
+ say(stderr) unless status.success? || stderr.empty?
131
+ end
132
+ end
133
+
134
+ def install_js_packages(deps)
135
+ run_with_capture("#{config.package_manager} add -D #{deps}", stdin_data: "\n")
136
+ end
137
+
138
+ # Internal: Avoid printing warning about missing vite.json, we will create one.
139
+ def silent_warnings
140
+ old_stderr = $stderr
141
+ $stderr = StringIO.new
142
+ yield
143
+ ensure
144
+ $stderr = old_stderr
145
+ end
146
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ViteRuby::CLI::SSR < ViteRuby::CLI::Vite
4
+ DEFAULT_ENV = CURRENT_ENV || "production"
5
+ JS_EXTENSIONS = %w[js mjs cjs]
6
+
7
+ desc "Run the resulting app from building in SSR mode."
8
+ executable_options
9
+
10
+ def call(mode:, inspect: false, trace_deprecation: false)
11
+ ViteRuby.env["VITE_RUBY_MODE"] = mode
12
+
13
+ ssr_entrypoint = JS_EXTENSIONS
14
+ .map { |ext| ViteRuby.config.ssr_output_dir.join("ssr.#{ext}") }
15
+ .find(&:exist?)
16
+
17
+ raise ArgumentError, "No ssr entrypoint found `#{ViteRuby.config.ssr_output_dir.relative_path_from(ViteRuby.config.root)}/ssr.{#{JS_EXTENSIONS.join(",")}}`. Have you run bin/vite build --ssr?" unless ssr_entrypoint
18
+
19
+ cmd = [
20
+ "node",
21
+ ("--inspect-brk" if inspect),
22
+ ("--trace-deprecation" if trace_deprecation),
23
+ ssr_entrypoint,
24
+ ]
25
+ Kernel.exec(*cmd.compact.map(&:to_s))
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ViteRuby::CLI::Upgrade < ViteRuby::CLI::Install
4
+ desc "Updates Vite Ruby related gems and npm packages."
5
+
6
+ def call(**)
7
+ upgrade_ruby_gems
8
+ upgrade_npm_packages
9
+ end
10
+
11
+ protected
12
+
13
+ def upgrade_ruby_gems
14
+ say "Updating gems"
15
+
16
+ libraries = ViteRuby.framework_libraries.map { |_f, library| library.name }
17
+
18
+ run_with_capture("bundle update #{libraries.join(" ")}")
19
+ end
20
+
21
+ # NOTE: Spawn a new process so that it uses the updated vite_ruby.
22
+ def upgrade_npm_packages
23
+ Kernel.exec("bundle exec vite upgrade_packages")
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ViteRuby::CLI::UpgradePackages < ViteRuby::CLI::Install
4
+ desc "Upgrades the npm packages to the recommended versions."
5
+
6
+ def call(**)
7
+ say "Upgrading npm packages"
8
+ install_js_packages js_dependencies.join(" ")
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ViteRuby::CLI::Version < Dry::CLI::Command
4
+ desc "Print version"
5
+
6
+ def call(**)
7
+ ViteRuby.commands.print_info
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ViteRuby::CLI::Vite < Dry::CLI::Command
4
+ CURRENT_ENV = ENV["RACK_ENV"] || ENV["RAILS_ENV"]
5
+
6
+ def self.executable_options
7
+ option(:mode, default: self::DEFAULT_ENV, values: %w[development production test], aliases: ["m"], desc: "The build mode for Vite")
8
+ option(:node_options, desc: "Node options for the Vite executable", aliases: ["node-options"])
9
+ option(:inspect, desc: "Run Vite in a debugging session with node --inspect-brk", aliases: ["inspect-brk"], type: :boolean)
10
+ option(:trace_deprecation, desc: "Run Vite in debugging mode with node --trace-deprecation", aliases: ["trace-deprecation"], type: :boolean)
11
+ end
12
+
13
+ def self.shared_options
14
+ executable_options
15
+ option(:debug, desc: "Run Vite in verbose mode, printing all debugging output", aliases: ["verbose"], type: :boolean)
16
+ option(:clobber, desc: "Clear cache and previous builds", type: :boolean, aliases: %w[clean clear])
17
+ end
18
+
19
+ def call(mode:, args: [], clobber: false, node_options: nil, inspect: nil, trace_deprecation: nil, **boolean_opts)
20
+ ViteRuby.env["VITE_RUBY_MODE"] = mode
21
+ ViteRuby.commands.clobber if clobber
22
+
23
+ node_options = [
24
+ node_options,
25
+ ("--inspect-brk" if inspect),
26
+ ("--trace-deprecation" if trace_deprecation),
27
+ ].compact.join(" ")
28
+
29
+ args << %(--node-options="#{node_options}") unless node_options.empty?
30
+
31
+ boolean_opts.map { |name, value| args << "--#{name}" if value }
32
+
33
+ yield(args)
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/cli"
4
+
5
+ # Public: Command line interface that allows to install the library, and run
6
+ # simple commands.
7
+ class ViteRuby::CLI
8
+ extend Dry::CLI::Registry
9
+
10
+ register "build", Build, aliases: ["b"]
11
+ register "clobber", Clobber, aliases: %w[clean clear]
12
+ register "dev", Dev, aliases: %w[d serve]
13
+ register "install", Install
14
+ register "ssr", SSR
15
+ register "version", Version, aliases: ["v", "-v", "--version", "info"]
16
+ register "upgrade", Upgrade, aliases: ["update"]
17
+ register "upgrade_packages", UpgradePackages, aliases: ["update_packages"]
18
+
19
+ # Internal: Allows framework-specific variants to extend the CLI.
20
+ def self.require_framework_libraries(path = "cli")
21
+ ViteRuby.framework_libraries.each do |_framework, library|
22
+ require [library.name.tr("-", "/").to_s, path].compact.join("/")
23
+ end
24
+ rescue LoadError
25
+ require_framework_libraries "installation" unless path == "installation"
26
+ end
27
+ end
28
+
29
+ # NOTE: This allows framework-specific variants to extend the CLI.
30
+ ViteRuby::CLI.require_framework_libraries("cli")
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Public: Encapsulates common tasks, available both programatically and from the
4
+ # CLI and Rake tasks.
5
+ class ViteRuby::Commands
6
+ def initialize(vite_ruby)
7
+ @vite_ruby = vite_ruby
8
+ end
9
+
10
+ # Public: Defaults to production, and exits if the build fails.
11
+ def build_from_task(*args)
12
+ with_node_env(ENV.fetch("NODE_ENV", "production")) {
13
+ ensure_log_goes_to_stdout {
14
+ build(*args) || exit!
15
+ }
16
+ }
17
+ end
18
+
19
+ # Public: Builds all assets that are managed by Vite, from the entrypoints.
20
+ def build(*args)
21
+ builder.build(*args).tap { manifest.refresh }
22
+ end
23
+
24
+ # Public: Removes all build cache and previously compiled assets.
25
+ def clobber
26
+ dirs = [config.build_output_dir, config.ssr_output_dir, config.build_cache_dir, config.vite_cache_dir]
27
+ dirs.each { |dir| dir.rmtree if dir.exist? }
28
+ $stdout.puts "Removed vite cache and output dirs:\n\t#{dirs.join("\n\t")}"
29
+ end
30
+
31
+ # Internal: Installs the binstub for the CLI in the appropriate path.
32
+ def install_binstubs
33
+ `bundle binstub vite_ruby --path #{config.root.join("bin")}`
34
+ `bundle config --delete bin`
35
+ end
36
+
37
+ # Internal: Checks if the npm version is 6 or lower.
38
+ def legacy_npm_version?
39
+ `npm --version`.to_i < 7 rescue false
40
+ end
41
+
42
+ # Internal: Checks if the yarn version is 1.x.
43
+ def legacy_yarn_version?
44
+ `yarn --version`.to_i < 2 rescue false
45
+ end
46
+
47
+ # Internal: Verifies if ViteRuby is properly installed.
48
+ def verify_install
49
+ unless File.exist?(config.root.join("bin/vite"))
50
+ warn <<~WARN
51
+
52
+ vite binstub not found.
53
+ Have you run `bundle binstub vite_ruby`?
54
+ Make sure the bin directory and bin/vite are not included in .gitignore
55
+ WARN
56
+ end
57
+
58
+ config_path = config.root.join(config.config_path)
59
+ unless config_path.exist?
60
+ warn <<~WARN
61
+
62
+ Configuration #{config_path} file for vite-plugin-ruby not found.
63
+ Make sure `bundle exec vite install` has run successfully before running dependent tasks.
64
+ WARN
65
+ exit!
66
+ end
67
+ end
68
+
69
+ # Internal: Prints information about ViteRuby's environment.
70
+ def print_info
71
+ config.within_root do
72
+ $stdout.puts "bin/vite present?: #{File.exist? "bin/vite"}"
73
+
74
+ $stdout.puts "vite_ruby: #{ViteRuby::VERSION}"
75
+ ViteRuby.framework_libraries.each do |framework, library|
76
+ $stdout.puts "#{library.name}: #{library.version}"
77
+ $stdout.puts "#{framework}: #{Gem.loaded_specs[framework]&.version}"
78
+ end
79
+
80
+ $stdout.puts "ruby: #{`ruby --version`}"
81
+ $stdout.puts "node: #{`node --version`}"
82
+
83
+ pkg = config.package_manager
84
+ $stdout.puts "#{pkg}: #{`#{pkg} --version` rescue nil}"
85
+
86
+ $stdout.puts "\n"
87
+ packages = `npm ls vite vite-plugin-ruby`
88
+ packages_msg = packages.include?("vite@") ? "installed packages:\n#{packages}" : "❌ Check that vite and vite-plugin-ruby have been added as development dependencies and installed."
89
+ $stdout.puts packages_msg
90
+
91
+ ViteRuby::CompatibilityCheck.verify_plugin_version(config.root)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ extend Forwardable
98
+
99
+ def_delegators :@vite_ruby, :config, :builder, :manifest, :logger, :logger=
100
+
101
+ def with_node_env(env)
102
+ original = ENV["NODE_ENV"]
103
+ ENV["NODE_ENV"] = env
104
+ yield
105
+ ensure
106
+ ENV["NODE_ENV"] = original
107
+ end
108
+
109
+ def ensure_log_goes_to_stdout
110
+ old_logger, original_sync = logger, $stdout.sync
111
+
112
+ $stdout.sync = true
113
+ self.logger = Logger.new($stdout, formatter: proc { |_, _, progname, msg| (progname == "vite") ? msg : "#{msg}\n" })
114
+ yield
115
+ ensure
116
+ self.logger, $stdout.sync = old_logger, original_sync
117
+ end
118
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ # Internal: Verifies that the installed vite-plugin-ruby version is compatible
6
+ # with the current version of vite_ruby.
7
+ #
8
+ # This helps to prevent more subtle runtime errors if there is a mismatch in the
9
+ # manifest schema.
10
+ module ViteRuby::CompatibilityCheck
11
+ class << self
12
+ # Public: Attempt to verify that the vite-plugin-ruby version is compatible.
13
+ def verify_plugin_version(root)
14
+ package = JSON.parse(root.join("package.json").read) rescue {}
15
+ requirement = package.dig("devDependencies", "vite-plugin-ruby") ||
16
+ package.dig("dependencies", "vite-plugin-ruby")
17
+
18
+ raise_unless_satisfied(requirement, ViteRuby::DEFAULT_PLUGIN_VERSION)
19
+ end
20
+
21
+ # Internal: Notifies the user of a possible incompatible plugin.
22
+ def raise_unless_satisfied(npm_req, ruby_req)
23
+ unless compatible_plugin?(npm_req, ruby_req)
24
+ raise ArgumentError, <<~ERROR
25
+ vite-plugin-ruby@#{npm_req} might not be compatible with vite_ruby-#{ViteRuby::VERSION}
26
+
27
+ You may disable this check if needed: https://vite-ruby.netlify.app/config/#skipcompatibilitycheck
28
+
29
+ You may upgrade both by running:
30
+
31
+ bundle exec vite upgrade
32
+ ERROR
33
+ end
34
+ end
35
+
36
+ # Internal: Returns true unless the check is performed and does not meet the
37
+ # requirement.
38
+ def compatible_plugin?(npm_req, ruby_req)
39
+ npm_req, ruby_req = [npm_req, ruby_req]
40
+ .map { |req| Gem::Requirement.new(req.sub("^", "~>")) }
41
+
42
+ current_version = npm_req.requirements.first.second
43
+
44
+ ruby_req.satisfied_by?(current_version)
45
+ rescue
46
+ true
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ # Public: Allows to resolve configuration sourced from `config/vite.json` and
6
+ # environment variables, combining them with the default options.
7
+ class ViteRuby::Config
8
+ def origin
9
+ "#{protocol}://#{host_with_port}"
10
+ end
11
+
12
+ def protocol
13
+ https ? "https" : "http"
14
+ end
15
+
16
+ def host_with_port
17
+ "#{host}:#{port}"
18
+ end
19
+
20
+ # Internal: Path to the manifest files generated by Vite and vite-plugin-ruby.
21
+ def known_manifest_paths
22
+ [
23
+ # NOTE: Generated by Vite when `manifest: true`, which vite-plugin-ruby enables.
24
+ build_output_dir.join(".vite/manifest.json"),
25
+
26
+ # NOTE: Path where vite-plugin-ruby outputs the assets manifest file.
27
+ build_output_dir.join(".vite/manifest-assets.json"),
28
+ ]
29
+ end
30
+
31
+ # Internal: Path to the manifest files generated by Vite and vite-plugin-ruby.
32
+ def manifest_paths
33
+ known_manifest_paths.select(&:exist?)
34
+ end
35
+
36
+ # Public: The directory where Vite will store the built assets.
37
+ def build_output_dir
38
+ root.join(public_dir, public_output_dir)
39
+ end
40
+
41
+ # Public: The directory where the entries are located.
42
+ def resolved_entrypoints_dir
43
+ vite_root_dir.join(entrypoints_dir)
44
+ end
45
+
46
+ # Internal: The directory where Vite stores its processing cache.
47
+ def vite_cache_dir
48
+ root.join("node_modules/.vite")
49
+ end
50
+
51
+ # Public: The directory that Vite uses as root.
52
+ def vite_root_dir
53
+ root.join(source_code_dir)
54
+ end
55
+
56
+ # Public: Loads an optional config/vite.rb file that can modify ViteRuby.env
57
+ def load_ruby_config
58
+ rb_config_path = File.expand_path(config_path.sub(/.json$/, ".rb"), root)
59
+ load rb_config_path if File.exist?(rb_config_path)
60
+ end
61
+
62
+ # Public: Sets additional environment variables for vite-plugin-ruby.
63
+ def to_env(env_vars = ViteRuby.env)
64
+ CONFIGURABLE_WITH_ENV.each_with_object({}) do |option, env|
65
+ unless (value = @config[option]).nil?
66
+ env["#{ViteRuby::ENV_PREFIX}_#{option.upcase}"] = value.to_s
67
+ end
68
+ end.merge(env_vars)
69
+ end
70
+
71
+ # Internal: Files and directories that should be watched for changes.
72
+ def watched_paths
73
+ [
74
+ *(watch_additional_paths + additional_entrypoints).reject { |dir|
75
+ dir.start_with?("~/") || dir.start_with?(source_code_dir)
76
+ },
77
+ "#{source_code_dir}/**/*",
78
+ config_path.sub(/.json$/, ".{rb,json}"),
79
+ *DEFAULT_WATCHED_PATHS,
80
+ ].freeze
81
+ end
82
+
83
+ # Internal: Changes the current directory to the root dir.
84
+ def within_root(&block)
85
+ Dir.chdir(File.expand_path(root), &block)
86
+ end
87
+
88
+ private
89
+
90
+ # Internal: Coerces all the configuration values, in case they were passed
91
+ # as environment variables which are always strings.
92
+ def coerce_values(config)
93
+ config["mode"] = config["mode"].to_s
94
+ config["port"] = config["port"].to_i
95
+ config["root"] = root = Pathname.new(config["root"])
96
+ config["build_cache_dir"] = root.join(config["build_cache_dir"])
97
+ config["ssr_output_dir"] = root.join(config["ssr_output_dir"])
98
+ coerce_booleans(config, "auto_build", "hide_build_console_output", "https", "skip_compatibility_check", "skip_proxy")
99
+ config["package_manager"] ||= detect_package_manager(root)
100
+ end
101
+
102
+ # Internal: Coerces configuration options to boolean.
103
+ def coerce_booleans(config, *names)
104
+ truthy = [true, "true"]
105
+ names.each { |name| config[name] = truthy.include?(config[name]) }
106
+ end
107
+
108
+ def detect_package_manager(root)
109
+ return "npm" if root.join("package-lock.json").exist?
110
+ return "pnpm" if root.join("pnpm-lock.yaml").exist?
111
+ return "bun" if root.join("bun.lockb").exist? || root.join("bun.lock").exist?
112
+ return "yarn" if root.join("yarn.lock").exist?
113
+
114
+ "npm"
115
+ end
116
+
117
+ def initialize(attrs)
118
+ @config = attrs.tap { |config| coerce_values(config) }.freeze
119
+ ViteRuby::CompatibilityCheck.verify_plugin_version(root) unless skip_compatibility_check
120
+ end
121
+
122
+ class << self
123
+ private :new
124
+
125
+ # Public: Returns the project configuration for Vite.
126
+ def resolve_config(**attrs)
127
+ config = config_defaults.merge(attrs.transform_keys(&:to_s))
128
+ file_path = File.join(config["root"], config["config_path"])
129
+ file_config = config_from_file(file_path, mode: config["mode"])
130
+ new DEFAULT_CONFIG.merge(file_config).merge(config_from_env).merge(config)
131
+ end
132
+
133
+ private
134
+
135
+ # Internal: Converts camelCase to snake_case.
136
+ SNAKE_CASE = ->(camel_cased_word) {
137
+ camel_cased_word.to_s.gsub("::", "/")
138
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
139
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
140
+ .tr("-", "_")
141
+ .downcase
142
+ }
143
+
144
+ # Internal: Default values for a Ruby application.
145
+ def config_defaults(asset_host: nil, mode: ENV.fetch("RACK_ENV", "development"), root: Dir.pwd)
146
+ {
147
+ "asset_host" => option_from_env("asset_host") || asset_host,
148
+ "config_path" => option_from_env("config_path") || DEFAULT_CONFIG.fetch("config_path"),
149
+ "mode" => option_from_env("mode") || mode,
150
+ "root" => option_from_env("root") || root,
151
+ }.select { |_, value| value }
152
+ end
153
+
154
+ # Internal: Used to load a JSON file from the specified path.
155
+ def load_json(path)
156
+ JSON.parse(File.read(File.expand_path(path))).each do |_env, config|
157
+ config.transform_keys!(&SNAKE_CASE) if config.is_a?(Hash)
158
+ end.tap do |config|
159
+ config.transform_keys!(&SNAKE_CASE)
160
+ end
161
+ end
162
+
163
+ # Internal: Retrieves a configuration option from environment variables.
164
+ def option_from_env(name)
165
+ ViteRuby.env["#{ViteRuby::ENV_PREFIX}_#{name.upcase}"]
166
+ end
167
+
168
+ # Internal: Extracts the configuration options provided as env vars.
169
+ def config_from_env
170
+ CONFIGURABLE_WITH_ENV.each_with_object({}) do |option, env_vars|
171
+ if value = option_from_env(option)
172
+ env_vars[option] = value
173
+ end
174
+ end
175
+ end
176
+
177
+ # Internal: Loads the configuration options provided in a JSON file.
178
+ def config_from_file(path, mode:)
179
+ multi_env_config = load_json(path)
180
+ multi_env_config.fetch("all", {})
181
+ .merge(multi_env_config.fetch(mode, {}))
182
+ rescue Errno::ENOENT => error
183
+ $stderr << "Check that your vite.json configuration file is available in the load path:\n\n\t#{error.message}\n\n"
184
+ {}
185
+ end
186
+ end
187
+
188
+ # Internal: Shared configuration with the Vite plugin for Ruby.
189
+ DEFAULT_CONFIG = load_json("#{__dir__}/../../default.vite.json").freeze
190
+
191
+ # Internal: Configuration options that can not be provided as env vars.
192
+ NOT_CONFIGURABLE_WITH_ENV = %w[additional_entrypoints watch_additional_paths].freeze
193
+
194
+ # Internal: Configuration options that can be provided as env vars.
195
+ CONFIGURABLE_WITH_ENV = (DEFAULT_CONFIG.keys + %w[mode root] - NOT_CONFIGURABLE_WITH_ENV).freeze
196
+
197
+ # Internal: If any of these files is modified the build won't be skipped.
198
+ DEFAULT_WATCHED_PATHS = %w[
199
+ bun.lockb
200
+ package-lock.json
201
+ package.json
202
+ pnpm-lock.yaml
203
+ postcss.config.js
204
+ tailwind.config.js
205
+ vite.config.js
206
+ vite.config.mjs
207
+ vite.config.mts
208
+ vite.config.ts
209
+ windi.config.ts
210
+ yarn.lock
211
+ ].freeze
212
+
213
+ public
214
+
215
+ # Define getters for the configuration options.
216
+ (CONFIGURABLE_WITH_ENV + NOT_CONFIGURABLE_WITH_ENV).each do |option|
217
+ define_method(option) { @config[option] }
218
+ end
219
+ end