minivite_rails 0.1.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/.rspec +3 -0
- data/.rubocop.yml +7 -0
- data/.vscode/settings.json +6 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +126 -0
- data/LICENSE.txt +21 -0
- data/README.md +201 -0
- data/Rakefile +12 -0
- data/lib/minivite_rails/configuration.rb +79 -0
- data/lib/minivite_rails/manifest.rb +170 -0
- data/lib/minivite_rails/tag_helpers.rb +104 -0
- data/lib/minivite_rails/version.rb +5 -0
- data/lib/minivite_rails.rb +29 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 346d0e1dda504de6fe45499ad41cbc3d1561aea6be67b7703c4a4beb67df27c5
|
4
|
+
data.tar.gz: f9f7fa0218f21622446b89a095d6d5ec4631ff4df8cd216172673c209d83c92c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 33b98d69cca67c961f6af9efadd61499b71f2e97b0585275fa6c1d2d67092bd1c7ae592f4ee356fead6d9acb5a518c94812ef115a2441b2dffd7e2a72e79adca
|
7
|
+
data.tar.gz: 7d7b59b04ed4f778b9fa079eac169af790802d40c89f01d273f5eb85bc1faa0a6b76396da051eb1ac10024fbfe3553b424ea7d07aefe4fe71c257b8d389dce4d
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
minivite_rails (0.1.0)
|
5
|
+
actionview (>= 6.0.0)
|
6
|
+
railties (>= 6.0.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionpack (7.0.4.2)
|
12
|
+
actionview (= 7.0.4.2)
|
13
|
+
activesupport (= 7.0.4.2)
|
14
|
+
rack (~> 2.0, >= 2.2.0)
|
15
|
+
rack-test (>= 0.6.3)
|
16
|
+
rails-dom-testing (~> 2.0)
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
18
|
+
actionview (7.0.4.2)
|
19
|
+
activesupport (= 7.0.4.2)
|
20
|
+
builder (~> 3.1)
|
21
|
+
erubi (~> 1.4)
|
22
|
+
rails-dom-testing (~> 2.0)
|
23
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
24
|
+
activesupport (7.0.4.2)
|
25
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
26
|
+
i18n (>= 1.6, < 2)
|
27
|
+
minitest (>= 5.1)
|
28
|
+
tzinfo (~> 2.0)
|
29
|
+
ast (2.4.2)
|
30
|
+
builder (3.2.4)
|
31
|
+
concurrent-ruby (1.2.0)
|
32
|
+
crass (1.0.6)
|
33
|
+
debug (1.7.1)
|
34
|
+
irb (>= 1.5.0)
|
35
|
+
reline (>= 0.3.1)
|
36
|
+
diff-lcs (1.5.0)
|
37
|
+
erubi (1.12.0)
|
38
|
+
i18n (1.12.0)
|
39
|
+
concurrent-ruby (~> 1.0)
|
40
|
+
io-console (0.6.0)
|
41
|
+
irb (1.6.2)
|
42
|
+
reline (>= 0.3.0)
|
43
|
+
json (2.6.3)
|
44
|
+
loofah (2.19.1)
|
45
|
+
crass (~> 1.0.2)
|
46
|
+
nokogiri (>= 1.5.9)
|
47
|
+
method_source (1.0.0)
|
48
|
+
minitest (5.17.0)
|
49
|
+
nokogiri (1.14.1-x86_64-darwin)
|
50
|
+
racc (~> 1.4)
|
51
|
+
parallel (1.22.1)
|
52
|
+
parser (3.2.0.0)
|
53
|
+
ast (~> 2.4.1)
|
54
|
+
racc (1.6.2)
|
55
|
+
rack (2.2.6.2)
|
56
|
+
rack-test (2.0.2)
|
57
|
+
rack (>= 1.3)
|
58
|
+
rails-dom-testing (2.0.3)
|
59
|
+
activesupport (>= 4.2.0)
|
60
|
+
nokogiri (>= 1.6)
|
61
|
+
rails-html-sanitizer (1.5.0)
|
62
|
+
loofah (~> 2.19, >= 2.19.1)
|
63
|
+
railties (7.0.4.2)
|
64
|
+
actionpack (= 7.0.4.2)
|
65
|
+
activesupport (= 7.0.4.2)
|
66
|
+
method_source
|
67
|
+
rake (>= 12.2)
|
68
|
+
thor (~> 1.0)
|
69
|
+
zeitwerk (~> 2.5)
|
70
|
+
rainbow (3.1.1)
|
71
|
+
rake (13.0.6)
|
72
|
+
regexp_parser (2.6.1)
|
73
|
+
reline (0.3.2)
|
74
|
+
io-console (~> 0.5)
|
75
|
+
rexml (3.2.5)
|
76
|
+
rspec (3.12.0)
|
77
|
+
rspec-core (~> 3.12.0)
|
78
|
+
rspec-expectations (~> 3.12.0)
|
79
|
+
rspec-mocks (~> 3.12.0)
|
80
|
+
rspec-core (3.12.0)
|
81
|
+
rspec-support (~> 3.12.0)
|
82
|
+
rspec-expectations (3.12.1)
|
83
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
84
|
+
rspec-support (~> 3.12.0)
|
85
|
+
rspec-mocks (3.12.1)
|
86
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
87
|
+
rspec-support (~> 3.12.0)
|
88
|
+
rspec-support (3.12.0)
|
89
|
+
rubocop (1.42.0)
|
90
|
+
json (~> 2.3)
|
91
|
+
parallel (~> 1.10)
|
92
|
+
parser (>= 3.1.2.1)
|
93
|
+
rainbow (>= 2.2.2, < 4.0)
|
94
|
+
regexp_parser (>= 1.8, < 3.0)
|
95
|
+
rexml (>= 3.2.5, < 4.0)
|
96
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
97
|
+
ruby-progressbar (~> 1.7)
|
98
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
99
|
+
rubocop-ast (1.24.1)
|
100
|
+
parser (>= 3.1.1.0)
|
101
|
+
rubocop-capybara (2.17.1)
|
102
|
+
rubocop (~> 1.41)
|
103
|
+
rubocop-rspec (2.18.1)
|
104
|
+
rubocop (~> 1.33)
|
105
|
+
rubocop-capybara (~> 2.17)
|
106
|
+
ruby-progressbar (1.11.0)
|
107
|
+
thor (1.2.1)
|
108
|
+
tzinfo (2.0.6)
|
109
|
+
concurrent-ruby (~> 1.0)
|
110
|
+
unicode-display_width (2.4.2)
|
111
|
+
zeitwerk (2.6.6)
|
112
|
+
|
113
|
+
PLATFORMS
|
114
|
+
x86_64-darwin-21
|
115
|
+
x86_64-darwin-22
|
116
|
+
|
117
|
+
DEPENDENCIES
|
118
|
+
debug (~> 1.0)
|
119
|
+
minivite_rails!
|
120
|
+
rake (~> 13.0)
|
121
|
+
rspec (~> 3.0)
|
122
|
+
rubocop (~> 1.21)
|
123
|
+
rubocop-rspec (~> 2.18)
|
124
|
+
|
125
|
+
BUNDLED WITH
|
126
|
+
2.4.6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Linh Tran
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
# MiniviteRails
|
2
|
+
|
3
|
+
MiniviteRails provides minimal integration with [Vite](https://vitejs.dev/) for Rails projects.
|
4
|
+
|
5
|
+
__DISCLAIMER:__ This gem is not thoroughly tested and is not yet ready for production use. For battle-tested and full featured integration with Vite for not only Rails but Ruby projects, please check out [vite_ruby](https://vite-ruby.netlify.app/). In fact, this gem reuses a lot of code from [vite_ruby](https://vite-ruby.netlify.app/) and is intended just for my projects' specific use cases that requires full manual control over Vite's configuration and build process.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* Rails view helpers to resolve paths to assets which are built by Vite (both by Vite's dev server and by Vite's build process).
|
10
|
+
* Support multiple configurations for Vite
|
11
|
+
|
12
|
+
### Notes
|
13
|
+
|
14
|
+
* MiniviteRails does not automatically install Vite and manage Vite's configuration. You must install Vite, its dependencies and manage Vite's configuration manually.
|
15
|
+
* It also does not provide any integration with Rails' asset pipeline. So you must use Vite to build all your assets.
|
16
|
+
|
17
|
+
If you need above features, please use [vite_ruby](https://vite-ruby.netlify.app/)
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'minivite_rails'
|
25
|
+
```
|
26
|
+
|
27
|
+
And then run:
|
28
|
+
|
29
|
+
```sh
|
30
|
+
$ bundle install
|
31
|
+
```
|
32
|
+
|
33
|
+
## Configuration
|
34
|
+
|
35
|
+
After installed, configure your Rails app below as a new file `config/initializers/minivite_rails.rb`.
|
36
|
+
|
37
|
+
```rb
|
38
|
+
MiniviteRails.configuration do |c|
|
39
|
+
# By default c.cache is set to `false`, which means an application always parses a
|
40
|
+
# manifest.json. In development, you should set cache false usually.
|
41
|
+
# Instead, setting it `true` which caches the manifest in memory is recommended basically.
|
42
|
+
c.cache = Rails.env.production?
|
43
|
+
|
44
|
+
# Register vite dev server address here, if you are using vite dev server.
|
45
|
+
# It will only be used in development mode.
|
46
|
+
c.vite_dev_server = 'http://127.0.0.1:5173'
|
47
|
+
|
48
|
+
# Vite base path, default will be `/vite`
|
49
|
+
# c.public_base_path = '/vite'
|
50
|
+
|
51
|
+
# Vite public directory, default will be `public`
|
52
|
+
# c.public_dir, default: 'public'
|
53
|
+
|
54
|
+
# Vite manifest file path, default will be `#{c.public_dir}#{c.public_base_path}/manifest.json`
|
55
|
+
# c.manifest_path = "#{c.public_dir}#{c.public_base_path}/manifest.json"
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
With the above default configuration, you are expected to have a Vite's configuration like below:
|
60
|
+
|
61
|
+
```js
|
62
|
+
import { fileURLToPath, URL } from 'node:url'
|
63
|
+
|
64
|
+
import { defineConfig } from 'vite'
|
65
|
+
import vue from '@vitejs/plugin-vue'
|
66
|
+
|
67
|
+
export default defineConfig({
|
68
|
+
base: '/vite',
|
69
|
+
plugins: [vue()],
|
70
|
+
build: {
|
71
|
+
manifest: true, // This is required
|
72
|
+
rollupOptions: {
|
73
|
+
input: fileURLToPath(new URL('./src/main.ts', import.meta.url)) // The entry file of your application, it will depends on your project structure
|
74
|
+
},
|
75
|
+
// For this example, the frontend code and vite configuration file are directly in a child folder from the root of the Rails project.
|
76
|
+
outDir: fileURLToPath(new URL('../public/vite', import.meta.url)),
|
77
|
+
emptyOutDir: true,
|
78
|
+
},
|
79
|
+
server: {
|
80
|
+
origin: 'http://127.0.0.1:5173' // For referencing assets from vite dev server
|
81
|
+
}
|
82
|
+
})
|
83
|
+
```
|
84
|
+
|
85
|
+
## Usage
|
86
|
+
|
87
|
+
### Rails view helpers
|
88
|
+
|
89
|
+
Use the following helpers in your Rails views to resolve paths to assets which are built by Vite.
|
90
|
+
|
91
|
+
### `vite_javascript_tag`
|
92
|
+
|
93
|
+
Renders a `<script>` tag for the specified Vite js entrypoints. This is the most commonly used helper and will also load needed css entrypoints.
|
94
|
+
|
95
|
+
```erb
|
96
|
+
<%= vite_javascript_tag 'src/main.js' %>
|
97
|
+
```
|
98
|
+
|
99
|
+
### `vite_typescript_tag`
|
100
|
+
|
101
|
+
Same as `vite_javascript_tag` but for typescript entrypoints
|
102
|
+
|
103
|
+
### `vite_stylesheet_tag`
|
104
|
+
|
105
|
+
Renders a `<link>` tag for the specified Vite css entrypoints
|
106
|
+
|
107
|
+
```erb
|
108
|
+
<%= vite_stylesheet_tag 'app.css' %>
|
109
|
+
```
|
110
|
+
|
111
|
+
### `vite_client_tag`
|
112
|
+
|
113
|
+
Render a script tag to load vite/client to enable HMR
|
114
|
+
|
115
|
+
```erb
|
116
|
+
<%= vite_client_tag %>
|
117
|
+
```
|
118
|
+
|
119
|
+
### `vite_react_refesth_tag`
|
120
|
+
|
121
|
+
Render a script tag to enable HMR with React Refresh
|
122
|
+
|
123
|
+
```erb
|
124
|
+
<%= vite_react_refesh_tag %>
|
125
|
+
```
|
126
|
+
|
127
|
+
### `vite_asset_path`
|
128
|
+
|
129
|
+
Render a path to a specified asset built by Vite
|
130
|
+
|
131
|
+
```erb
|
132
|
+
<%= vite_asset_path 'calender.js' %>
|
133
|
+
```
|
134
|
+
|
135
|
+
### `vite_asset_url`
|
136
|
+
|
137
|
+
Same as `vite_asset_path` but returns a full URL
|
138
|
+
|
139
|
+
### `vite_image_tag`
|
140
|
+
|
141
|
+
Renders an `<img>` tag for the specified Vite image asset
|
142
|
+
|
143
|
+
```erb
|
144
|
+
<%= vite_image_tag 'logo.png' %>
|
145
|
+
```
|
146
|
+
|
147
|
+
### Multiple configurations
|
148
|
+
|
149
|
+
Besides the main configuration, you can also register multiple configurations for Vite. For example, you may want to have a separate configuration for your admin panel.
|
150
|
+
|
151
|
+
```rb
|
152
|
+
MiniviteRails.configuration do |c|
|
153
|
+
# Main configuration
|
154
|
+
c.cache = Rails.env.production?
|
155
|
+
c.vite_dev_server = 'http://127.0.0.1:5173'
|
156
|
+
|
157
|
+
# Sub configuration for admin panel
|
158
|
+
c.add :admin do |sc|
|
159
|
+
sc.public_base_path = '/vite_admin'
|
160
|
+
sc.vite_dev_server = 'http://127.0.0.1:5174'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
And the corresponding Vite configuration for admin panel:
|
166
|
+
|
167
|
+
```js
|
168
|
+
import { fileURLToPath, URL } from 'node:url'
|
169
|
+
import { defineConfig } from 'vite'
|
170
|
+
import react from '@vitejs/plugin-react'
|
171
|
+
|
172
|
+
// https://vitejs.dev/config/
|
173
|
+
export default defineConfig({
|
174
|
+
base: '/vite_ops/',
|
175
|
+
plugins: [react()],
|
176
|
+
build: {
|
177
|
+
manifest: true, // This is required
|
178
|
+
rollupOptions: {
|
179
|
+
input: fileURLToPath(new URL('./src/main.jsx', import.meta.url))
|
180
|
+
},
|
181
|
+
outDir: fileURLToPath(new URL('../public/vite_admin', import.meta.url)),
|
182
|
+
emptyOutDir: true,
|
183
|
+
},
|
184
|
+
server: {
|
185
|
+
port: 5174,
|
186
|
+
origin: 'http://127.0.0.1:5174'
|
187
|
+
}
|
188
|
+
})
|
189
|
+
|
190
|
+
```
|
191
|
+
|
192
|
+
## Special Thanks
|
193
|
+
|
194
|
+
This project uses ideas and codes from the following projects:
|
195
|
+
|
196
|
+
* [vite_ruby](https://github.com/ElMassimo/vite_ruby)
|
197
|
+
* [minipack](https://github.com/nikushi/minipack)
|
198
|
+
|
199
|
+
## License
|
200
|
+
|
201
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniviteRails
|
4
|
+
class Configuration
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
ROOT_DEFAULT_ID = :''
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def config_attr(prop, default: nil)
|
11
|
+
define_method(prop) do
|
12
|
+
# If not overridden, children will use parent's setting
|
13
|
+
@config.fetch(prop) do
|
14
|
+
@parent ? @parent.public_send(prop) : default
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method("#{prop}=".to_sym) do |v|
|
19
|
+
@config[prop] = v
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Private
|
25
|
+
config_attr :id, default: ROOT_DEFAULT_ID
|
26
|
+
config_attr :cache, default: false
|
27
|
+
# The base directory of the frontend.
|
28
|
+
config_attr :vite_dev_server
|
29
|
+
config_attr :manifest_path
|
30
|
+
config_attr :public_base_path, default: '/vite'
|
31
|
+
config_attr :public_dir, default: 'public'
|
32
|
+
|
33
|
+
# Initializes a new instance of Configuration class.
|
34
|
+
def initialize
|
35
|
+
@parent = nil
|
36
|
+
@children = {}
|
37
|
+
@config = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def public_asset_dir
|
41
|
+
File.expand_path('.', File.join(public_dir, public_base_path))
|
42
|
+
end
|
43
|
+
|
44
|
+
def manifest
|
45
|
+
@manifest ||= Manifest.new(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def reload_manifest
|
49
|
+
@manifest&.update_config(self)
|
50
|
+
@children.each_value(&:reload_manifest)
|
51
|
+
end
|
52
|
+
|
53
|
+
def add(id)
|
54
|
+
raise Error, 'Can only define sub configuration from root config' unless root?
|
55
|
+
raise Error, 'Id already used by root configuration' if id == @config[:id]
|
56
|
+
|
57
|
+
@children[id] ||= self.class.new.tap do |c|
|
58
|
+
c.instance_variable_set(:@parent, self)
|
59
|
+
c.instance_variable_set(:@children, nil)
|
60
|
+
c.id = id
|
61
|
+
yield c if block_given?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def child_by_id(id)
|
66
|
+
raise Error, 'Can only get sub configuration from root config' unless root?
|
67
|
+
|
68
|
+
return self if id == @config[:id] # return itself if id is root id
|
69
|
+
|
70
|
+
@children.fetch(id)
|
71
|
+
rescue KeyError
|
72
|
+
raise Error, "No sub configuration with id #{id}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def root?
|
76
|
+
@parent.nil?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Modified from vite_ruby's source code
|
4
|
+
# https://github.com/ElMassimo/vite_ruby/blob/main/vite_ruby/lib/vite_ruby/manifest.rb
|
5
|
+
|
6
|
+
require 'json'
|
7
|
+
require 'active_support/core_ext/object/blank'
|
8
|
+
require 'rails'
|
9
|
+
|
10
|
+
module MiniviteRails
|
11
|
+
class Manifest
|
12
|
+
class FileNotFoundError < StandardError; end
|
13
|
+
class MissingEntryError < StandardError; end
|
14
|
+
|
15
|
+
attr_reader :config, :manifest_path
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
update_config(config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_config(config)
|
22
|
+
@config = config
|
23
|
+
@manifest_path = config.manifest_path || File.join(config.public_asset_dir, 'manifest.json')
|
24
|
+
@data = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def data
|
28
|
+
return load_manifest unless config.cache
|
29
|
+
|
30
|
+
@data ||= load_manifest
|
31
|
+
end
|
32
|
+
|
33
|
+
def path_for(name, **options)
|
34
|
+
lookup!(name, **options).fetch('file')
|
35
|
+
end
|
36
|
+
|
37
|
+
def vite_client_src
|
38
|
+
prefix_vite_asset('@vite/client') if dev_server_available?
|
39
|
+
end
|
40
|
+
|
41
|
+
def resolve_entries(*names, **options)
|
42
|
+
entries = names.map { |name| lookup!(name, **options) }
|
43
|
+
script_paths = entries.map { |entry| entry.fetch('file') }
|
44
|
+
|
45
|
+
imports = dev_server_available? ? [] : entries.flat_map { |entry| entry['imports'] }.compact.uniq
|
46
|
+
{
|
47
|
+
scripts: script_paths,
|
48
|
+
imports: imports.map { |entry| entry.fetch('file') }.uniq,
|
49
|
+
stylesheets: dev_server_available? ? [] : (entries + imports).flat_map { |entry| entry['css'] }.compact.uniq
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def react_refresh_preamble
|
54
|
+
return unless dev_server_available?
|
55
|
+
|
56
|
+
<<~REACT_REFRESH
|
57
|
+
<script type="module">
|
58
|
+
import RefreshRuntime from '#{prefix_vite_asset('@react-refresh')}'
|
59
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
60
|
+
window.$RefreshReg$ = () => {}
|
61
|
+
window.$RefreshSig$ = () => (type) => type
|
62
|
+
window.__vite_plugin_react_preamble_installed__ = true
|
63
|
+
</script>
|
64
|
+
REACT_REFRESH
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def dev_server_available?
|
70
|
+
!Rails.env.production? && config.vite_dev_server.present?
|
71
|
+
end
|
72
|
+
|
73
|
+
def load_manifest
|
74
|
+
u = URI.parse(manifest_path)
|
75
|
+
data = nil
|
76
|
+
if u.scheme == 'file' || u.path == manifest_path # file path
|
77
|
+
raise(FileNotFoundError, "#{manifest_path}: no such manifest found") unless File.exist?(manifest_path)
|
78
|
+
|
79
|
+
data = File.read(manifest_path)
|
80
|
+
else
|
81
|
+
# http url
|
82
|
+
data = u.read
|
83
|
+
end
|
84
|
+
JSON.parse(data).tap(&method(:resolve_references))
|
85
|
+
end
|
86
|
+
|
87
|
+
def prefix_vite_asset(path)
|
88
|
+
root_path = dev_server_available? ? config.vite_dev_server : '/'
|
89
|
+
File.join(root_path, config.public_base_path, path)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Internal: Resolves the paths that reference a manifest entry.
|
93
|
+
def resolve_references(manifest)
|
94
|
+
manifest.each_value do |entry|
|
95
|
+
entry['file'] = prefix_vite_asset(entry['file'])
|
96
|
+
%w[css assets].each do |key|
|
97
|
+
entry[key] = entry[key].map { |path| prefix_vite_asset(path) } if entry[key]
|
98
|
+
end
|
99
|
+
entry['imports']&.map! { |name| manifest.fetch(name) }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def lookup!(name, **options)
|
104
|
+
lookup(name, **options) || missing_entry_error(name, **options)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Internal: Computes the path for a given Vite asset using manifest.json.
|
108
|
+
#
|
109
|
+
# Returns a relative path, or nil if the asset is not found.
|
110
|
+
#
|
111
|
+
# Example:
|
112
|
+
# manifest.lookup('calendar.js')
|
113
|
+
# => { "file" => "/vite/assets/calendar-1016838bab065ae1e122.js", "imports" => [] }
|
114
|
+
def lookup(name, **options)
|
115
|
+
find_manifest_entry resolve_entry_name(name, **options)
|
116
|
+
end
|
117
|
+
|
118
|
+
def find_manifest_entry(name)
|
119
|
+
if dev_server_available?
|
120
|
+
{ 'file' => prefix_vite_asset(name) }
|
121
|
+
else
|
122
|
+
data[name]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def resolve_entry_name(name, type: nil)
|
127
|
+
return resolve_virtual_entry(name) if type == :virtual
|
128
|
+
|
129
|
+
name = with_file_extension(name.to_s, type)
|
130
|
+
raise ArgumentError, "Asset names can not be relative. Found: #{name}" if name.start_with?('.')
|
131
|
+
|
132
|
+
# Explicit path, relative to the source_code_dir.
|
133
|
+
name.sub(%r{^~/(.+)$}) { return Regexp.last_match(1) }
|
134
|
+
name
|
135
|
+
end
|
136
|
+
|
137
|
+
# Internal: Resolves a virtual entry by walking all the manifest keys.
|
138
|
+
def resolve_virtual_entry(name)
|
139
|
+
data.keys.find { |file| file.include?(name) } || name
|
140
|
+
end
|
141
|
+
|
142
|
+
# Internal: Adds a file extension to the file name, unless it already has one.
|
143
|
+
def with_file_extension(name, entry_type)
|
144
|
+
if File.extname(name).empty? && (ext = extension_for_type(entry_type))
|
145
|
+
"#{name}.#{ext}"
|
146
|
+
else
|
147
|
+
name
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Internal: Allows to receive :javascript and :stylesheet as :type in helpers.
|
152
|
+
def extension_for_type(entry_type)
|
153
|
+
case entry_type
|
154
|
+
when :javascript then 'js'
|
155
|
+
when :stylesheet then 'css'
|
156
|
+
when :typescript then 'ts'
|
157
|
+
else entry_type
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Internal: Raises a detailed message when an entry is missing in the manifest.
|
162
|
+
def missing_entry_error(name, **_options)
|
163
|
+
raise MissingEntryError, <<~MSG
|
164
|
+
Can not find #{name} in #{manifest_path}.
|
165
|
+
Your manifest contains:
|
166
|
+
#{JSON.pretty_generate(data)}
|
167
|
+
MSG
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
# Use tag helpers from Vite Ruby
|
6
|
+
# https://github.com/ElMassimo/vite_ruby/blob/main/vite_rails/lib/vite_rails/tag_helpers.rb
|
7
|
+
|
8
|
+
# Public: Allows to render HTML tags for scripts and styles processed by Vite.
|
9
|
+
module MiniviteRails
|
10
|
+
module TagHelpers
|
11
|
+
# Public: Renders a script tag for vite/client to enable HMR in development.
|
12
|
+
def vite_client_tag(id: nil, **options)
|
13
|
+
src = vite_manifest(id: id).vite_client_src
|
14
|
+
return unless src
|
15
|
+
|
16
|
+
javascript_include_tag(src, type: 'module', extname: false, **options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Renders a script tag to enable HMR with React Refresh.
|
20
|
+
def vite_react_refresh_tag(id: nil)
|
21
|
+
vite_manifest(id: id).react_refresh_preamble&.html_safe
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Resolves the path for the specified Vite asset.
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
# <%= vite_asset_path 'calendar.css' %> # => "/vite/assets/calendar-1016838bab065ae1e122.css"
|
28
|
+
def vite_asset_path(name, id: nil, **options)
|
29
|
+
path_to_asset vite_manifest(id: id).path_for(name, **options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Resolves the url for the specified Vite asset.
|
33
|
+
#
|
34
|
+
# Example:
|
35
|
+
# <%= vite_asset_url 'calendar.css' %> # => "https://example.com/vite/assets/calendar-1016838bab065ae1e122.css"
|
36
|
+
def vite_asset_url(name, id: nil, **options)
|
37
|
+
url_to_asset vite_manifest(id: id).path_for(name, **options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public: Renders a <script> tag for the specified Vite entrypoints.
|
41
|
+
def vite_javascript_tag(*names,
|
42
|
+
id: nil,
|
43
|
+
type: 'module',
|
44
|
+
asset_type: :javascript,
|
45
|
+
skip_preload_tags: false,
|
46
|
+
skip_style_tags: false,
|
47
|
+
crossorigin: 'anonymous',
|
48
|
+
media: 'screen',
|
49
|
+
**options)
|
50
|
+
entries = vite_manifest(id: id).resolve_entries(*names, type: asset_type)
|
51
|
+
tags = javascript_include_tag(*entries.fetch(:scripts), crossorigin: crossorigin, type: type, extname: false,
|
52
|
+
**options)
|
53
|
+
tags << vite_preload_tag(*entries.fetch(:imports), crossorigin: crossorigin, **options) unless skip_preload_tags
|
54
|
+
|
55
|
+
options[:extname] = false if Rails::VERSION::MAJOR >= 7
|
56
|
+
|
57
|
+
tags << stylesheet_link_tag(*entries.fetch(:stylesheets), media: media, **options) unless skip_style_tags
|
58
|
+
|
59
|
+
tags
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Renders a <script> tag for the specified Vite entrypoints.
|
63
|
+
def vite_typescript_tag(*names, id: nil, **options)
|
64
|
+
vite_javascript_tag(*names, id: id, asset_type: :typescript, **options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Renders a <link> tag for the specified Vite entrypoints.
|
68
|
+
def vite_stylesheet_tag(*names, id: nil, **options)
|
69
|
+
style_paths = names.map { |name| vite_asset_path(name, id: id, type: :stylesheet) }
|
70
|
+
|
71
|
+
options[:extname] = false if Rails::VERSION::MAJOR >= 7
|
72
|
+
|
73
|
+
stylesheet_link_tag(*style_paths, **options)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: Renders an <img> tag for the specified Vite asset.
|
77
|
+
def vite_image_tag(name, id: nil, **options)
|
78
|
+
if options[:srcset] && !options[:srcset].is_a?(String)
|
79
|
+
options[:srcset] = options[:srcset].map do |src_name, size|
|
80
|
+
"#{vite_asset_path(src_name, id: id)} #{size}"
|
81
|
+
end.join(', ')
|
82
|
+
end
|
83
|
+
|
84
|
+
image_tag(vite_asset_path(name, id: id), options)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Internal: Returns the current manifest loaded by Vite Ruby.
|
90
|
+
def vite_manifest(id: nil)
|
91
|
+
MiniviteRails.manifest(id: id)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Internal: Renders a modulepreload link tag.
|
95
|
+
def vite_preload_tag(*sources, crossorigin:, **options)
|
96
|
+
sources.map do |source|
|
97
|
+
href = path_to_asset(source)
|
98
|
+
try(:request).try(:send_early_hints,
|
99
|
+
'Link' => %(<#{href}>; rel=modulepreload; as=script; crossorigin=#{crossorigin}))
|
100
|
+
tag.link(rel: 'modulepreload', href: href, as: 'script', crossorigin: crossorigin, **options)
|
101
|
+
end.join("\n").html_safe
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniviteRails
|
4
|
+
require 'minivite_rails/configuration'
|
5
|
+
require 'minivite_rails/manifest'
|
6
|
+
require 'minivite_rails/tag_helpers'
|
7
|
+
require 'minivite_rails/version'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
yield @configuration if block_given?
|
13
|
+
@configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def manifest(id: nil)
|
17
|
+
raise 'MiniviteRails is not configured' if @configuration.nil?
|
18
|
+
|
19
|
+
return @configuration.manifest if id.nil?
|
20
|
+
|
21
|
+
@configuration.child_by_id(id).manifest
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'active_support/lazy_load_hooks'
|
27
|
+
ActiveSupport.on_load :action_view do
|
28
|
+
::ActionView::Base.include MiniviteRails::TagHelpers
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: minivite_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Linh Tran
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionview
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: railties
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 6.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 6.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: debug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '13.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '13.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.21'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.21'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.18'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.18'
|
111
|
+
description: Minivite Ruby Gem
|
112
|
+
email:
|
113
|
+
- linh.mtran168@live.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".rspec"
|
119
|
+
- ".rubocop.yml"
|
120
|
+
- ".vscode/settings.json"
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- lib/minivite_rails.rb
|
127
|
+
- lib/minivite_rails/configuration.rb
|
128
|
+
- lib/minivite_rails/manifest.rb
|
129
|
+
- lib/minivite_rails/tag_helpers.rb
|
130
|
+
- lib/minivite_rails/version.rb
|
131
|
+
homepage: https://github.com/linhmtran168/minivite_rails
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata:
|
135
|
+
homepage_uri: https://github.com/linhmtran168/minivite_rails
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 2.7.0
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubygems_version: 3.4.6
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Minivite Ruby Gem
|
155
|
+
test_files: []
|