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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +752 -0
- data/LICENSE.txt +21 -0
- data/README.md +84 -0
- data/default.vite.json +26 -0
- data/exe/vite +12 -0
- data/lib/tasks/vite.rake +95 -0
- data/lib/vite_ruby/build.rb +82 -0
- data/lib/vite_ruby/builder.rb +86 -0
- data/lib/vite_ruby/cli/build.rb +16 -0
- data/lib/vite_ruby/cli/clobber.rb +14 -0
- data/lib/vite_ruby/cli/dev.rb +13 -0
- data/lib/vite_ruby/cli/file_utils.rb +132 -0
- data/lib/vite_ruby/cli/install.rb +146 -0
- data/lib/vite_ruby/cli/ssr.rb +27 -0
- data/lib/vite_ruby/cli/upgrade.rb +25 -0
- data/lib/vite_ruby/cli/upgrade_packages.rb +10 -0
- data/lib/vite_ruby/cli/version.rb +9 -0
- data/lib/vite_ruby/cli/vite.rb +35 -0
- data/lib/vite_ruby/cli.rb +30 -0
- data/lib/vite_ruby/commands.rb +118 -0
- data/lib/vite_ruby/compatibility_check.rb +49 -0
- data/lib/vite_ruby/config.rb +219 -0
- data/lib/vite_ruby/dev_server_proxy.rb +76 -0
- data/lib/vite_ruby/error.rb +11 -0
- data/lib/vite_ruby/io.rb +32 -0
- data/lib/vite_ruby/manifest.rb +224 -0
- data/lib/vite_ruby/missing_entrypoint_error.rb +53 -0
- data/lib/vite_ruby/missing_executable_error.rb +13 -0
- data/lib/vite_ruby/runner.rb +53 -0
- data/lib/vite_ruby/version.rb +9 -0
- data/lib/vite_ruby.rb +153 -0
- data/templates/config/vite.config.ts +8 -0
- data/templates/config/vite.json +16 -0
- data/templates/entrypoints/application.js +8 -0
- metadata +258 -0
@@ -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,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
|