vite_ruby 1.0.0
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 +3 -0
- data/CONTRIBUTING.md +33 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/default.vite.json +17 -0
- data/exe/vite +10 -0
- data/lib/tasks/vite.rake +51 -0
- data/lib/vite_ruby.rb +125 -0
- data/lib/vite_ruby/builder.rb +103 -0
- data/lib/vite_ruby/cli.rb +14 -0
- data/lib/vite_ruby/cli/build.rb +18 -0
- data/lib/vite_ruby/cli/dev.rb +12 -0
- data/lib/vite_ruby/cli/install.rb +122 -0
- data/lib/vite_ruby/cli/version.rb +9 -0
- data/lib/vite_ruby/commands.rb +153 -0
- data/lib/vite_ruby/config.rb +152 -0
- data/lib/vite_ruby/dev_server_proxy.rb +62 -0
- data/lib/vite_ruby/manifest.rb +168 -0
- data/lib/vite_ruby/runner.rb +57 -0
- data/lib/vite_ruby/version.rb +5 -0
- data/templates/config/vite.config.ts +8 -0
- data/templates/config/vite.json +15 -0
- data/templates/entrypoints/application.js +8 -0
- metadata +153 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/proxy'
|
4
|
+
|
5
|
+
# Public: Allows to relay asset requests to the Vite development server.
|
6
|
+
class ViteRuby::DevServerProxy < Rack::Proxy
|
7
|
+
HOST_WITH_PORT_REGEX = %r{^(.+?)(:\d+)/}
|
8
|
+
VITE_DEPENDENCY_PREFIX = '/@'
|
9
|
+
|
10
|
+
def initialize(app = nil, options = {})
|
11
|
+
@vite_ruby = options.delete(:vite_ruby) || ViteRuby.instance
|
12
|
+
options[:streaming] = false if ViteRuby.mode == 'test' && !options.key?(:streaming)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
# Rack: Intercept asset requests and send them to the Vite server.
|
17
|
+
def perform_request(env)
|
18
|
+
if vite_should_handle?(env) && dev_server_running?
|
19
|
+
forward_to_vite_dev_server(env)
|
20
|
+
super(env)
|
21
|
+
else
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
extend Forwardable
|
29
|
+
|
30
|
+
def_delegators :@vite_ruby, :config, :dev_server_running?
|
31
|
+
|
32
|
+
def rewrite_uri_for_vite(env)
|
33
|
+
uri = env.fetch('REQUEST_URI') { [env['PATH_INFO'], env['QUERY_STRING']].reject { |str| str.to_s.strip.empty? }.join('?') }
|
34
|
+
.sub(vite_asset_url_prefix, '/')
|
35
|
+
.sub(HOST_WITH_PORT_REGEX, '/') # Hanami adds the host and port.
|
36
|
+
.sub('.ts.js', '.ts') # Hanami's javascript helper always adds the extension.
|
37
|
+
env['PATH_INFO'], env['QUERY_STRING'] = (env['REQUEST_URI'] = uri).split('?')
|
38
|
+
end
|
39
|
+
|
40
|
+
def forward_to_vite_dev_server(env)
|
41
|
+
rewrite_uri_for_vite(env)
|
42
|
+
env['HTTP_HOST'] = env['HTTP_X_FORWARDED_HOST'] = config.host
|
43
|
+
env['HTTP_X_FORWARDED_SERVER'] = config.host_with_port
|
44
|
+
env['HTTP_PORT'] = env['HTTP_X_FORWARDED_PORT'] = config.port.to_s
|
45
|
+
env['HTTP_X_FORWARDED_PROTO'] = env['HTTP_X_FORWARDED_SCHEME'] = config.protocol
|
46
|
+
env['HTTPS'] = env['HTTP_X_FORWARDED_SSL'] = 'off' unless config.https
|
47
|
+
env['SCRIPT_NAME'] = ''
|
48
|
+
end
|
49
|
+
|
50
|
+
def vite_should_handle?(env)
|
51
|
+
path, query, referer = env['PATH_INFO'], env['QUERY_STRING'], env['HTTP_REFERER']
|
52
|
+
return true if path.start_with?(vite_asset_url_prefix) # Vite asset
|
53
|
+
return true if path.start_with?(VITE_DEPENDENCY_PREFIX) # Packages and imports
|
54
|
+
return true if query&.start_with?('t=') # Hot Reload for a stylesheet
|
55
|
+
return true if query&.start_with?('import&') # Hot Reload for an imported entrypoint
|
56
|
+
return true if referer && URI.parse(referer).path.start_with?(vite_asset_url_prefix) # Entry imported from another entry.
|
57
|
+
end
|
58
|
+
|
59
|
+
def vite_asset_url_prefix
|
60
|
+
@vite_asset_url_prefix ||= "/#{ config.public_output_dir }/"
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Public: Registry for accessing resources managed by Vite, using a generated
|
4
|
+
# manifest file which maps entrypoint names to file paths.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
# lookup_entrypoint('calendar', type: :javascript)
|
8
|
+
# => { "file" => "/vite/assets/calendar-1016838bab065ae1e314.js", "imports" => [] }
|
9
|
+
#
|
10
|
+
# NOTE: Using "autoBuild": true` in `config/vite.json` file will trigger a build
|
11
|
+
# on demand as needed, before performing any lookup.
|
12
|
+
class ViteRuby::Manifest
|
13
|
+
class MissingEntryError < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(vite_ruby)
|
17
|
+
@vite_ruby = vite_ruby
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Returns the path for the specified Vite entrypoint file.
|
21
|
+
#
|
22
|
+
# Raises an error if the resource could not be found in the manifest.
|
23
|
+
def path_for(name, **options)
|
24
|
+
lookup!(name, **options).fetch('file')
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: Returns scripts, imported modules, and stylesheets for the specified
|
28
|
+
# entrypoint files.
|
29
|
+
def resolve_entries(*names, **options)
|
30
|
+
entries = names.map { |name| lookup!(name, **options) }
|
31
|
+
script_paths = entries.map { |entry| entry.fetch('file') }
|
32
|
+
|
33
|
+
imports = dev_server_running? ? [] : entries.flat_map { |entry| entry['imports'] }.compact.uniq
|
34
|
+
{
|
35
|
+
scripts: script_paths,
|
36
|
+
imports: imports.map { |entry| entry.fetch('file') }.uniq,
|
37
|
+
stylesheets: dev_server_running? ? [] : (entries + imports).flat_map { |entry| entry['css'] }.compact.uniq,
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: Refreshes the cached mappings by reading the updated manifest files.
|
42
|
+
def refresh
|
43
|
+
@manifest = load_manifest
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: The path from where the browser can download the Vite HMR client.
|
47
|
+
def vite_client_src
|
48
|
+
prefix_vite_asset('@vite/client') if dev_server_running?
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
# Internal: Strict version of lookup.
|
54
|
+
#
|
55
|
+
# Returns a relative path for the asset, or raises an error if not found.
|
56
|
+
def lookup!(*args, **options)
|
57
|
+
lookup(*args, **options) || missing_entry_error(*args, **options)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Internal: Computes the path for a given Vite asset using manifest.json.
|
61
|
+
#
|
62
|
+
# Returns a relative path, or nil if the asset is not found.
|
63
|
+
#
|
64
|
+
# Example:
|
65
|
+
# manifest.lookup('calendar.js')
|
66
|
+
# => { "file" => "/vite/assets/calendar-1016838bab065ae1e122.js", "imports" => [] }
|
67
|
+
def lookup(name, type: nil)
|
68
|
+
build if should_build?
|
69
|
+
|
70
|
+
find_manifest_entry(with_file_extension(name, type))
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
extend Forwardable
|
76
|
+
|
77
|
+
def_delegators :@vite_ruby, :config, :build, :dev_server_running?
|
78
|
+
|
79
|
+
# NOTE: Auto compilation is convenient when running tests, when the developer
|
80
|
+
# won't focus on the frontend, or when running the Vite server is not desired.
|
81
|
+
def should_build?
|
82
|
+
config.auto_build && !dev_server_running?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Internal: Finds the specified entry in the manifest.
|
86
|
+
def find_manifest_entry(name)
|
87
|
+
if dev_server_running?
|
88
|
+
{ 'file' => prefix_vite_asset(name.to_s) }
|
89
|
+
else
|
90
|
+
manifest[name.to_s]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Internal: The parsed data from manifest.json.
|
95
|
+
#
|
96
|
+
# NOTE: When using build-on-demand in development and testing, the manifest
|
97
|
+
# is reloaded automatically before each lookup, to ensure it's always fresh.
|
98
|
+
def manifest
|
99
|
+
return refresh if config.auto_build
|
100
|
+
|
101
|
+
@manifest ||= load_manifest
|
102
|
+
end
|
103
|
+
|
104
|
+
# Internal: Loads and merges the manifest files, resolving the asset paths.
|
105
|
+
def load_manifest
|
106
|
+
files = [config.manifest_path, config.assets_manifest_path].select(&:exist?)
|
107
|
+
files.map { |path| JSON.parse(path.read) }.inject({}, &:merge).tap(&method(:resolve_references))
|
108
|
+
end
|
109
|
+
|
110
|
+
# Internal: Scopes an asset to the output folder in public, as a path.
|
111
|
+
def prefix_vite_asset(path)
|
112
|
+
File.join("/#{ config.public_output_dir }", path)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Internal: Resolves the paths that reference a manifest entry.
|
116
|
+
def resolve_references(manifest)
|
117
|
+
manifest.each_value do |entry|
|
118
|
+
entry['file'] = prefix_vite_asset(entry['file'])
|
119
|
+
entry['css'] = entry['css'].map { |path| prefix_vite_asset(path) } if entry['css']
|
120
|
+
entry['imports']&.map! { |name| manifest.fetch(name) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Internal: Adds a file extension to the file name, unless it already has one.
|
125
|
+
def with_file_extension(name, entry_type)
|
126
|
+
return name unless File.extname(name.to_s).empty?
|
127
|
+
|
128
|
+
extension = extension_for_type(entry_type)
|
129
|
+
extension ? "#{ name }.#{ extension }" : name
|
130
|
+
end
|
131
|
+
|
132
|
+
# Internal: Allows to receive :javascript and :stylesheet as :type in helpers.
|
133
|
+
def extension_for_type(entry_type)
|
134
|
+
case entry_type
|
135
|
+
when :javascript then 'js'
|
136
|
+
when :stylesheet then 'css'
|
137
|
+
when :typescript then 'ts'
|
138
|
+
else entry_type
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Internal: Raises a detailed message when an entry is missing in the manifest.
|
143
|
+
def missing_entry_error(name, type: nil, **_options)
|
144
|
+
file_name = with_file_extension(name, type)
|
145
|
+
raise ViteRuby::Manifest::MissingEntryError, <<~MSG
|
146
|
+
Vite Ruby can't find #{ file_name } in #{ config.manifest_path } or #{ config.assets_manifest_path }.
|
147
|
+
|
148
|
+
Possible causes:
|
149
|
+
#{ missing_entry_causes.map { |cause| "\t- #{ cause }" }.join("\n") }
|
150
|
+
|
151
|
+
Content in your manifests:
|
152
|
+
#{ JSON.pretty_generate(@manifest) }
|
153
|
+
MSG
|
154
|
+
end
|
155
|
+
|
156
|
+
def missing_entry_causes
|
157
|
+
local = config.auto_build
|
158
|
+
[
|
159
|
+
(dev_server_running? && 'Vite has not yet re-built your latest changes.'),
|
160
|
+
(local && !dev_server_running? && "\"autoBuild\": false in your #{ config.mode } configuration."),
|
161
|
+
(local && !dev_server_running? && 'The Vite development server has crashed or is no longer available.'),
|
162
|
+
'You have misconfigured config/vite.json file.',
|
163
|
+
(!local && 'Assets have not been precompiled'),
|
164
|
+
].select(&:itself)
|
165
|
+
rescue StandardError
|
166
|
+
[]
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
# Public: Executes Vite commands, providing conveniences for debugging.
|
6
|
+
class ViteRuby::Runner
|
7
|
+
# Public: Executes Vite with the specified arguments.
|
8
|
+
def run(argv, **options)
|
9
|
+
$stdout.sync = true
|
10
|
+
detect_unsupported_switches!(argv)
|
11
|
+
execute_command(argv.clone, **options)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
UNSUPPORTED_SWITCHES = %w[--host --port --https --root -r --config -c].freeze
|
17
|
+
|
18
|
+
# Internal: Allows to prevent configuration mistakes by ensuring the Ruby app
|
19
|
+
# and vite-plugin-ruby are using the same configuration for the dev server.
|
20
|
+
def detect_unsupported_switches!(args)
|
21
|
+
return unless (unsupported = UNSUPPORTED_SWITCHES & args).any?
|
22
|
+
|
23
|
+
$stdout.puts "Please set the following switches in your vite.json instead: #{ unsupported }."
|
24
|
+
exit!
|
25
|
+
end
|
26
|
+
|
27
|
+
# Internal: Executes the command with the specified arguments.
|
28
|
+
def execute_command(args, capture: false)
|
29
|
+
if capture
|
30
|
+
Open3.capture3(*command_for(args), chdir: ViteRuby.config.root)
|
31
|
+
else
|
32
|
+
Dir.chdir(ViteRuby.config.root) { Kernel.exec(*command_for(args)) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Internal: Returns an Array with the command to run.
|
37
|
+
def command_for(args)
|
38
|
+
[vite_env].tap do |cmd|
|
39
|
+
cmd.append('node', '--inspect-brk') if args.include?('--debug')
|
40
|
+
cmd.append('node', '--trace-deprecation') if args.delete('--trace-deprecation')
|
41
|
+
cmd.append(*vite_executable(cmd))
|
42
|
+
cmd.append(*args)
|
43
|
+
cmd.append('--mode', ViteRuby.mode) unless args.include?('--mode') || args.include?('-m')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: The environment variables to pass to the command.
|
48
|
+
def vite_env
|
49
|
+
ViteRuby.config.to_env
|
50
|
+
end
|
51
|
+
|
52
|
+
# Internal: Resolves to an executable for Vite.
|
53
|
+
def vite_executable(cmd)
|
54
|
+
npx = cmd.include?('node') ? `which npx`.chomp("\n") : 'npx' rescue 'npx'
|
55
|
+
[npx, 'vite']
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
// To see this message, follow the instructions for your Ruby framework.
|
2
|
+
//
|
3
|
+
// When using a plain API, perhaps it's better to generate an HTML entrypoint
|
4
|
+
// and link to the scripts and stylesheets, and let Vite transform it.
|
5
|
+
console.log('Vite ⚡️ Ruby')
|
6
|
+
|
7
|
+
// Example: Import a stylesheet in <sourceCodeDir>/index.css
|
8
|
+
// import '~/index.css'
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vite_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Máximo Mussini
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-cli
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-proxy
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.6.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.6.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: zeitwerk
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-performance
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- maximomussini@gmail.com
|
100
|
+
executables:
|
101
|
+
- vite
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- CHANGELOG.md
|
106
|
+
- CONTRIBUTING.md
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- default.vite.json
|
110
|
+
- exe/vite
|
111
|
+
- lib/tasks/vite.rake
|
112
|
+
- lib/vite_ruby.rb
|
113
|
+
- lib/vite_ruby/builder.rb
|
114
|
+
- lib/vite_ruby/cli.rb
|
115
|
+
- lib/vite_ruby/cli/build.rb
|
116
|
+
- lib/vite_ruby/cli/dev.rb
|
117
|
+
- lib/vite_ruby/cli/install.rb
|
118
|
+
- lib/vite_ruby/cli/version.rb
|
119
|
+
- lib/vite_ruby/commands.rb
|
120
|
+
- lib/vite_ruby/config.rb
|
121
|
+
- lib/vite_ruby/dev_server_proxy.rb
|
122
|
+
- lib/vite_ruby/manifest.rb
|
123
|
+
- lib/vite_ruby/runner.rb
|
124
|
+
- lib/vite_ruby/version.rb
|
125
|
+
- templates/config/vite.config.ts
|
126
|
+
- templates/config/vite.json
|
127
|
+
- templates/entrypoints/application.js
|
128
|
+
homepage: https://github.com/ElMassimo/vite_rails
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata:
|
132
|
+
source_code_uri: https://github.com/ElMassimo/vite_rails/tree/vite_ruby@1.0.0/vite_ruby
|
133
|
+
changelog_uri: https://github.com/ElMassimo/vite_rails/blob/vite_ruby@1.0.0/vite_ruby/CHANGELOG.md
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '2.5'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubygems_version: 3.1.4
|
150
|
+
signing_key:
|
151
|
+
specification_version: 4
|
152
|
+
summary: Use Vite in Ruby and bring joy to your JavaScript experience
|
153
|
+
test_files: []
|