vite_ruby 3.9.0 → 4.0.0.alpha1
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 +4 -4
- data/CHANGELOG.md +2 -400
- data/README.md +1 -1
- data/default.vite.json +2 -7
- data/exe/vite +0 -2
- data/lib/tasks/vite.rake +12 -49
- data/lib/vite_ruby/build.rb +16 -46
- data/lib/vite_ruby/builder.rb +26 -21
- data/lib/vite_ruby/cli/build.rb +0 -2
- data/lib/vite_ruby/cli/install.rb +19 -24
- data/lib/vite_ruby/cli/upgrade_packages.rb +2 -1
- data/lib/vite_ruby/cli/vite.rb +4 -19
- data/lib/vite_ruby/cli.rb +0 -13
- data/lib/vite_ruby/commands.rb +61 -16
- data/lib/vite_ruby/compatibility_check.rb +1 -1
- data/lib/vite_ruby/config.rb +12 -41
- data/lib/vite_ruby/dev_server_proxy.rb +5 -10
- data/lib/vite_ruby/io.rb +1 -1
- data/lib/vite_ruby/manifest.rb +17 -29
- data/lib/vite_ruby/missing_entrypoint_error.rb +13 -21
- data/lib/vite_ruby/runner.rb +10 -18
- data/lib/vite_ruby/version.rb +3 -3
- data/lib/vite_ruby.rb +11 -26
- metadata +10 -31
- data/lib/vite_ruby/cli/ssr.rb +0 -27
data/lib/vite_ruby/builder.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
3
4
|
require 'digest/sha1'
|
4
5
|
|
5
6
|
# Public: Keeps track of watched files and triggers builds as needed.
|
@@ -11,13 +12,9 @@ class ViteRuby::Builder
|
|
11
12
|
# Public: Checks if the watched files have changed since the last compilation,
|
12
13
|
# and triggers a Vite build if any files have changed.
|
13
14
|
def build(*args)
|
14
|
-
last_build = last_build_metadata
|
15
|
-
|
16
|
-
|
17
|
-
stdout, stderr, status = build_with_vite(*args)
|
18
|
-
log_build_result(stdout, stderr, status)
|
19
|
-
record_build_metadata(last_build, errors: stderr, success: status.success?)
|
20
|
-
status.success?
|
15
|
+
last_build = last_build_metadata
|
16
|
+
if args.delete('--force') || last_build.stale?
|
17
|
+
build_with_vite(*args).tap { |success| record_build_metadata(success, last_build) }
|
21
18
|
elsif last_build.success
|
22
19
|
logger.debug "Skipping vite build. Watched files have not changed since the last build at #{ last_build.timestamp }"
|
23
20
|
true
|
@@ -28,8 +25,8 @@ class ViteRuby::Builder
|
|
28
25
|
end
|
29
26
|
|
30
27
|
# Internal: Reads the result of the last compilation from disk.
|
31
|
-
def last_build_metadata
|
32
|
-
ViteRuby::Build.from_previous(
|
28
|
+
def last_build_metadata
|
29
|
+
ViteRuby::Build.from_previous(last_build_attrs, watched_files_digest)
|
33
30
|
end
|
34
31
|
|
35
32
|
private
|
@@ -38,35 +35,44 @@ private
|
|
38
35
|
|
39
36
|
def_delegators :@vite_ruby, :config, :logger, :run
|
40
37
|
|
38
|
+
# Internal: Reads metadata recorded on the last build, if it exists.
|
39
|
+
def last_build_attrs
|
40
|
+
last_build_path.exist? ? JSON.parse(last_build_path.read.to_s) : {}
|
41
|
+
rescue JSON::JSONError, Errno::ENOENT, Errno::ENOTDIR
|
42
|
+
{}
|
43
|
+
end
|
44
|
+
|
41
45
|
# Internal: Writes a digest of the watched files to disk for future checks.
|
42
|
-
def record_build_metadata(
|
46
|
+
def record_build_metadata(success, build)
|
43
47
|
config.build_cache_dir.mkpath
|
44
|
-
build.with_result(
|
48
|
+
last_build_path.write build.with_result(success).to_json
|
45
49
|
end
|
46
50
|
|
47
51
|
# Internal: The file path where metadata of the last build is stored.
|
48
|
-
def last_build_path
|
49
|
-
config.build_cache_dir.join("last
|
52
|
+
def last_build_path
|
53
|
+
config.build_cache_dir.join("last-build-#{ config.mode }.json")
|
50
54
|
end
|
51
55
|
|
52
56
|
# Internal: Returns a digest of all the watched files, allowing to detect
|
53
57
|
# changes, and skip Vite builds if no files have changed.
|
54
58
|
def watched_files_digest
|
55
|
-
|
56
|
-
|
57
|
-
config.within_root do
|
59
|
+
Dir.chdir File.expand_path(config.root) do
|
58
60
|
files = Dir[*config.watched_paths].reject { |f| File.directory?(f) }
|
59
61
|
file_ids = files.sort.map { |f| "#{ File.basename(f) }/#{ Digest::SHA1.file(f).hexdigest }" }
|
60
|
-
|
61
|
-
@last_digest = Digest::SHA1.hexdigest(file_ids.join('/'))
|
62
|
+
Digest::SHA1.hexdigest(file_ids.join('/'))
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
65
66
|
# Public: Initiates a Vite build command to generate assets.
|
67
|
+
#
|
68
|
+
# Returns true if the build is successful, or false if it failed.
|
66
69
|
def build_with_vite(*args)
|
67
70
|
logger.info 'Building with Vite ⚡️'
|
68
71
|
|
69
|
-
run(['build', *args])
|
72
|
+
stdout, stderr, status = run(['build', *args])
|
73
|
+
log_build_result(stdout, stderr.to_s, status)
|
74
|
+
|
75
|
+
status.success?
|
70
76
|
end
|
71
77
|
|
72
78
|
# Internal: Outputs the build results.
|
@@ -75,10 +81,9 @@ private
|
|
75
81
|
def log_build_result(_stdout, stderr, status)
|
76
82
|
if status.success?
|
77
83
|
logger.info "Build with Vite complete: #{ config.build_output_dir }"
|
78
|
-
logger.error stderr unless stderr.empty?
|
84
|
+
logger.error stderr.to_s unless stderr.empty?
|
79
85
|
else
|
80
86
|
logger.error stderr
|
81
|
-
logger.error status
|
82
87
|
logger.error 'Build with Vite failed! ❌'
|
83
88
|
logger.error '❌ Check that vite and vite-plugin-ruby are in devDependencies and have been installed. ' if stderr.include?('ERR! canceled')
|
84
89
|
end
|
data/lib/vite_ruby/cli/build.rb
CHANGED
@@ -5,10 +5,8 @@ class ViteRuby::CLI::Build < ViteRuby::CLI::Vite
|
|
5
5
|
|
6
6
|
desc 'Bundle all entrypoints using Vite.'
|
7
7
|
shared_options
|
8
|
-
option(:ssr, desc: 'Build the SSR entrypoint instead', type: :boolean)
|
9
8
|
option(:force, desc: 'Force the build even if assets have not changed', type: :boolean)
|
10
9
|
option(:watch, desc: 'Start the Rollup watcher and rebuild on files changes', type: :boolean)
|
11
|
-
option(:profile, desc: 'Gather performance metrics from the build ', type: :boolean)
|
12
10
|
|
13
11
|
def call(**options)
|
14
12
|
super { |args| ViteRuby.commands.build_from_task(*args) }
|
@@ -1,16 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'stringio'
|
4
|
-
require 'json'
|
5
4
|
|
6
5
|
class ViteRuby::CLI::Install < Dry::CLI::Command
|
7
6
|
desc 'Performs the initial configuration setup to get started with Vite Ruby.'
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
def call(package_manager: nil, **)
|
12
|
-
ENV['VITE_RUBY_PACKAGE_MANAGER'] ||= package_manager if package_manager
|
13
|
-
|
8
|
+
def call(**)
|
14
9
|
$stdout.sync = true
|
15
10
|
|
16
11
|
say 'Creating binstub'
|
@@ -84,20 +79,9 @@ private
|
|
84
79
|
# Internal: Installs vite and vite-plugin-ruby at the project level.
|
85
80
|
def install_js_dependencies
|
86
81
|
package_json = root.join('package.json')
|
87
|
-
unless package_json.exist?
|
88
|
-
|
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(' ')
|
82
|
+
write(package_json, '{}') unless package_json.exist?
|
83
|
+
deps = js_dependencies.join(' ')
|
84
|
+
run_with_capture("#{ npm_install } -D #{ deps }", stdin_data: "\n")
|
101
85
|
end
|
102
86
|
|
103
87
|
# Internal: Adds compilation output dirs to git ignore.
|
@@ -107,7 +91,9 @@ private
|
|
107
91
|
append(gitignore_file, <<~GITIGNORE)
|
108
92
|
|
109
93
|
# Vite Ruby
|
110
|
-
/public/vite
|
94
|
+
/public/vite
|
95
|
+
/public/vite-dev
|
96
|
+
/public/vite-test
|
111
97
|
node_modules
|
112
98
|
# Vite uses dotenv and suggests to ignore local-only env files. See
|
113
99
|
# https://vitejs.dev/guide/env-and-mode.html#env-files
|
@@ -127,12 +113,16 @@ private
|
|
127
113
|
def run_with_capture(*args, **options)
|
128
114
|
Dir.chdir(root) do
|
129
115
|
_, stderr, status = ViteRuby::IO.capture(*args, **options)
|
130
|
-
say(stderr) unless status.success? || stderr.empty?
|
116
|
+
say(stderr) unless status.success? || stderr.to_s.empty?
|
131
117
|
end
|
132
118
|
end
|
133
119
|
|
134
|
-
|
135
|
-
|
120
|
+
# Internal: Support all popular package managers.
|
121
|
+
def npm_install
|
122
|
+
return 'yarn add' if root.join('yarn.lock').exist?
|
123
|
+
return 'pnpm install' if root.join('pnpm-lock.yaml').exist?
|
124
|
+
|
125
|
+
'npm install'
|
136
126
|
end
|
137
127
|
|
138
128
|
# Internal: Avoid printing warning about missing vite.json, we will create one.
|
@@ -144,3 +134,8 @@ private
|
|
144
134
|
$stderr = old_stderr
|
145
135
|
end
|
146
136
|
end
|
137
|
+
|
138
|
+
# NOTE: This allows framework-specific variants to extend the installation.
|
139
|
+
ViteRuby.framework_libraries.each do |_framework, library|
|
140
|
+
require "#{ library.name.tr('-', '/') }/installation"
|
141
|
+
end
|
data/lib/vite_ruby/cli/vite.rb
CHANGED
@@ -3,33 +3,18 @@
|
|
3
3
|
class ViteRuby::CLI::Vite < Dry::CLI::Command
|
4
4
|
CURRENT_ENV = ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
5
5
|
|
6
|
-
def self.
|
6
|
+
def self.shared_options
|
7
7
|
option(:mode, default: self::DEFAULT_ENV, values: %w[development production test], aliases: ['m'], desc: 'The build mode for Vite')
|
8
|
-
option(:
|
8
|
+
option(:clobber, desc: 'Clear cache and previous builds', type: :boolean, aliases: %w[clean clear])
|
9
|
+
option(:debug, desc: 'Run Vite in verbose mode, printing all debugging output', aliases: ['verbose'], type: :boolean)
|
9
10
|
option(:inspect, desc: 'Run Vite in a debugging session with node --inspect-brk', aliases: ['inspect-brk'], type: :boolean)
|
10
11
|
option(:trace_deprecation, desc: 'Run Vite in debugging mode with node --trace-deprecation', aliases: ['trace-deprecation'], type: :boolean)
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
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)
|
14
|
+
def call(mode:, args: [], clobber: false, **boolean_opts)
|
20
15
|
ViteRuby.env['VITE_RUBY_MODE'] = mode
|
21
16
|
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
17
|
boolean_opts.map { |name, value| args << "--#{ name }" if value }
|
32
|
-
|
33
18
|
yield(args)
|
34
19
|
end
|
35
20
|
end
|
data/lib/vite_ruby/cli.rb
CHANGED
@@ -11,20 +11,7 @@ class ViteRuby::CLI
|
|
11
11
|
register 'clobber', Clobber, aliases: %w[clean clear]
|
12
12
|
register 'dev', Dev, aliases: %w[d serve]
|
13
13
|
register 'install', Install
|
14
|
-
register 'ssr', SSR
|
15
14
|
register 'version', Version, aliases: ['v', '-v', '--version', 'info']
|
16
15
|
register 'upgrade', Upgrade, aliases: ['update']
|
17
16
|
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
17
|
end
|
28
|
-
|
29
|
-
# NOTE: This allows framework-specific variants to extend the CLI.
|
30
|
-
ViteRuby::CLI.require_framework_libraries('cli')
|
data/lib/vite_ruby/commands.rb
CHANGED
@@ -23,25 +23,47 @@ 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
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.
|
@@ -68,7 +90,7 @@ class ViteRuby::Commands
|
|
68
90
|
|
69
91
|
# Internal: Prints information about ViteRuby's environment.
|
70
92
|
def print_info
|
71
|
-
config.
|
93
|
+
Dir.chdir(config.root) do
|
72
94
|
$stdout.puts "bin/vite present?: #{ File.exist? 'bin/vite' }"
|
73
95
|
|
74
96
|
$stdout.puts "vite_ruby: #{ ViteRuby::VERSION }"
|
@@ -77,11 +99,11 @@ class ViteRuby::Commands
|
|
77
99
|
$stdout.puts "#{ framework }: #{ Gem.loaded_specs[framework]&.version }"
|
78
100
|
end
|
79
101
|
|
80
|
-
$stdout.puts "ruby: #{ `ruby --version` }"
|
81
102
|
$stdout.puts "node: #{ `node --version` }"
|
82
|
-
|
83
|
-
|
84
|
-
$stdout.puts "
|
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`
|
@@ -98,6 +120,29 @@ 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
147
|
original = ENV['NODE_ENV']
|
103
148
|
ENV['NODE_ENV'] = env
|
@@ -24,7 +24,7 @@ module ViteRuby::CompatibilityCheck
|
|
24
24
|
raise ArgumentError, <<~ERROR
|
25
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
|
|
data/lib/vite_ruby/config.rb
CHANGED
@@ -5,10 +5,6 @@ require 'json'
|
|
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
9
|
https ? 'https' : 'http'
|
14
10
|
end
|
@@ -17,20 +13,14 @@ class ViteRuby::Config
|
|
17
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.
|
@@ -60,12 +50,12 @@ class ViteRuby::Config
|
|
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
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.
|
@@ -80,11 +70,6 @@ class ViteRuby::Config
|
|
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
|
@@ -92,11 +77,9 @@ private
|
|
92
77
|
def coerce_values(config)
|
93
78
|
config['mode'] = config['mode'].to_s
|
94
79
|
config['port'] = config['port'].to_i
|
95
|
-
config['root'] =
|
96
|
-
config['build_cache_dir'] = root.join(config['build_cache_dir'])
|
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)
|
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.
|
@@ -104,15 +87,6 @@ private
|
|
104
87
|
names.each { |name| config[name] = [true, 'true'].include?(config[name]) }
|
105
88
|
end
|
106
89
|
|
107
|
-
def detect_package_manager(root)
|
108
|
-
return 'npm' if root.join('package-lock.json').exist?
|
109
|
-
return 'pnpm' if root.join('pnpm-lock.yaml').exist?
|
110
|
-
return 'bun' if root.join('bun.lockb').exist?
|
111
|
-
return 'yarn' if root.join('yarn.lock').exist?
|
112
|
-
|
113
|
-
'npm'
|
114
|
-
end
|
115
|
-
|
116
90
|
def initialize(attrs)
|
117
91
|
@config = attrs.tap { |config| coerce_values(config) }.freeze
|
118
92
|
ViteRuby::CompatibilityCheck.verify_plugin_version(root) unless skip_compatibility_check
|
@@ -147,7 +121,7 @@ private
|
|
147
121
|
'config_path' => option_from_env('config_path') || DEFAULT_CONFIG.fetch('config_path'),
|
148
122
|
'mode' => option_from_env('mode') || mode,
|
149
123
|
'root' => option_from_env('root') || root,
|
150
|
-
}
|
124
|
+
}
|
151
125
|
end
|
152
126
|
|
153
127
|
# Internal: Used to load a JSON file from the specified path.
|
@@ -195,15 +169,12 @@ private
|
|
195
169
|
|
196
170
|
# Internal: If any of these files is modified the build won't be skipped.
|
197
171
|
DEFAULT_WATCHED_PATHS = %w[
|
198
|
-
bun.lockb
|
199
172
|
package-lock.json
|
200
173
|
package.json
|
201
174
|
pnpm-lock.yaml
|
202
175
|
postcss.config.js
|
203
176
|
tailwind.config.js
|
204
177
|
vite.config.js
|
205
|
-
vite.config.mjs
|
206
|
-
vite.config.mts
|
207
178
|
vite.config.ts
|
208
179
|
windi.config.ts
|
209
180
|
yarn.lock
|
@@ -38,7 +38,7 @@ private
|
|
38
38
|
uri
|
39
39
|
.sub(HOST_WITH_PORT_REGEX, '/') # Hanami adds the host and port.
|
40
40
|
.sub('.ts.js', '.ts') # Hanami's javascript helper always adds the extension.
|
41
|
-
.sub(
|
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)
|
@@ -54,22 +54,17 @@ private
|
|
54
54
|
|
55
55
|
def vite_should_handle?(env)
|
56
56
|
path = normalize_uri(env['PATH_INFO'])
|
57
|
-
return true if path.start_with?(
|
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
|
58
59
|
return true if file_in_vite_root?(path) # Fallback if Vite can serve the file
|
59
60
|
end
|
60
61
|
|
61
|
-
# NOTE: When using an empty 'public_output_dir', we need to rely on a
|
62
|
-
# filesystem check to check whether Vite should serve the request.
|
63
62
|
def file_in_vite_root?(path)
|
64
63
|
path.include?('.') && # Check for extension, avoid filesystem if possible.
|
65
64
|
config.vite_root_dir.join(path.start_with?('/') ? path[1..-1] : path).file?
|
66
65
|
end
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
# If the path starts with that prefix, it will be redirected to Vite.
|
72
|
-
def vite_url_prefix
|
73
|
-
@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 }/"
|
74
69
|
end
|
75
70
|
end
|
data/lib/vite_ruby/io.rb
CHANGED
data/lib/vite_ruby/manifest.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# lookup_entrypoint('calendar', type: :javascript)
|
8
8
|
# => { "file" => "/vite/assets/calendar-1016838bab065ae1e314.js", "imports" => [] }
|
9
9
|
#
|
10
|
-
# NOTE: Using
|
10
|
+
# NOTE: Using "autoBuild": true` in `config/vite.json` file will trigger a build
|
11
11
|
# on demand as needed, before performing any lookup.
|
12
12
|
class ViteRuby::Manifest
|
13
13
|
def initialize(vite_ruby)
|
@@ -22,16 +22,15 @@ class ViteRuby::Manifest
|
|
22
22
|
lookup!(name, **options).fetch('file')
|
23
23
|
end
|
24
24
|
|
25
|
-
# Public: Returns
|
25
|
+
# Public: Returns entries, imported modules, and stylesheets for the specified
|
26
26
|
# entrypoint files.
|
27
27
|
def resolve_entries(*names, **options)
|
28
28
|
entries = names.map { |name| lookup!(name, **options) }
|
29
|
-
script_paths = entries.map { |entry| entry.fetch('file') }
|
30
29
|
|
31
30
|
imports = dev_server_running? ? [] : entries.flat_map { |entry| entry['imports'] }.compact.uniq
|
32
31
|
{
|
33
|
-
|
34
|
-
imports: imports.map
|
32
|
+
main: entries.map(&TO_ENTRY),
|
33
|
+
imports: imports.map(&TO_ENTRY).uniq,
|
35
34
|
stylesheets: dev_server_running? ? [] : (entries + imports).flat_map { |entry| entry['css'] }.compact.uniq,
|
36
35
|
}
|
37
36
|
end
|
@@ -51,27 +50,21 @@ class ViteRuby::Manifest
|
|
51
50
|
if dev_server_running?
|
52
51
|
<<~REACT_REFRESH
|
53
52
|
<script type="module">
|
54
|
-
#{
|
53
|
+
import RefreshRuntime from '#{ prefix_asset_with_host('@react-refresh') }'
|
54
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
55
|
+
window.$RefreshReg$ = () => {}
|
56
|
+
window.$RefreshSig$ = () => (type) => type
|
57
|
+
window.__vite_plugin_react_preamble_installed__ = true
|
55
58
|
</script>
|
56
59
|
REACT_REFRESH
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
|
-
# Public: Source script for the React Refresh plugin.
|
61
|
-
def react_preamble_code
|
62
|
-
if dev_server_running?
|
63
|
-
<<~REACT_PREAMBLE_CODE
|
64
|
-
import RefreshRuntime from '#{ prefix_asset_with_host('@react-refresh') }'
|
65
|
-
RefreshRuntime.injectIntoGlobalHook(window)
|
66
|
-
window.$RefreshReg$ = () => {}
|
67
|
-
window.$RefreshSig$ = () => (type) => type
|
68
|
-
window.__vite_plugin_react_preamble_installed__ = true
|
69
|
-
REACT_PREAMBLE_CODE
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
63
|
protected
|
74
64
|
|
65
|
+
# Internal: Returns a [src, attrs] entry.
|
66
|
+
TO_ENTRY = ->(entry) { [entry.fetch('file'), entry.slice('integrity').symbolize_keys] }
|
67
|
+
|
75
68
|
# Internal: Strict version of lookup.
|
76
69
|
#
|
77
70
|
# Returns a relative path for the asset, or raises an error if not found.
|
@@ -87,7 +80,7 @@ protected
|
|
87
80
|
# manifest.lookup('calendar.js')
|
88
81
|
# => { "file" => "/vite/assets/calendar-1016838bab065ae1e122.js", "imports" => [] }
|
89
82
|
def lookup(name, **options)
|
90
|
-
@build_mutex.synchronize { builder.build
|
83
|
+
@build_mutex.synchronize { builder.build } if should_build?
|
91
84
|
|
92
85
|
find_manifest_entry resolve_entry_name(name, **options)
|
93
86
|
end
|
@@ -128,24 +121,19 @@ private
|
|
128
121
|
|
129
122
|
# Internal: Loads and merges the manifest files, resolving the asset paths.
|
130
123
|
def load_manifest
|
131
|
-
files = config.
|
124
|
+
files = [config.manifest_path, config.assets_manifest_path].select(&:exist?)
|
132
125
|
files.map { |path| JSON.parse(path.read) }.inject({}, &:merge).tap(&method(:resolve_references))
|
133
126
|
end
|
134
127
|
|
135
128
|
# Internal: Scopes an asset to the output folder in public, as a path.
|
136
129
|
def prefix_vite_asset(path)
|
137
|
-
File.join(
|
130
|
+
File.join("/#{ config.public_output_dir }", path)
|
138
131
|
end
|
139
132
|
|
140
133
|
# Internal: Prefixes an asset with the `asset_host` for tags that do not use
|
141
134
|
# the framework tag helpers.
|
142
135
|
def prefix_asset_with_host(path)
|
143
|
-
File.join(
|
144
|
-
end
|
145
|
-
|
146
|
-
# Internal: The origin of assets managed by Vite.
|
147
|
-
def vite_asset_origin
|
148
|
-
config.origin if dev_server_running? && config.skip_proxy
|
136
|
+
File.join(config.asset_host || '/', config.public_output_dir, path)
|
149
137
|
end
|
150
138
|
|
151
139
|
# Internal: Resolves the paths that reference a manifest entry.
|
@@ -212,7 +200,7 @@ private
|
|
212
200
|
|
213
201
|
# Internal: Raises a detailed message when an entry is missing in the manifest.
|
214
202
|
def missing_entry_error(name, **options)
|
215
|
-
raise ViteRuby::MissingEntrypointError.new(
|
203
|
+
raise ViteRuby::MissingEntrypointError, OpenStruct.new(
|
216
204
|
file_name: resolve_entry_name(name, **options),
|
217
205
|
last_build: builder.last_build_metadata,
|
218
206
|
manifest: @manifest,
|