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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e21a7726c621ab685293e27628370999aa8f4eac432f7bba906385f61e07e9d7
4
- data.tar.gz: 1b960c132edf8437be44cf32237c2b13e87fdf0611c009a90a3c5807e6f117d9
3
+ metadata.gz: f9257421d49c1d9cc9dc84fb86429215d7bf94af8a0352a9731d209fb38b6565
4
+ data.tar.gz: a7830e222230ad5780ff0bb02740b82136d3dfdc1e6e820b75b15ab831b97e31
5
5
  SHA512:
6
- metadata.gz: 4e4c557a47e858aece8cfaa5d80e1f39b1858327107ff92142b44d329a37713bbba32a84075eb821a5ef89c25150be8a374fbdec756bfef24dfbb5d88266878c
7
- data.tar.gz: 6fbadd4536700b156f151250abb377665d099ae0ace3c2b97f9e60bcc6a2df510efb6f3d7c39c53d85030e5842807d6aa9d4b21b87eb86e2716c24b324618417
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.0...HEAD
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
+ [![Gem Version](https://badge.fury.io/rb/rails_vite.svg)](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, nonce: nil)` | Emits script, stylesheet, and modulepreload tags |
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). Auto-detects `application.{js,ts,jsx,tsx}` in `sourceDir` |
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.
@@ -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] || raise(MissingEntryError.new(name, @path))
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
- css: entry.fetch("css", []),
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"]] + resolve_imports(imported, seen, manifest)
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 |import_file|
59
- next if preloaded.include?(import_file)
60
- preloaded.add(import_file)
61
- tags << tag.link(rel: "modulepreload", href: vite_asset_url(import_file), nonce: nonce)
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]), nonce: nonce)
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 |css_file|
67
- tags << tag.link(rel: "stylesheet", href: vite_asset_url(css_file), nonce: nonce)
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
@@ -1,3 +1,3 @@
1
1
  module RailsVite
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
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.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov