vite_ruby 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|