rails_vite 0.2.2 → 0.2.3
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 +19 -0
- data/README.md +41 -1
- data/lib/rails_vite/auto_build.rb +7 -4
- data/lib/rails_vite/tag_helper.rb +14 -5
- data/lib/rails_vite/version.rb +1 -1
- data/lib/rails_vite.rb +14 -0
- 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: 87116a45bfd905d7f8e598b6dbf486f7206f39bc62ab39d9a0d43450f2ed2a28
|
|
4
|
+
data.tar.gz: f5b4ca52c2c10a2ad14da1bb46b3b43a2f566bf706e1063154d7056e09c16c31
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 206278e4c87c73d4c1529db69778c751b809d904f066f35ba23c06afa127dcdceac52c0fb0e1aba2d35ec5dfdd17d251ae62bca82ab4e7bbd503766a951d509d
|
|
7
|
+
data.tar.gz: 1759ef4e320790b3090c18c3476dcf1c253e30a910de32230f80fd5c78460d27f5b6f752812053571c4209b5d1a8724590c6ee26702e79abda2035c560e29074
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog],
|
|
6
6
|
and this project adheres to [Semantic Versioning].
|
|
7
7
|
|
|
8
|
+
## rails_vite@0.2.3 / rails-vite-plugin@0.2.5 - 2026-06-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `RailsVite.dev_server_csp_source` returns a Content Security Policy source for the running Vite dev server, resolved per request so it tracks the real (possibly auto-incremented) port and adds nothing when the server is down. Pass `websocket: true` for the HMR socket (#25) ([@skryukov])
|
|
13
|
+
- `prependSourceDirToEntries` plugin option for `rails()` and `jsbundling()`. Set it to `false` to use Vite's `root` as your `sourceDir` — entries are then resolved by their bare, root-relative names instead of being prefixed with `sourceDir` (#22) ([@skryukov])
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Auto builds now run quietly (`vite build --logLevel warn`) to keep system-test output clean; warnings and errors are still shown ([@skryukov])
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- The development `@vite/client` and React Fast Refresh tags now derive their nonce from the request (`content_security_policy_nonce`) instead of inheriting whatever nonce the first `vite_*` call passed. A nonce-less first call (e.g. a stylesheet) no longer ships a nonce-less client script under a `strict-dynamic` CSP (#25) ([@skryukov])
|
|
22
|
+
- Persist auto-build freshness across process restarts so repeated local system-test runs no longer rebuild unchanged assets. Freshness now derives from the build manifest's timestamp instead of in-memory state (#21) ([@skryukov], [@brodienguyen])
|
|
23
|
+
- Don't delete the dev metadata file owned by another Vite process on exit, so concurrent Vite servers no longer clobber each other's dev server info (#27) ([@brodienguyen])
|
|
24
|
+
- Allow Vite config to resolve in CI environments without weakening the dev-server environment guard (#28) ([@brodienguyen])
|
|
25
|
+
|
|
8
26
|
## rails_vite@0.2.2 / rails-vite-plugin@0.2.4 - 2026-04-09
|
|
9
27
|
|
|
10
28
|
### Added
|
|
@@ -74,6 +92,7 @@ and this project adheres to [Semantic Versioning].
|
|
|
74
92
|
- Initial release ([@skryukov])
|
|
75
93
|
|
|
76
94
|
[@skryukov]: https://github.com/skryukov
|
|
95
|
+
[@brodienguyen]: https://github.com/brodienguyen
|
|
77
96
|
|
|
78
97
|
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
|
79
98
|
[Semantic Versioning]: https://semver.org/spec/v2.0.0.html
|
data/README.md
CHANGED
|
@@ -107,12 +107,27 @@ CSS files are detected by extension and emit `<link rel="stylesheet">`:
|
|
|
107
107
|
<%= vite_tags "application.css" %>
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
###
|
|
110
|
+
### Content Security Policy
|
|
111
|
+
|
|
112
|
+
Pass a `nonce` to any tag helper; it's applied to every tag it emits:
|
|
111
113
|
|
|
112
114
|
```erb
|
|
113
115
|
<%= vite_tags "application.js", nonce: content_security_policy_nonce %>
|
|
114
116
|
```
|
|
115
117
|
|
|
118
|
+
The dev server's `@vite/client` and React Fast Refresh tags pick up the request nonce automatically, so they work under `strict-dynamic` even when your first `vite_*` call is a nonce-less stylesheet.
|
|
119
|
+
|
|
120
|
+
Running a CSP in development? Allow the dev server's origin with `RailsVite.dev_server_csp_source` — it resolves per request, so it tracks Vite's actual port and adds nothing when the server is down:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
# config/initializers/content_security_policy.rb — inside your `policy` block
|
|
124
|
+
if Rails.env.development?
|
|
125
|
+
policy.script_src(*policy.script_src, RailsVite.dev_server_csp_source)
|
|
126
|
+
policy.style_src(*policy.style_src, RailsVite.dev_server_csp_source)
|
|
127
|
+
policy.connect_src(*policy.connect_src, RailsVite.dev_server_csp_source(websocket: true)) # HMR
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
116
131
|
### Subresource Integrity (SRI)
|
|
117
132
|
|
|
118
133
|
[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:
|
|
@@ -178,6 +193,7 @@ export default defineConfig({
|
|
|
178
193
|
| `buildDir` | `'vite'` | Build output subdirectory inside `public/` |
|
|
179
194
|
| `publicDir` | `'public'` | Public directory |
|
|
180
195
|
| `refresh` | `true` | Paths to watch for full-page reload. `true` watches `app/views/**` and `app/helpers/**` |
|
|
196
|
+
| `prependSourceDirToEntries` | `true` | When `false`, entries are resolved without the `sourceDir` prefix. Set this when Vite's `root` is your `sourceDir` (see below) |
|
|
181
197
|
|
|
182
198
|
### Multiple Entry Points
|
|
183
199
|
|
|
@@ -208,6 +224,26 @@ rails({
|
|
|
208
224
|
<%= vite_tags "entrypoints/application.ts" %>
|
|
209
225
|
```
|
|
210
226
|
|
|
227
|
+
### Setting Vite's `root` to your source directory
|
|
228
|
+
|
|
229
|
+
By default, `root` is the Rails project root, so `import.meta.glob` keys and manifest entries are prefixed with `sourceDir` (e.g. `app/frontend/components/Foo.jsx`). If you prefer `vite_ruby`-style root-relative names (`components/Foo.jsx`), set Vite's `root` to your source directory and tell the plugin not to prepend `sourceDir`:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { fileURLToPath } from 'node:url'
|
|
233
|
+
|
|
234
|
+
export default defineConfig({
|
|
235
|
+
root: fileURLToPath(new URL('./app/frontend', import.meta.url)),
|
|
236
|
+
plugins: [
|
|
237
|
+
rails({ sourceDir: 'app/frontend', prependSourceDirToEntries: false }),
|
|
238
|
+
],
|
|
239
|
+
build: {
|
|
240
|
+
outDir: fileURLToPath(new URL('./public/vite', import.meta.url)),
|
|
241
|
+
},
|
|
242
|
+
})
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
With `root` set to the source directory, Vite emits bare manifest keys, and `prependSourceDirToEntries: false` makes the Rails helpers look them up by those bare names. Set both together. (Don't point `root` at a symlinked path — Vite resolves symlinks and emits keys that escape the root.)
|
|
246
|
+
|
|
211
247
|
## Adding Frameworks
|
|
212
248
|
|
|
213
249
|
### React
|
|
@@ -271,6 +307,10 @@ node ssr/ssr.js
|
|
|
271
307
|
|
|
272
308
|
When the Vite dev server is not running, rails_vite automatically rebuilds assets on the first request if sources have changed. This is useful for system tests and quick checks without running `bin/dev`.
|
|
273
309
|
|
|
310
|
+
Freshness is determined by comparing your source files' timestamps against the build manifest's timestamp. Since the manifest lives on disk, unchanged assets are not rebuilt across process restarts — for example, on repeated local system-test runs.
|
|
311
|
+
|
|
312
|
+
Auto builds run quietly (`vite build --logLevel warn`), so they don't clutter your test output; warnings and errors are still shown. Run `rake vite:build` directly for the full build log.
|
|
313
|
+
|
|
274
314
|
Disable it:
|
|
275
315
|
|
|
276
316
|
```ruby
|
|
@@ -9,7 +9,6 @@ module RailsVite
|
|
|
9
9
|
@app = app
|
|
10
10
|
@config = config
|
|
11
11
|
@mutex = Mutex.new
|
|
12
|
-
@last_build_at = NEVER
|
|
13
12
|
@cached_source_mtime = nil
|
|
14
13
|
@source_mtime_checked_at = nil
|
|
15
14
|
end
|
|
@@ -25,16 +24,20 @@ module RailsVite
|
|
|
25
24
|
@mutex.synchronize do
|
|
26
25
|
return unless stale?
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
# Build quietly: vite's per-build summary is noise on every auto-build
|
|
28
|
+
# (e.g. each local system-test run). Warnings and errors still stream.
|
|
29
|
+
Rails.logger.error("rails-vite: build failed") unless system("#{RailsVite::Tasks.build_command} --logLevel warn")
|
|
30
30
|
@cached_source_mtime = nil
|
|
31
31
|
@source_mtime_checked_at = nil
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def stale?
|
|
36
|
+
# The manifest file's mtime is our persisted "last build" timestamp: it
|
|
37
|
+
# lives on disk, so freshness survives process restarts (e.g. a new Rails
|
|
38
|
+
# process per local system-test run) without any extra state.
|
|
36
39
|
!File.exist?(@config.manifest_path) ||
|
|
37
|
-
latest_source_mtime > @
|
|
40
|
+
latest_source_mtime > File.mtime(@config.manifest_path)
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def latest_source_mtime
|
|
@@ -47,8 +47,14 @@ module RailsVite
|
|
|
47
47
|
tags = []
|
|
48
48
|
|
|
49
49
|
unless @_vite_client_emitted
|
|
50
|
+
# The client/react-refresh tags are emitted once, on the first vite_*
|
|
51
|
+
# call. Their nonce must come from the request — not from whichever call
|
|
52
|
+
# happened to be first — so a nonce-less first call (e.g. a stylesheet)
|
|
53
|
+
# doesn't ship a nonce-less @vite/client under a `strict-dynamic` CSP.
|
|
54
|
+
client_nonce = nonce || (content_security_policy_nonce if respond_to?(:content_security_policy_nonce))
|
|
55
|
+
|
|
50
56
|
if RailsVite.config.react_refresh?
|
|
51
|
-
tags << tag.script(type: "module", nonce:
|
|
57
|
+
tags << tag.script(type: "module", nonce: client_nonce) {
|
|
52
58
|
<<~JS.squish.html_safe
|
|
53
59
|
import{injectIntoGlobalHook}from'#{dev_url}/@react-refresh';
|
|
54
60
|
injectIntoGlobalHook(window);
|
|
@@ -57,7 +63,7 @@ module RailsVite
|
|
|
57
63
|
JS
|
|
58
64
|
}
|
|
59
65
|
end
|
|
60
|
-
tags << tag.script(src: "#{dev_url}/@vite/client", type: "module", nonce:
|
|
66
|
+
tags << tag.script(src: "#{dev_url}/@vite/client", type: "module", nonce: client_nonce)
|
|
61
67
|
@_vite_client_emitted = true
|
|
62
68
|
end
|
|
63
69
|
|
|
@@ -111,10 +117,13 @@ module RailsVite
|
|
|
111
117
|
end
|
|
112
118
|
|
|
113
119
|
def resolve_vite_entry(entry, source_dir, entrypoints_dir)
|
|
114
|
-
return entry if entry.start_with?("
|
|
120
|
+
return entry if entry.start_with?("/")
|
|
121
|
+
return entry if !source_dir.empty? && entry.start_with?("#{source_dir}/")
|
|
115
122
|
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
# source_dir is empty when the plugin's `root` is the source directory
|
|
124
|
+
# (prependSourceDirToEntries: false) — entries are then looked up by their
|
|
125
|
+
# bare, manifest-relative names.
|
|
126
|
+
[source_dir, entrypoints_dir, entry].reject { |part| part.nil? || part.empty? }.join("/")
|
|
118
127
|
end
|
|
119
128
|
|
|
120
129
|
def vite_asset_url(file)
|
data/lib/rails_vite/version.rb
CHANGED
data/lib/rails_vite.rb
CHANGED
|
@@ -24,6 +24,20 @@ module RailsVite
|
|
|
24
24
|
manifest.digest
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# A Content Security Policy source for the running Vite dev server. Returns
|
|
28
|
+
# a lambda so the URL resolves per request: it tracks the dev server's real
|
|
29
|
+
# (possibly auto-incremented) port and contributes nothing when the server
|
|
30
|
+
# isn't running. Pass websocket: true for the HMR socket (http -> ws).
|
|
31
|
+
#
|
|
32
|
+
# RailsVite.config is qualified explicitly because Rails resolves CSP Proc
|
|
33
|
+
# sources via instance_exec, which rebinds self to the controller.
|
|
34
|
+
def dev_server_csp_source(websocket: false)
|
|
35
|
+
-> {
|
|
36
|
+
url = RailsVite.config.dev_server_url
|
|
37
|
+
url && (websocket ? url.sub(/\Ahttp/, "ws") : url)
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
27
41
|
def reset!
|
|
28
42
|
@config = nil
|
|
29
43
|
@manifest = nil
|