rails_vite 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -1
- data/README.md +101 -2
- data/lib/rails_vite/manifest.rb +26 -3
- data/lib/rails_vite/tag_helper.rb +39 -15
- data/lib/rails_vite/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f9257421d49c1d9cc9dc84fb86429215d7bf94af8a0352a9731d209fb38b6565
|
|
4
|
+
data.tar.gz: a7830e222230ad5780ff0bb02740b82136d3dfdc1e6e820b75b15ab831b97e31
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 899b0835c623650138a1d09924f141a638d408f4e067e2321637f5861b40f84afc1979e9ed0bccea3bd4b105db6d57f6646a6ab24405df1f4f36207ad0de05e6
|
|
7
|
+
data.tar.gz: 96ddd4ab25a3de8d247ca297a04844c022ad865666139e5150c695e2b304e55d5337216eeed5a8250f32f1393a818484a1cd746db588ae92447ef74d821e3c25
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning].
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.1] - 2026-03-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Support custom HTML attributes in `vite_tags` (e.g., `data-turbo-track`, `media`) ([@skryukov])
|
|
15
|
+
- Add `vite_javascript_tag`, `vite_stylesheet_tag`, and `vite_typescript_tag` compat helpers for easier migration from vite_rails ([@skryukov])
|
|
16
|
+
- Auto-discover entrypoints from `sourceDir/entrypoints/` directory ([@skryukov])
|
|
17
|
+
- Support extensionless entry names in `vite_tags` (e.g., `vite_tags("application")`) ([@skryukov])
|
|
18
|
+
- Subresource Integrity (SRI) support — automatically adds `integrity` and `crossorigin` attributes when `vite-plugin-manifest-sri` is used ([@skryukov])
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- `refresh: false` option now correctly disables file watching ([@skryukov])
|
|
23
|
+
|
|
10
24
|
## [0.1.0] - 2026-03-07
|
|
11
25
|
|
|
12
26
|
### Added
|
|
@@ -15,7 +29,8 @@ and this project adheres to [Semantic Versioning].
|
|
|
15
29
|
|
|
16
30
|
[@skryukov]: https://github.com/skryukov
|
|
17
31
|
|
|
18
|
-
[Unreleased]: https://github.com/skryukov/rails_vite/compare/v0.1.
|
|
32
|
+
[Unreleased]: https://github.com/skryukov/rails_vite/compare/v0.1.1...HEAD
|
|
33
|
+
[0.1.1]: https://github.com/skryukov/rails_vite/compare/v0.1.0...v0.1.1
|
|
19
34
|
[0.1.0]: https://github.com/skryukov/rails_vite/commits/v0.1.0
|
|
20
35
|
|
|
21
36
|
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
data/README.md
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
# RailsVite
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/rails_vite)
|
|
4
|
+
|
|
3
5
|
Vite integration for Rails, inspired by [Laravel's Vite plugin](https://laravel.com/docs/12.x/vite). No proxy, no config duplication, no magic.
|
|
4
6
|
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [How It Works](#how-it-works)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [Vite Config](#vite-config)
|
|
13
|
+
- [Adding Frameworks](#adding-frameworks)
|
|
14
|
+
- [SSR](#ssr)
|
|
15
|
+
- [Auto Build](#auto-build)
|
|
16
|
+
- [Testing the Build](#testing-the-build)
|
|
17
|
+
- [Custom Paths](#custom-paths)
|
|
18
|
+
- [Rake Tasks](#rake-tasks)
|
|
19
|
+
- [Migrating from vite_rails](#migrating-from-vite_rails)
|
|
20
|
+
- [Contributing](#contributing)
|
|
21
|
+
- [License](#license)
|
|
22
|
+
|
|
23
|
+
<a href="https://evilmartians.com/?utm_source=rails_vite&utm_campaign=project_page">
|
|
24
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Built by Evil Martians" width="236" height="54">
|
|
25
|
+
</a>
|
|
26
|
+
|
|
5
27
|
## How It Works
|
|
6
28
|
|
|
7
29
|
**Development:** The Vite plugin writes `tmp/rails-vite.json` with the dev server URL. The Rails helper reads it and emits `<script>` tags pointing directly at Vite. The browser talks to Vite — Puma never touches your assets.
|
|
@@ -62,10 +84,20 @@ Short names are automatically prefixed with `sourceDir` (default: `app/javascrip
|
|
|
62
84
|
|
|
63
85
|
| Helper | Purpose |
|
|
64
86
|
|--------|---------|
|
|
65
|
-
| `vite_tags(*entries,
|
|
87
|
+
| `vite_tags(*entries, **options)` | Emits script, stylesheet, and modulepreload tags |
|
|
88
|
+
| `vite_javascript_tag(*entries, **options)` | Same as `vite_tags`, appends `.js` to extensionless names |
|
|
89
|
+
| `vite_stylesheet_tag(*entries, **options)` | Same as `vite_tags`, appends `.css` to extensionless names |
|
|
90
|
+
| `vite_typescript_tag(*entries, **options)` | Same as `vite_tags`, appends `.ts` to extensionless names |
|
|
66
91
|
| `vite_asset_path(name)` | Returns the fingerprinted path from the manifest |
|
|
67
92
|
| `vite_image_tag(name, **options)` | Image tag with manifest-resolved src |
|
|
68
93
|
|
|
94
|
+
All tag helpers accept arbitrary HTML attributes:
|
|
95
|
+
|
|
96
|
+
```erb
|
|
97
|
+
<%= vite_tags "application.js", "application.css",
|
|
98
|
+
"data-turbo-track": "reload", nonce: content_security_policy_nonce %>
|
|
99
|
+
```
|
|
100
|
+
|
|
69
101
|
### CSS Entry Points
|
|
70
102
|
|
|
71
103
|
CSS files are detected by extension and emit `<link rel="stylesheet">`:
|
|
@@ -80,6 +112,29 @@ CSS files are detected by extension and emit `<link rel="stylesheet">`:
|
|
|
80
112
|
<%= vite_tags "application.js", nonce: content_security_policy_nonce %>
|
|
81
113
|
```
|
|
82
114
|
|
|
115
|
+
### Subresource Integrity (SRI)
|
|
116
|
+
|
|
117
|
+
[SRI](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) lets browsers verify that fetched assets haven't been tampered with by checking cryptographic hashes. Install the [`vite-plugin-manifest-sri`](https://github.com/nicolo-ribaudo/vite-plugin-manifest-sri) plugin:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm install -D vite-plugin-manifest-sri
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { defineConfig } from 'vite';
|
|
125
|
+
import rails from 'rails-vite-plugin';
|
|
126
|
+
import manifestSRI from 'vite-plugin-manifest-sri';
|
|
127
|
+
|
|
128
|
+
export default defineConfig({
|
|
129
|
+
plugins: [
|
|
130
|
+
rails(),
|
|
131
|
+
manifestSRI(),
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
That's it — `integrity` and `crossorigin="anonymous"` attributes are automatically added to all script, stylesheet, and modulepreload tags when the manifest includes integrity hashes.
|
|
137
|
+
|
|
83
138
|
### Asset Discovery (Images, Fonts)
|
|
84
139
|
|
|
85
140
|
Use `import.meta.glob` in your entry point to include assets in the Vite manifest:
|
|
@@ -114,7 +169,7 @@ export default defineConfig({
|
|
|
114
169
|
|
|
115
170
|
| Option | Default | Description |
|
|
116
171
|
|--------|---------|-------------|
|
|
117
|
-
| `input` | auto-detected | Entry point(s).
|
|
172
|
+
| `input` | auto-detected | Entry point(s). If `sourceDir/entrypoints/` exists, all files in it are used. Otherwise, detects `application.{js,ts,jsx,tsx}` in `sourceDir` |
|
|
118
173
|
| `sourceDir` | `'app/javascript'` | Source directory. Short names are prefixed with this. Also sets the `@` import alias |
|
|
119
174
|
| `ssr` | — | SSR entry point |
|
|
120
175
|
| `ssrOutputDirectory` | `'ssr'` | SSR output directory |
|
|
@@ -262,6 +317,50 @@ Defaults match the plugin defaults — no config needed if you follow convention
|
|
|
262
317
|
`vite:build` hooks into `assets:precompile` and `test:prepare` automatically. Skip with `SKIP_VITE_BUILD=1`.
|
|
263
318
|
|
|
264
319
|
|
|
320
|
+
## Migrating from vite_rails
|
|
321
|
+
|
|
322
|
+
### 1. Swap dependencies
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
# Gemfile
|
|
326
|
+
- gem "vite_rails"
|
|
327
|
+
+ gem "rails_vite"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```json
|
|
331
|
+
// package.json — replace vite-plugin-ruby with rails-vite-plugin
|
|
332
|
+
- "vite-plugin-ruby": "^5.1.1"
|
|
333
|
+
+ "rails-vite-plugin": "^0.1.0"
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 2. Replace `vite.config.ts`
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { defineConfig } from 'vite';
|
|
340
|
+
import rails from 'rails-vite-plugin';
|
|
341
|
+
|
|
342
|
+
export default defineConfig({
|
|
343
|
+
plugins: [
|
|
344
|
+
rails({
|
|
345
|
+
sourceDir: 'app/frontend',
|
|
346
|
+
}),
|
|
347
|
+
],
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
If you have an `entrypoints/` directory inside `sourceDir`, all files in it are auto-discovered — no need to list them. Otherwise, set `input` explicitly.
|
|
352
|
+
|
|
353
|
+
### 3. Delete files
|
|
354
|
+
|
|
355
|
+
- `config/vite.json` — settings now live in `vite.config.ts`
|
|
356
|
+
- `bin/vite` — no longer needed, `Procfile.dev` runs `npx vite` directly
|
|
357
|
+
|
|
358
|
+
### 4. Update layouts
|
|
359
|
+
|
|
360
|
+
Remove `vite_client_tag` and `vite_react_refresh_tag` — both are automatic now.
|
|
361
|
+
|
|
362
|
+
The `vite_javascript_tag`, `vite_stylesheet_tag`, and `vite_typescript_tag` helpers work as drop-in replacements:
|
|
363
|
+
|
|
265
364
|
## Contributing
|
|
266
365
|
|
|
267
366
|
Bug reports and pull requests are welcome on GitHub at https://github.com/skryukov/rails_vite.
|
data/lib/rails_vite/manifest.rb
CHANGED
|
@@ -6,13 +6,18 @@ module RailsVite
|
|
|
6
6
|
@path = path
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
# Vite's default resolve.extensions order, plus CSS extensions
|
|
10
|
+
RESOLVE_EXTENSIONS = %w[.mjs .js .mts .ts .jsx .tsx .json .css .scss .sass .less .styl .pcss].freeze
|
|
11
|
+
|
|
9
12
|
def lookup(name)
|
|
10
13
|
manifest = data
|
|
11
|
-
entry = manifest[name] ||
|
|
14
|
+
entry = manifest[name] || resolve_with_extension(name, manifest) ||
|
|
15
|
+
raise(MissingEntryError.new(name, @path))
|
|
12
16
|
|
|
13
17
|
{
|
|
14
18
|
file: entry["file"],
|
|
15
|
-
|
|
19
|
+
integrity: entry["integrity"],
|
|
20
|
+
css: resolve_css(entry, manifest),
|
|
16
21
|
imports: resolve_imports(entry, Set.new, manifest)
|
|
17
22
|
}
|
|
18
23
|
end
|
|
@@ -43,6 +48,23 @@ module RailsVite
|
|
|
43
48
|
raise MissingManifestError.new(@path)
|
|
44
49
|
end
|
|
45
50
|
|
|
51
|
+
def resolve_with_extension(name, manifest)
|
|
52
|
+
return if File.extname(name).present?
|
|
53
|
+
|
|
54
|
+
RESOLVE_EXTENSIONS.each do |ext|
|
|
55
|
+
entry = manifest["#{name}#{ext}"]
|
|
56
|
+
return entry if entry
|
|
57
|
+
end
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def resolve_css(entry, manifest)
|
|
62
|
+
entry.fetch("css", []).map do |css_file|
|
|
63
|
+
integrity = manifest.values.find { |e| e["file"] == css_file }&.dig("integrity")
|
|
64
|
+
{file: css_file, integrity: integrity}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
46
68
|
def resolve_imports(entry, seen, manifest)
|
|
47
69
|
entry.fetch("imports", []).flat_map do |import_key|
|
|
48
70
|
next [] if seen.include?(import_key)
|
|
@@ -51,7 +73,8 @@ module RailsVite
|
|
|
51
73
|
imported = manifest[import_key]
|
|
52
74
|
next [] unless imported
|
|
53
75
|
|
|
54
|
-
[imported["file"]
|
|
76
|
+
[{file: imported["file"], integrity: imported["integrity"]}] +
|
|
77
|
+
resolve_imports(imported, seen, manifest)
|
|
55
78
|
end
|
|
56
79
|
end
|
|
57
80
|
end
|
|
@@ -7,11 +7,24 @@ module RailsVite
|
|
|
7
7
|
resolved = entries.map { |e| resolve_vite_entry(e, config.source_dir) }
|
|
8
8
|
|
|
9
9
|
if config.dev_server_running?
|
|
10
|
-
vite_dev_tags(resolved, config.dev_server_url, nonce: nonce)
|
|
10
|
+
vite_dev_tags(resolved, config.dev_server_url, nonce: nonce, **options)
|
|
11
11
|
else
|
|
12
|
-
vite_prod_tags(resolved, nonce: nonce)
|
|
12
|
+
vite_prod_tags(resolved, nonce: nonce, **options)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
|
+
alias_method :vite_tag, :vite_tags
|
|
16
|
+
|
|
17
|
+
def vite_javascript_tag(*entries, **options)
|
|
18
|
+
vite_tags(*entries.map { |e| with_default_ext(e, ".js") }, **options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def vite_stylesheet_tag(*entries, **options)
|
|
22
|
+
vite_tags(*entries.map { |e| with_default_ext(e, ".css") }, **options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def vite_typescript_tag(*entries, **options)
|
|
26
|
+
vite_tags(*entries.map { |e| with_default_ext(e, ".ts") }, **options)
|
|
27
|
+
end
|
|
15
28
|
|
|
16
29
|
def vite_asset_path(name)
|
|
17
30
|
vite_asset_url(RailsVite.manifest.path_for(name))
|
|
@@ -23,7 +36,7 @@ module RailsVite
|
|
|
23
36
|
|
|
24
37
|
private
|
|
25
38
|
|
|
26
|
-
def vite_dev_tags(entries, dev_url, nonce: nil)
|
|
39
|
+
def vite_dev_tags(entries, dev_url, nonce: nil, **options)
|
|
27
40
|
tags = []
|
|
28
41
|
|
|
29
42
|
unless @_vite_client_emitted
|
|
@@ -42,43 +55,54 @@ module RailsVite
|
|
|
42
55
|
end
|
|
43
56
|
|
|
44
57
|
entries.each do |entry|
|
|
45
|
-
tags << build_asset_tag(entry, "#{dev_url}/#{entry}", nonce: nonce)
|
|
58
|
+
tags << build_asset_tag(entry, "#{dev_url}/#{entry}", nonce: nonce, **options)
|
|
46
59
|
end
|
|
47
60
|
|
|
48
61
|
safe_join(tags, "\n")
|
|
49
62
|
end
|
|
50
63
|
|
|
51
|
-
def vite_prod_tags(entries, nonce: nil)
|
|
64
|
+
def vite_prod_tags(entries, nonce: nil, **options)
|
|
52
65
|
tags = []
|
|
53
66
|
preloaded = Set.new
|
|
54
67
|
|
|
55
68
|
entries.each do |entry|
|
|
56
69
|
result = RailsVite.manifest.lookup(entry)
|
|
57
70
|
|
|
58
|
-
Array(result[:imports]).each do |
|
|
59
|
-
next if preloaded.include?(
|
|
60
|
-
preloaded.add(
|
|
61
|
-
tags << tag.link(rel: "modulepreload", href: vite_asset_url(
|
|
71
|
+
Array(result[:imports]).each do |import_entry|
|
|
72
|
+
next if preloaded.include?(import_entry[:file])
|
|
73
|
+
preloaded.add(import_entry[:file])
|
|
74
|
+
tags << tag.link(rel: "modulepreload", href: vite_asset_url(import_entry[:file]),
|
|
75
|
+
nonce: nonce, **sri_attrs(import_entry[:integrity]))
|
|
62
76
|
end
|
|
63
77
|
|
|
64
|
-
tags << build_asset_tag(entry, vite_asset_url(result[:file]),
|
|
78
|
+
tags << build_asset_tag(entry, vite_asset_url(result[:file]),
|
|
79
|
+
nonce: nonce, integrity: result[:integrity], **options)
|
|
65
80
|
|
|
66
|
-
Array(result[:css]).each do |
|
|
67
|
-
tags << tag.link(rel: "stylesheet", href: vite_asset_url(
|
|
81
|
+
Array(result[:css]).each do |css_entry|
|
|
82
|
+
tags << tag.link(rel: "stylesheet", href: vite_asset_url(css_entry[:file]),
|
|
83
|
+
nonce: nonce, **sri_attrs(css_entry[:integrity]), **options)
|
|
68
84
|
end
|
|
69
85
|
end
|
|
70
86
|
|
|
71
87
|
safe_join(tags, "\n")
|
|
72
88
|
end
|
|
73
89
|
|
|
74
|
-
def build_asset_tag(entry, url, nonce: nil)
|
|
90
|
+
def build_asset_tag(entry, url, nonce: nil, integrity: nil, **options)
|
|
75
91
|
if css_entry?(entry)
|
|
76
|
-
tag.link(rel: "stylesheet", href: url, nonce: nonce)
|
|
92
|
+
tag.link(rel: "stylesheet", href: url, nonce: nonce, **sri_attrs(integrity), **options)
|
|
77
93
|
else
|
|
78
|
-
tag.script(src: url, type: "module", nonce: nonce)
|
|
94
|
+
tag.script(src: url, type: "module", nonce: nonce, **sri_attrs(integrity), **options)
|
|
79
95
|
end
|
|
80
96
|
end
|
|
81
97
|
|
|
98
|
+
def sri_attrs(integrity)
|
|
99
|
+
integrity ? {integrity: integrity, crossorigin: "anonymous"} : {}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def with_default_ext(name, ext)
|
|
103
|
+
File.extname(name).empty? ? "#{name}#{ext}" : name
|
|
104
|
+
end
|
|
105
|
+
|
|
82
106
|
def resolve_vite_entry(entry, source_dir)
|
|
83
107
|
entry.start_with?("#{source_dir}/", "/") ? entry : "#{source_dir}/#{entry}"
|
|
84
108
|
end
|
data/lib/rails_vite/version.rb
CHANGED