vite_ruby 3.9.1 → 4.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -409
- data/README.md +1 -1
- data/default.vite.json +2 -7
- data/exe/vite +3 -5
- data/lib/tasks/vite.rake +19 -56
- data/lib/vite_ruby/build.rb +17 -47
- data/lib/vite_ruby/builder.rb +34 -29
- data/lib/vite_ruby/cli/build.rb +4 -6
- data/lib/vite_ruby/cli/clobber.rb +4 -4
- data/lib/vite_ruby/cli/dev.rb +3 -3
- data/lib/vite_ruby/cli/file_utils.rb +8 -8
- data/lib/vite_ruby/cli/install.rb +38 -43
- data/lib/vite_ruby/cli/upgrade.rb +4 -4
- data/lib/vite_ruby/cli/upgrade_packages.rb +4 -3
- data/lib/vite_ruby/cli/version.rb +1 -1
- data/lib/vite_ruby/cli/vite.rb +9 -24
- data/lib/vite_ruby/cli.rb +8 -21
- data/lib/vite_ruby/commands.rb +75 -30
- data/lib/vite_ruby/compatibility_check.rb +8 -8
- data/lib/vite_ruby/config.rb +37 -67
- data/lib/vite_ruby/dev_server_proxy.rb +24 -30
- data/lib/vite_ruby/error.rb +1 -1
- data/lib/vite_ruby/io.rb +4 -4
- data/lib/vite_ruby/manifest.rb +32 -46
- data/lib/vite_ruby/missing_entrypoint_error.rb +14 -22
- data/lib/vite_ruby/missing_executable_error.rb +1 -1
- data/lib/vite_ruby/runner.rb +12 -20
- data/lib/vite_ruby/version.rb +3 -3
- data/lib/vite_ruby.rb +34 -49
- metadata +10 -46
- data/lib/vite_ruby/cli/ssr.rb +0 -27
data/lib/vite_ruby/commands.rb
CHANGED
@@ -9,7 +9,7 @@ class ViteRuby::Commands
|
|
9
9
|
|
10
10
|
# Public: Defaults to production, and exits if the build fails.
|
11
11
|
def build_from_task(*args)
|
12
|
-
with_node_env(ENV.fetch(
|
12
|
+
with_node_env(ENV.fetch('NODE_ENV', 'production')) {
|
13
13
|
ensure_log_goes_to_stdout {
|
14
14
|
build(*args) || exit!
|
15
15
|
}
|
@@ -23,30 +23,52 @@ class ViteRuby::Commands
|
|
23
23
|
|
24
24
|
# Public: Removes all build cache and previously compiled assets.
|
25
25
|
def clobber
|
26
|
-
dirs = [config.build_output_dir, config.
|
26
|
+
dirs = [config.build_output_dir, config.build_cache_dir, config.vite_cache_dir]
|
27
27
|
dirs.each { |dir| dir.rmtree if dir.exist? }
|
28
|
-
$stdout.puts "Removed vite cache and output dirs:\n\t#{dirs.join("\n\t")}"
|
28
|
+
$stdout.puts "Removed vite cache and output dirs:\n\t#{ dirs.join("\n\t") }"
|
29
29
|
end
|
30
30
|
|
31
|
-
#
|
32
|
-
def
|
33
|
-
|
34
|
-
|
31
|
+
# Public: Receives arguments from a rake task.
|
32
|
+
def clean_from_task(args)
|
33
|
+
ensure_log_goes_to_stdout {
|
34
|
+
clean(keep_up_to: Integer(args.keep || 2), age_in_seconds: Integer(args.age || 3600))
|
35
|
+
}
|
35
36
|
end
|
36
37
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
38
|
+
# Public: Cleanup old assets in the output directory.
|
39
|
+
#
|
40
|
+
# keep_up_to - Max amount of backups to preserve.
|
41
|
+
# age_in_seconds - Amount of time to look back in order to preserve them.
|
42
|
+
#
|
43
|
+
# NOTE: By default keeps the last version, or 2 if created in the past hour.
|
44
|
+
#
|
45
|
+
# Examples:
|
46
|
+
# To force only 1 backup to be kept: clean(1, 0)
|
47
|
+
# To only keep files created within the last 10 minutes: clean(0, 600)
|
48
|
+
def clean(keep_up_to: 2, age_in_seconds: 3600)
|
49
|
+
return false unless may_clean?
|
50
|
+
|
51
|
+
versions
|
52
|
+
.each_with_index
|
53
|
+
.drop_while { |(mtime, _), index|
|
54
|
+
max_age = [0, Time.now - Time.at(mtime)].max
|
55
|
+
max_age < age_in_seconds || index < keep_up_to
|
56
|
+
}
|
57
|
+
.each do |(_, files), _|
|
58
|
+
clean_files(files)
|
59
|
+
end
|
60
|
+
true
|
40
61
|
end
|
41
62
|
|
42
|
-
# Internal:
|
43
|
-
def
|
44
|
-
`
|
63
|
+
# Internal: Installs the binstub for the CLI in the appropriate path.
|
64
|
+
def install_binstubs
|
65
|
+
`bundle binstub vite_ruby --path #{ config.root.join('bin') }`
|
66
|
+
`bundle config --delete bin`
|
45
67
|
end
|
46
68
|
|
47
69
|
# Internal: Verifies if ViteRuby is properly installed.
|
48
70
|
def verify_install
|
49
|
-
unless File.exist?(config.root.join(
|
71
|
+
unless File.exist?(config.root.join('bin/vite'))
|
50
72
|
warn <<~WARN
|
51
73
|
|
52
74
|
vite binstub not found.
|
@@ -59,7 +81,7 @@ class ViteRuby::Commands
|
|
59
81
|
unless config_path.exist?
|
60
82
|
warn <<~WARN
|
61
83
|
|
62
|
-
Configuration #{config_path} file for vite-plugin-ruby not found.
|
84
|
+
Configuration #{ config_path } file for vite-plugin-ruby not found.
|
63
85
|
Make sure `bundle exec vite install` has run successfully before running dependent tasks.
|
64
86
|
WARN
|
65
87
|
exit!
|
@@ -68,24 +90,24 @@ class ViteRuby::Commands
|
|
68
90
|
|
69
91
|
# Internal: Prints information about ViteRuby's environment.
|
70
92
|
def print_info
|
71
|
-
config.
|
72
|
-
$stdout.puts "bin/vite present?: #{File.exist?
|
93
|
+
Dir.chdir(config.root) do
|
94
|
+
$stdout.puts "bin/vite present?: #{ File.exist? 'bin/vite' }"
|
73
95
|
|
74
|
-
$stdout.puts "vite_ruby: #{ViteRuby::VERSION}"
|
96
|
+
$stdout.puts "vite_ruby: #{ ViteRuby::VERSION }"
|
75
97
|
ViteRuby.framework_libraries.each do |framework, library|
|
76
|
-
$stdout.puts "#{library.name}: #{library.version}"
|
77
|
-
$stdout.puts "#{framework}: #{Gem.loaded_specs[framework]&.version}"
|
98
|
+
$stdout.puts "#{ library.name }: #{ library.version }"
|
99
|
+
$stdout.puts "#{ framework }: #{ Gem.loaded_specs[framework]&.version }"
|
78
100
|
end
|
79
101
|
|
80
|
-
$stdout.puts "
|
81
|
-
$stdout.puts "
|
82
|
-
|
83
|
-
|
84
|
-
$stdout.puts "
|
102
|
+
$stdout.puts "node: #{ `node --version` }"
|
103
|
+
$stdout.puts "npm: #{ `npm --version` }"
|
104
|
+
$stdout.puts "yarn: #{ `yarn --version` rescue nil }"
|
105
|
+
$stdout.puts "pnpm: #{ `pnpm --version` rescue nil }"
|
106
|
+
$stdout.puts "ruby: #{ `ruby --version` }"
|
85
107
|
|
86
108
|
$stdout.puts "\n"
|
87
109
|
packages = `npm ls vite vite-plugin-ruby`
|
88
|
-
packages_msg = packages.include?(
|
110
|
+
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
111
|
$stdout.puts packages_msg
|
90
112
|
|
91
113
|
ViteRuby::CompatibilityCheck.verify_plugin_version(config.root)
|
@@ -98,19 +120,42 @@ private
|
|
98
120
|
|
99
121
|
def_delegators :@vite_ruby, :config, :builder, :manifest, :logger, :logger=
|
100
122
|
|
123
|
+
def may_clean?
|
124
|
+
config.build_output_dir.exist? && config.manifest_path.exist?
|
125
|
+
end
|
126
|
+
|
127
|
+
def clean_files(files)
|
128
|
+
files.select { |file| File.file?(file) }.each do |file|
|
129
|
+
File.delete(file)
|
130
|
+
logger.info("Removed #{ file }")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def versions
|
135
|
+
all_files = Dir.glob("#{ config.build_output_dir }/**/*")
|
136
|
+
entries = all_files - [config.manifest_path] - current_version_files
|
137
|
+
entries.reject { |file| File.directory?(file) }
|
138
|
+
.group_by { |file| File.mtime(file).utc.to_i }
|
139
|
+
.sort.reverse
|
140
|
+
end
|
141
|
+
|
142
|
+
def current_version_files
|
143
|
+
Dir.glob(manifest.refresh.values.map { |value| config.build_output_dir.join("#{ value['file'] }*") })
|
144
|
+
end
|
145
|
+
|
101
146
|
def with_node_env(env)
|
102
|
-
original = ENV[
|
103
|
-
ENV[
|
147
|
+
original = ENV['NODE_ENV']
|
148
|
+
ENV['NODE_ENV'] = env
|
104
149
|
yield
|
105
150
|
ensure
|
106
|
-
ENV[
|
151
|
+
ENV['NODE_ENV'] = original
|
107
152
|
end
|
108
153
|
|
109
154
|
def ensure_log_goes_to_stdout
|
110
155
|
old_logger, original_sync = logger, $stdout.sync
|
111
156
|
|
112
157
|
$stdout.sync = true
|
113
|
-
self.logger = Logger.new($stdout, formatter: proc { |_, _, progname, msg|
|
158
|
+
self.logger = Logger.new($stdout, formatter: proc { |_, _, progname, msg| progname == 'vite' ? msg : "#{ msg }\n" })
|
114
159
|
yield
|
115
160
|
ensure
|
116
161
|
self.logger, $stdout.sync = old_logger, original_sync
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'json'
|
4
4
|
|
5
5
|
# Internal: Verifies that the installed vite-plugin-ruby version is compatible
|
6
6
|
# with the current version of vite_ruby.
|
@@ -11,9 +11,9 @@ module ViteRuby::CompatibilityCheck
|
|
11
11
|
class << self
|
12
12
|
# Public: Attempt to verify that the vite-plugin-ruby version is compatible.
|
13
13
|
def verify_plugin_version(root)
|
14
|
-
package = JSON.parse(root.join(
|
15
|
-
requirement = package.dig(
|
16
|
-
|
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
17
|
|
18
18
|
raise_unless_satisfied(requirement, ViteRuby::DEFAULT_PLUGIN_VERSION)
|
19
19
|
end
|
@@ -22,9 +22,9 @@ module ViteRuby::CompatibilityCheck
|
|
22
22
|
def raise_unless_satisfied(npm_req, ruby_req)
|
23
23
|
unless compatible_plugin?(npm_req, ruby_req)
|
24
24
|
raise ArgumentError, <<~ERROR
|
25
|
-
vite-plugin-ruby@#{npm_req} might not be compatible with vite_ruby-#{ViteRuby::VERSION}
|
25
|
+
vite-plugin-ruby@#{ npm_req } might not be compatible with vite_ruby-#{ ViteRuby::VERSION }
|
26
26
|
|
27
|
-
You may disable this check if needed: https://vite-ruby.netlify.app/config/#
|
27
|
+
You may disable this check if needed: https://vite-ruby.netlify.app/config/#skipCompatibilityCheck
|
28
28
|
|
29
29
|
You may upgrade both by running:
|
30
30
|
|
@@ -37,12 +37,12 @@ module ViteRuby::CompatibilityCheck
|
|
37
37
|
# requirement.
|
38
38
|
def compatible_plugin?(npm_req, ruby_req)
|
39
39
|
npm_req, ruby_req = [npm_req, ruby_req]
|
40
|
-
.map { |req| Gem::Requirement.new(req.sub(
|
40
|
+
.map { |req| Gem::Requirement.new(req.sub('^', '~>')) }
|
41
41
|
|
42
42
|
current_version = npm_req.requirements.first.second
|
43
43
|
|
44
44
|
ruby_req.satisfied_by?(current_version)
|
45
|
-
rescue
|
45
|
+
rescue StandardError
|
46
46
|
true
|
47
47
|
end
|
48
48
|
end
|
data/lib/vite_ruby/config.rb
CHANGED
@@ -1,36 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'json'
|
4
4
|
|
5
5
|
# Public: Allows to resolve configuration sourced from `config/vite.json` and
|
6
6
|
# environment variables, combining them with the default options.
|
7
7
|
class ViteRuby::Config
|
8
|
-
def origin
|
9
|
-
"#{protocol}://#{host_with_port}"
|
10
|
-
end
|
11
|
-
|
12
8
|
def protocol
|
13
|
-
https ?
|
9
|
+
https ? 'https' : 'http'
|
14
10
|
end
|
15
11
|
|
16
12
|
def host_with_port
|
17
|
-
"#{host}:#{port}"
|
13
|
+
"#{ host }:#{ port }"
|
18
14
|
end
|
19
15
|
|
20
|
-
# Internal: Path
|
21
|
-
def
|
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
|
-
]
|
16
|
+
# Internal: Path where Vite outputs the manifest file.
|
17
|
+
def manifest_path
|
18
|
+
build_output_dir.join('manifest.json')
|
29
19
|
end
|
30
20
|
|
31
|
-
# Internal: Path
|
32
|
-
def
|
33
|
-
|
21
|
+
# Internal: Path where vite-plugin-ruby outputs the assets manifest file.
|
22
|
+
def assets_manifest_path
|
23
|
+
build_output_dir.join('manifest-assets.json')
|
34
24
|
end
|
35
25
|
|
36
26
|
# Public: The directory where Vite will store the built assets.
|
@@ -45,7 +35,7 @@ class ViteRuby::Config
|
|
45
35
|
|
46
36
|
# Internal: The directory where Vite stores its processing cache.
|
47
37
|
def vite_cache_dir
|
48
|
-
root.join(
|
38
|
+
root.join('node_modules/.vite')
|
49
39
|
end
|
50
40
|
|
51
41
|
# Public: The directory that Vite uses as root.
|
@@ -55,63 +45,46 @@ class ViteRuby::Config
|
|
55
45
|
|
56
46
|
# Public: Loads an optional config/vite.rb file that can modify ViteRuby.env
|
57
47
|
def load_ruby_config
|
58
|
-
rb_config_path = File.expand_path(config_path.sub(/.json$/,
|
48
|
+
rb_config_path = File.expand_path(config_path.sub(/.json$/, '.rb'), root)
|
59
49
|
load rb_config_path if File.exist?(rb_config_path)
|
60
50
|
end
|
61
51
|
|
62
52
|
# Public: Sets additional environment variables for vite-plugin-ruby.
|
63
|
-
def to_env
|
53
|
+
def to_env
|
64
54
|
CONFIGURABLE_WITH_ENV.each_with_object({}) do |option, env|
|
65
55
|
unless (value = @config[option]).nil?
|
66
|
-
env["#{ViteRuby::ENV_PREFIX}_#{option.upcase}"] = value.to_s
|
56
|
+
env["#{ ViteRuby::ENV_PREFIX }_#{ option.upcase }"] = value.to_s
|
67
57
|
end
|
68
|
-
end.merge(
|
58
|
+
end.merge(ViteRuby.env)
|
69
59
|
end
|
70
60
|
|
71
61
|
# Internal: Files and directories that should be watched for changes.
|
72
62
|
def watched_paths
|
73
63
|
[
|
74
64
|
*(watch_additional_paths + additional_entrypoints).reject { |dir|
|
75
|
-
dir.start_with?(
|
65
|
+
dir.start_with?('~/') || dir.start_with?(source_code_dir)
|
76
66
|
},
|
77
|
-
"#{source_code_dir}/**/*",
|
78
|
-
config_path.sub(/.json$/,
|
67
|
+
"#{ source_code_dir }/**/*",
|
68
|
+
config_path.sub(/.json$/, '.{rb,json}'),
|
79
69
|
*DEFAULT_WATCHED_PATHS,
|
80
70
|
].freeze
|
81
71
|
end
|
82
72
|
|
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
73
|
private
|
89
74
|
|
90
75
|
# Internal: Coerces all the configuration values, in case they were passed
|
91
76
|
# as environment variables which are always strings.
|
92
77
|
def coerce_values(config)
|
93
|
-
config[
|
94
|
-
config[
|
95
|
-
config[
|
96
|
-
config[
|
97
|
-
config
|
98
|
-
coerce_booleans(config, "auto_build", "hide_build_console_output", "https", "skip_compatibility_check", "skip_proxy")
|
99
|
-
config["package_manager"] ||= detect_package_manager(root)
|
78
|
+
config['mode'] = config['mode'].to_s
|
79
|
+
config['port'] = config['port'].to_i
|
80
|
+
config['root'] = Pathname.new(config['root'])
|
81
|
+
config['build_cache_dir'] = config['root'].join(config['build_cache_dir'])
|
82
|
+
coerce_booleans(config, 'auto_build', 'hide_build_console_output', 'https', 'skip_compatibility_check')
|
100
83
|
end
|
101
84
|
|
102
85
|
# Internal: Coerces configuration options to boolean.
|
103
86
|
def coerce_booleans(config, *names)
|
104
|
-
|
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?
|
112
|
-
return "yarn" if root.join("yarn.lock").exist?
|
113
|
-
|
114
|
-
"npm"
|
87
|
+
names.each { |name| config[name] = [true, 'true'].include?(config[name]) }
|
115
88
|
end
|
116
89
|
|
117
90
|
def initialize(attrs)
|
@@ -125,8 +98,8 @@ private
|
|
125
98
|
# Public: Returns the project configuration for Vite.
|
126
99
|
def resolve_config(**attrs)
|
127
100
|
config = config_defaults.merge(attrs.transform_keys(&:to_s))
|
128
|
-
file_path = File.join(config[
|
129
|
-
file_config = config_from_file(file_path, mode: config[
|
101
|
+
file_path = File.join(config['root'], config['config_path'])
|
102
|
+
file_config = config_from_file(file_path, mode: config['mode'])
|
130
103
|
new DEFAULT_CONFIG.merge(file_config).merge(config_from_env).merge(config)
|
131
104
|
end
|
132
105
|
|
@@ -134,21 +107,21 @@ private
|
|
134
107
|
|
135
108
|
# Internal: Converts camelCase to snake_case.
|
136
109
|
SNAKE_CASE = ->(camel_cased_word) {
|
137
|
-
camel_cased_word.to_s.gsub(
|
110
|
+
camel_cased_word.to_s.gsub(/::/, '/')
|
138
111
|
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
139
112
|
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
140
|
-
.tr(
|
113
|
+
.tr('-', '_')
|
141
114
|
.downcase
|
142
115
|
}
|
143
116
|
|
144
117
|
# Internal: Default values for a Ruby application.
|
145
|
-
def config_defaults(asset_host: nil, mode: ENV.fetch(
|
118
|
+
def config_defaults(asset_host: nil, mode: ENV.fetch('RACK_ENV', 'development'), root: Dir.pwd)
|
146
119
|
{
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
}
|
120
|
+
'asset_host' => option_from_env('asset_host') || asset_host,
|
121
|
+
'config_path' => option_from_env('config_path') || DEFAULT_CONFIG.fetch('config_path'),
|
122
|
+
'mode' => option_from_env('mode') || mode,
|
123
|
+
'root' => option_from_env('root') || root,
|
124
|
+
}
|
152
125
|
end
|
153
126
|
|
154
127
|
# Internal: Used to load a JSON file from the specified path.
|
@@ -162,7 +135,7 @@ private
|
|
162
135
|
|
163
136
|
# Internal: Retrieves a configuration option from environment variables.
|
164
137
|
def option_from_env(name)
|
165
|
-
ViteRuby.env["#{ViteRuby::ENV_PREFIX}_#{name.upcase}"]
|
138
|
+
ViteRuby.env["#{ ViteRuby::ENV_PREFIX }_#{ name.upcase }"]
|
166
139
|
end
|
167
140
|
|
168
141
|
# Internal: Extracts the configuration options provided as env vars.
|
@@ -177,16 +150,16 @@ private
|
|
177
150
|
# Internal: Loads the configuration options provided in a JSON file.
|
178
151
|
def config_from_file(path, mode:)
|
179
152
|
multi_env_config = load_json(path)
|
180
|
-
multi_env_config.fetch(
|
153
|
+
multi_env_config.fetch('all', {})
|
181
154
|
.merge(multi_env_config.fetch(mode, {}))
|
182
155
|
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"
|
156
|
+
$stderr << "Check that your vite.json configuration file is available in the load path:\n\n\t#{ error.message }\n\n"
|
184
157
|
{}
|
185
158
|
end
|
186
159
|
end
|
187
160
|
|
188
161
|
# Internal: Shared configuration with the Vite plugin for Ruby.
|
189
|
-
DEFAULT_CONFIG = load_json("#{__dir__}/../../default.vite.json").freeze
|
162
|
+
DEFAULT_CONFIG = load_json("#{ __dir__ }/../../default.vite.json").freeze
|
190
163
|
|
191
164
|
# Internal: Configuration options that can not be provided as env vars.
|
192
165
|
NOT_CONFIGURABLE_WITH_ENV = %w[additional_entrypoints watch_additional_paths].freeze
|
@@ -196,15 +169,12 @@ private
|
|
196
169
|
|
197
170
|
# Internal: If any of these files is modified the build won't be skipped.
|
198
171
|
DEFAULT_WATCHED_PATHS = %w[
|
199
|
-
bun.lockb
|
200
172
|
package-lock.json
|
201
173
|
package.json
|
202
174
|
pnpm-lock.yaml
|
203
175
|
postcss.config.js
|
204
176
|
tailwind.config.js
|
205
177
|
vite.config.js
|
206
|
-
vite.config.mjs
|
207
|
-
vite.config.mts
|
208
178
|
vite.config.ts
|
209
179
|
windi.config.ts
|
210
180
|
yarn.lock
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'rack/proxy'
|
4
4
|
|
5
5
|
# Public: Allows to relay asset requests to the Vite development server.
|
6
6
|
class ViteRuby::DevServerProxy < Rack::Proxy
|
7
7
|
HOST_WITH_PORT_REGEX = %r{^(.+?)(:\d+)/}
|
8
|
-
VITE_DEPENDENCY_PREFIX =
|
8
|
+
VITE_DEPENDENCY_PREFIX = '/@'
|
9
9
|
|
10
10
|
def initialize(app = nil, options = {})
|
11
11
|
@vite_ruby = options.delete(:vite_ruby) || ViteRuby.instance
|
12
|
-
options[:streaming] = false if config.mode ==
|
12
|
+
options[:streaming] = false if config.mode == 'test' && !options.key?(:streaming)
|
13
13
|
super
|
14
14
|
end
|
15
15
|
|
@@ -17,7 +17,7 @@ class ViteRuby::DevServerProxy < Rack::Proxy
|
|
17
17
|
def perform_request(env)
|
18
18
|
if vite_should_handle?(env) && dev_server_running?
|
19
19
|
forward_to_vite_dev_server(env)
|
20
|
-
super
|
20
|
+
super(env)
|
21
21
|
else
|
22
22
|
@app.call(env)
|
23
23
|
end
|
@@ -30,47 +30,41 @@ private
|
|
30
30
|
def_delegators :@vite_ruby, :config, :dev_server_running?
|
31
31
|
|
32
32
|
def rewrite_uri_for_vite(env)
|
33
|
-
uri = env.fetch(
|
34
|
-
env[
|
33
|
+
uri = env.fetch('REQUEST_URI') { [env['PATH_INFO'], env['QUERY_STRING']].reject { |str| str.to_s.strip.empty? }.join('?') }
|
34
|
+
env['PATH_INFO'], env['QUERY_STRING'] = (env['REQUEST_URI'] = normalize_uri(uri)).split('?')
|
35
35
|
end
|
36
36
|
|
37
37
|
def normalize_uri(uri)
|
38
38
|
uri
|
39
|
-
.sub(HOST_WITH_PORT_REGEX,
|
40
|
-
.sub(
|
41
|
-
.sub(
|
39
|
+
.sub(HOST_WITH_PORT_REGEX, '/') # Hanami adds the host and port.
|
40
|
+
.sub('.ts.js', '.ts') # Hanami's javascript helper always adds the extension.
|
41
|
+
.sub(/(\.(?!min|module)\w+)\.css$/, '\1') # Rails' stylesheet_link_tag always adds the extension.
|
42
42
|
end
|
43
43
|
|
44
44
|
def forward_to_vite_dev_server(env)
|
45
45
|
rewrite_uri_for_vite(env)
|
46
|
-
env[
|
47
|
-
env[
|
48
|
-
env[
|
49
|
-
env[
|
50
|
-
env[
|
51
|
-
env[
|
52
|
-
env[
|
46
|
+
env['QUERY_STRING'] ||= ''
|
47
|
+
env['HTTP_HOST'] = env['HTTP_X_FORWARDED_HOST'] = config.host
|
48
|
+
env['HTTP_X_FORWARDED_SERVER'] = config.host_with_port
|
49
|
+
env['HTTP_PORT'] = env['HTTP_X_FORWARDED_PORT'] = config.port.to_s
|
50
|
+
env['HTTP_X_FORWARDED_PROTO'] = env['HTTP_X_FORWARDED_SCHEME'] = config.protocol
|
51
|
+
env['HTTPS'] = env['HTTP_X_FORWARDED_SSL'] = 'off' unless config.https
|
52
|
+
env['SCRIPT_NAME'] = ''
|
53
53
|
end
|
54
54
|
|
55
55
|
def vite_should_handle?(env)
|
56
|
-
path = normalize_uri(env[
|
57
|
-
return true if path.start_with?(
|
58
|
-
|
59
|
-
true if file_in_vite_root?(path) # Fallback if Vite can serve the file
|
56
|
+
path = normalize_uri(env['PATH_INFO'])
|
57
|
+
return true if path.start_with?(vite_asset_url_prefix) # Vite asset
|
58
|
+
return true if path.start_with?(VITE_DEPENDENCY_PREFIX) # Packages and imports
|
59
|
+
return true if file_in_vite_root?(path) # Fallback if Vite can serve the file
|
60
60
|
end
|
61
61
|
|
62
|
-
# NOTE: When using an empty 'public_output_dir', we need to rely on a
|
63
|
-
# filesystem check to check whether Vite should serve the request.
|
64
62
|
def file_in_vite_root?(path)
|
65
|
-
path.include?(
|
66
|
-
config.vite_root_dir.join(path.start_with?(
|
63
|
+
path.include?('.') && # Check for extension, avoid filesystem if possible.
|
64
|
+
config.vite_root_dir.join(path.start_with?('/') ? path[1..-1] : path).file?
|
67
65
|
end
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
#
|
72
|
-
# If the path starts with that prefix, it will be redirected to Vite.
|
73
|
-
def vite_url_prefix
|
74
|
-
@vite_url_prefix ||= config.public_output_dir.empty? ? VITE_DEPENDENCY_PREFIX : "/#{config.public_output_dir}/"
|
67
|
+
def vite_asset_url_prefix
|
68
|
+
@vite_asset_url_prefix ||= config.public_output_dir.empty? ? "\0" : "/#{ config.public_output_dir }/"
|
75
69
|
end
|
76
70
|
end
|
data/lib/vite_ruby/error.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Internal: Provides common functionality for errors.
|
4
4
|
class ViteRuby::Error < StandardError
|
5
5
|
def message
|
6
|
-
super.sub(
|
6
|
+
super.sub(':troubleshooting:', <<~MSG)
|
7
7
|
Visit the Troubleshooting guide for more information:
|
8
8
|
https://vite-ruby.netlify.app/guide/troubleshooting.html#troubleshooting
|
9
9
|
MSG
|
data/lib/vite_ruby/io.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'open3'
|
4
4
|
|
5
5
|
# Public: Builds on top of Ruby I/O open3 providing a friendlier experience.
|
6
6
|
module ViteRuby::IO
|
7
7
|
class << self
|
8
8
|
# Internal: A modified version of capture3 that can continuosly print stdout.
|
9
9
|
# NOTE: Streaming output provides a better UX when running bin/vite build.
|
10
|
-
def capture(*cmd, with_output: $stdout.method(:puts), stdin_data:
|
10
|
+
def capture(*cmd, with_output: $stdout.method(:puts), stdin_data: '', **opts)
|
11
11
|
return Open3.capture3(*cmd, **opts) unless with_output
|
12
12
|
|
13
13
|
Open3.popen3(*cmd, **opts) { |stdin, stdout, stderr, wait_threads|
|
@@ -15,13 +15,13 @@ module ViteRuby::IO
|
|
15
15
|
stdin.close
|
16
16
|
out = Thread.new { read_lines(stdout, &with_output) }
|
17
17
|
err = Thread.new { stderr.read }
|
18
|
-
[out.value, err.value
|
18
|
+
[out.value, err.value, wait_threads.value]
|
19
19
|
}
|
20
20
|
end
|
21
21
|
|
22
22
|
# Internal: Reads and yield every line in the stream. Returns the full content.
|
23
23
|
def read_lines(io)
|
24
|
-
buffer = +
|
24
|
+
buffer = +''
|
25
25
|
while line = io.gets
|
26
26
|
buffer << line
|
27
27
|
yield line
|