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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65246e1c68cbefab2638785dcb3f9b28290922e2826ba1af7c2f48b00d4edc8e
4
- data.tar.gz: 2fba5e05a532396f04fda7e64cf20e5d07e85776309b09a170d3ec8925f8a95f
3
+ metadata.gz: 87116a45bfd905d7f8e598b6dbf486f7206f39bc62ab39d9a0d43450f2ed2a28
4
+ data.tar.gz: f5b4ca52c2c10a2ad14da1bb46b3b43a2f566bf706e1063154d7056e09c16c31
5
5
  SHA512:
6
- metadata.gz: 532d10ec22f6e0e4a0fefaed1216ad04efc618b3979365a47b9278be4c03d5ae24488b5fdb5bdce93ecbe963ed0a214483c22d673daa76bc1bb1af355b20850a
7
- data.tar.gz: 16f20d8f3ee96046f57b12f81671d094fef7d057cc0f8b8d4ae82fb2ab848a44db2d8fcbdc8bed01246a8351433d2874c63f7f041ab8471e0db8f4c8907f40dc
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
- ### CSP Nonces
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
- Rails.logger.error("rails-vite: build failed") unless system(RailsVite::Tasks.build_command)
29
- @last_build_at = Time.now
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 > @last_build_at
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: 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: 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?("#{source_dir}/", "/")
120
+ return entry if entry.start_with?("/")
121
+ return entry if !source_dir.empty? && entry.start_with?("#{source_dir}/")
115
122
 
116
- prefix = entrypoints_dir ? "#{source_dir}/#{entrypoints_dir}" : source_dir
117
- "#{prefix}/#{entry}"
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)
@@ -1,3 +1,3 @@
1
1
  module RailsVite
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_vite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov