vite_ruby 3.7.0 → 4.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(ssr: args.include?('--ssr'))
15
-
16
- if args.delete('--force') || last_build.stale? || config.manifest_paths.empty?
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(ssr: false)
32
- ViteRuby::Build.from_previous(last_build_path(ssr: ssr), watched_files_digest)
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(build, **attrs)
46
+ def record_build_metadata(success, build)
43
47
  config.build_cache_dir.mkpath
44
- build.with_result(**attrs).write_to_cache
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(ssr:)
49
- config.build_cache_dir.join("last#{ '-ssr' if ssr }-build-#{ config.mode }.json")
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
- return @last_digest if @last_digest_at && Time.now - @last_digest_at < 1
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
- @last_digest_at = Time.now
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
@@ -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
- 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
-
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
- 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(' ')
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
- def install_js_packages(deps)
135
- run_with_capture("#{ config.package_manager } add -D #{ deps }", stdin_data: "\n")
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
@@ -5,6 +5,7 @@ class ViteRuby::CLI::UpgradePackages < ViteRuby::CLI::Install
5
5
 
6
6
  def call(**)
7
7
  say 'Upgrading npm packages'
8
- install_js_packages js_dependencies.join(' ')
8
+ deps = js_dependencies.join(' ')
9
+ run_with_capture("#{ npm_install } -D #{ deps }")
9
10
  end
10
11
  end
@@ -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.executable_options
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(:node_options, desc: 'Node options for the Vite executable', aliases: ['node-options'])
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 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)
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')
@@ -23,7 +23,7 @@ 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.ssr_output_dir, config.build_cache_dir, config.vite_cache_dir]
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
@@ -50,7 +50,7 @@ class ViteRuby::Commands
50
50
 
51
51
  versions
52
52
  .each_with_index
53
- .drop_while { |(mtime, _files), index|
53
+ .drop_while { |(mtime, _), index|
54
54
  max_age = [0, Time.now - Time.at(mtime)].max
55
55
  max_age < age_in_seconds || index < keep_up_to
56
56
  }
@@ -66,16 +66,6 @@ class ViteRuby::Commands
66
66
  `bundle config --delete bin`
67
67
  end
68
68
 
69
- # Internal: Checks if the npm version is 6 or lower.
70
- def legacy_npm_version?
71
- `npm --version`.to_i < 7 rescue false
72
- end
73
-
74
- # Internal: Checks if the yarn version is 1.x.
75
- def legacy_yarn_version?
76
- `yarn --version`.to_i < 2 rescue false
77
- end
78
-
79
69
  # Internal: Verifies if ViteRuby is properly installed.
80
70
  def verify_install
81
71
  unless File.exist?(config.root.join('bin/vite'))
@@ -100,7 +90,7 @@ class ViteRuby::Commands
100
90
 
101
91
  # Internal: Prints information about ViteRuby's environment.
102
92
  def print_info
103
- config.within_root do
93
+ Dir.chdir(config.root) do
104
94
  $stdout.puts "bin/vite present?: #{ File.exist? 'bin/vite' }"
105
95
 
106
96
  $stdout.puts "vite_ruby: #{ ViteRuby::VERSION }"
@@ -109,11 +99,11 @@ class ViteRuby::Commands
109
99
  $stdout.puts "#{ framework }: #{ Gem.loaded_specs[framework]&.version }"
110
100
  end
111
101
 
112
- $stdout.puts "ruby: #{ `ruby --version` }"
113
102
  $stdout.puts "node: #{ `node --version` }"
114
-
115
- pkg = config.package_manager
116
- $stdout.puts "#{ pkg }: #{ `#{ pkg } --version` rescue nil }"
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` }"
117
107
 
118
108
  $stdout.puts "\n"
119
109
  packages = `npm ls vite vite-plugin-ruby`
@@ -131,7 +121,7 @@ private
131
121
  def_delegators :@vite_ruby, :config, :builder, :manifest, :logger, :logger=
132
122
 
133
123
  def may_clean?
134
- config.build_output_dir.exist? && config.manifest_paths.any?
124
+ config.build_output_dir.exist? && config.manifest_path.exist?
135
125
  end
136
126
 
137
127
  def clean_files(files)
@@ -143,16 +133,14 @@ private
143
133
 
144
134
  def versions
145
135
  all_files = Dir.glob("#{ config.build_output_dir }/**/*")
146
- entries = all_files - config.manifest_paths - files_referenced_in_manifests
136
+ entries = all_files - [config.manifest_path] - current_version_files
147
137
  entries.reject { |file| File.directory?(file) }
148
138
  .group_by { |file| File.mtime(file).utc.to_i }
149
139
  .sort.reverse
150
140
  end
151
141
 
152
- def files_referenced_in_manifests
153
- config.manifest_paths.flat_map { |path|
154
- JSON.parse(path.read).map { |_, entry| entry['file'] }
155
- }.compact.uniq.map { |path| config.build_output_dir.join(path).to_s }
142
+ def current_version_files
143
+ Dir.glob(manifest.refresh.values.map { |value| config.build_output_dir.join("#{ value['file'] }*") })
156
144
  end
157
145
 
158
146
  def with_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/#skipcompatibilitycheck
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
 
@@ -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,24 +13,14 @@ class ViteRuby::Config
17
13
  "#{ host }:#{ port }"
18
14
  end
19
15
 
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
- 'manifest.json',
25
- # NOTE: Path where vite-plugin-ruby outputs the assets manifest file.
26
- 'manifest-assets.json',
27
- ].flat_map { |path|
28
- [
29
- build_output_dir.join(".vite/#{ path }"), # Vite 5 onwards
30
- build_output_dir.join(path), # Vite 4 and below
31
- ]
32
- }
16
+ # Internal: Path where Vite outputs the manifest file.
17
+ def manifest_path
18
+ build_output_dir.join('manifest.json')
33
19
  end
34
20
 
35
- # Internal: Path to the manifest files generated by Vite and vite-plugin-ruby.
36
- def manifest_paths
37
- known_manifest_paths.select(&:exist?)
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')
38
24
  end
39
25
 
40
26
  # Public: The directory where Vite will store the built assets.
@@ -64,12 +50,12 @@ class ViteRuby::Config
64
50
  end
65
51
 
66
52
  # Public: Sets additional environment variables for vite-plugin-ruby.
67
- def to_env(env_vars = ViteRuby.env)
53
+ def to_env
68
54
  CONFIGURABLE_WITH_ENV.each_with_object({}) do |option, env|
69
55
  unless (value = @config[option]).nil?
70
56
  env["#{ ViteRuby::ENV_PREFIX }_#{ option.upcase }"] = value.to_s
71
57
  end
72
- end.merge(env_vars)
58
+ end.merge(ViteRuby.env)
73
59
  end
74
60
 
75
61
  # Internal: Files and directories that should be watched for changes.
@@ -84,11 +70,6 @@ class ViteRuby::Config
84
70
  ].freeze
85
71
  end
86
72
 
87
- # Internal: Changes the current directory to the root dir.
88
- def within_root(&block)
89
- Dir.chdir(File.expand_path(root), &block)
90
- end
91
-
92
73
  private
93
74
 
94
75
  # Internal: Coerces all the configuration values, in case they were passed
@@ -96,11 +77,9 @@ private
96
77
  def coerce_values(config)
97
78
  config['mode'] = config['mode'].to_s
98
79
  config['port'] = config['port'].to_i
99
- config['root'] = root = Pathname.new(config['root'])
100
- config['build_cache_dir'] = root.join(config['build_cache_dir'])
101
- config['ssr_output_dir'] = root.join(config['ssr_output_dir'])
102
- coerce_booleans(config, 'auto_build', 'hide_build_console_output', 'https', 'skip_compatibility_check', 'skip_proxy')
103
- 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')
104
83
  end
105
84
 
106
85
  # Internal: Coerces configuration options to boolean.
@@ -108,15 +87,6 @@ private
108
87
  names.each { |name| config[name] = [true, 'true'].include?(config[name]) }
109
88
  end
110
89
 
111
- def detect_package_manager(root)
112
- return 'npm' if root.join('package-lock.json').exist?
113
- return 'pnpm' if root.join('pnpm-lock.yaml').exist?
114
- return 'bun' if root.join('bun.lockb').exist?
115
- return 'yarn' if root.join('yarn.lock').exist?
116
-
117
- 'npm'
118
- end
119
-
120
90
  def initialize(attrs)
121
91
  @config = attrs.tap { |config| coerce_values(config) }.freeze
122
92
  ViteRuby::CompatibilityCheck.verify_plugin_version(root) unless skip_compatibility_check
@@ -151,7 +121,7 @@ private
151
121
  'config_path' => option_from_env('config_path') || DEFAULT_CONFIG.fetch('config_path'),
152
122
  'mode' => option_from_env('mode') || mode,
153
123
  'root' => option_from_env('root') || root,
154
- }.select { |_, value| value }
124
+ }
155
125
  end
156
126
 
157
127
  # Internal: Used to load a JSON file from the specified path.
@@ -199,15 +169,12 @@ private
199
169
 
200
170
  # Internal: If any of these files is modified the build won't be skipped.
201
171
  DEFAULT_WATCHED_PATHS = %w[
202
- bun.lockb
203
172
  package-lock.json
204
173
  package.json
205
174
  pnpm-lock.yaml
206
175
  postcss.config.js
207
176
  tailwind.config.js
208
177
  vite.config.js
209
- vite.config.mjs
210
- vite.config.mts
211
178
  vite.config.ts
212
179
  windi.config.ts
213
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(/\.(sass|scss|styl|stylus|less|pcss|postcss)\.css$/, '.\1') # Rails' stylesheet_link_tag 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)
@@ -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?(vite_url_prefix) # Vite asset
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
- # NOTE: Vite is configured to use 'public_output_dir' as the base, which can
69
- # be customized by the user in development to not match any of the routes.
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
@@ -15,7 +15,7 @@ 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.to_s, wait_threads.value]
18
+ [out.value, err.value, wait_threads.value]
19
19
  }
20
20
  end
21
21
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ostruct'
4
-
5
3
  # Public: Registry for accessing resources managed by Vite, using a generated
6
4
  # manifest file which maps entrypoint names to file paths.
7
5
  #
@@ -9,7 +7,7 @@ require 'ostruct'
9
7
  # lookup_entrypoint('calendar', type: :javascript)
10
8
  # => { "file" => "/vite/assets/calendar-1016838bab065ae1e314.js", "imports" => [] }
11
9
  #
12
- # NOTE: Using `"autoBuild": true` in `config/vite.json` file will trigger a build
10
+ # NOTE: Using "autoBuild": true` in `config/vite.json` file will trigger a build
13
11
  # on demand as needed, before performing any lookup.
14
12
  class ViteRuby::Manifest
15
13
  def initialize(vite_ruby)
@@ -24,16 +22,15 @@ class ViteRuby::Manifest
24
22
  lookup!(name, **options).fetch('file')
25
23
  end
26
24
 
27
- # Public: Returns scripts, imported modules, and stylesheets for the specified
25
+ # Public: Returns entries, imported modules, and stylesheets for the specified
28
26
  # entrypoint files.
29
27
  def resolve_entries(*names, **options)
30
28
  entries = names.map { |name| lookup!(name, **options) }
31
- script_paths = entries.map { |entry| entry.fetch('file') }
32
29
 
33
30
  imports = dev_server_running? ? [] : entries.flat_map { |entry| entry['imports'] }.compact.uniq
34
31
  {
35
- scripts: script_paths,
36
- imports: imports.map { |entry| entry.fetch('file') }.uniq,
32
+ main: entries.map(&TO_ENTRY),
33
+ imports: imports.map(&TO_ENTRY).uniq,
37
34
  stylesheets: dev_server_running? ? [] : (entries + imports).flat_map { |entry| entry['css'] }.compact.uniq,
38
35
  }
39
36
  end
@@ -53,27 +50,21 @@ class ViteRuby::Manifest
53
50
  if dev_server_running?
54
51
  <<~REACT_REFRESH
55
52
  <script type="module">
56
- #{ react_preamble_code }
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
57
58
  </script>
58
59
  REACT_REFRESH
59
60
  end
60
61
  end
61
62
 
62
- # Public: Source script for the React Refresh plugin.
63
- def react_preamble_code
64
- if dev_server_running?
65
- <<~REACT_PREAMBLE_CODE
66
- import RefreshRuntime from '#{ prefix_asset_with_host('@react-refresh') }'
67
- RefreshRuntime.injectIntoGlobalHook(window)
68
- window.$RefreshReg$ = () => {}
69
- window.$RefreshSig$ = () => (type) => type
70
- window.__vite_plugin_react_preamble_installed__ = true
71
- REACT_PREAMBLE_CODE
72
- end
73
- end
74
-
75
63
  protected
76
64
 
65
+ # Internal: Returns a [src, attrs] entry.
66
+ TO_ENTRY = ->(entry) { [entry.fetch('file'), entry.slice('integrity').symbolize_keys] }
67
+
77
68
  # Internal: Strict version of lookup.
78
69
  #
79
70
  # Returns a relative path for the asset, or raises an error if not found.
@@ -89,7 +80,7 @@ protected
89
80
  # manifest.lookup('calendar.js')
90
81
  # => { "file" => "/vite/assets/calendar-1016838bab065ae1e122.js", "imports" => [] }
91
82
  def lookup(name, **options)
92
- @build_mutex.synchronize { builder.build || (return nil) } if should_build?
83
+ @build_mutex.synchronize { builder.build } if should_build?
93
84
 
94
85
  find_manifest_entry resolve_entry_name(name, **options)
95
86
  end
@@ -130,24 +121,19 @@ private
130
121
 
131
122
  # Internal: Loads and merges the manifest files, resolving the asset paths.
132
123
  def load_manifest
133
- files = config.manifest_paths
124
+ files = [config.manifest_path, config.assets_manifest_path].select(&:exist?)
134
125
  files.map { |path| JSON.parse(path.read) }.inject({}, &:merge).tap(&method(:resolve_references))
135
126
  end
136
127
 
137
128
  # Internal: Scopes an asset to the output folder in public, as a path.
138
129
  def prefix_vite_asset(path)
139
- File.join(vite_asset_origin || '/', config.public_output_dir, path)
130
+ File.join("/#{ config.public_output_dir }", path)
140
131
  end
141
132
 
142
133
  # Internal: Prefixes an asset with the `asset_host` for tags that do not use
143
134
  # the framework tag helpers.
144
135
  def prefix_asset_with_host(path)
145
- File.join(vite_asset_origin || config.asset_host || '/', config.public_output_dir, path)
146
- end
147
-
148
- # Internal: The origin of assets managed by Vite.
149
- def vite_asset_origin
150
- config.origin if dev_server_running? && config.skip_proxy
136
+ File.join(config.asset_host || '/', config.public_output_dir, path)
151
137
  end
152
138
 
153
139
  # Internal: Resolves the paths that reference a manifest entry.