svelte-on-rails 6.0.1 → 7.0.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/README.md +30 -629
- data/lib/generators/svelte_on_rails/install/install_generator.rb +1 -1
- data/lib/svelte_on_rails/active_record_extensions.rb +13 -12
- data/lib/svelte_on_rails/lib/to_svelte.rb +63 -62
- data/lib/svelte_on_rails/lib/view_helper_support.rb +42 -34
- data/lib/svelte_on_rails/view_helpers.rb +5 -5
- data/templates/all_features_test/app/views/svelte_on_rails_hello_world/backend_frontend_rendered.html.erb +2 -2
- data/templates/all_features_test/app/views/svelte_on_rails_hello_world/index.html.erb +1 -1
- data/templates/all_features_test/app/views/svelte_on_rails_hello_world/ssr_auto_rendered.html.erb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b61dc717ae11cdc216f2ce8a35239826d075e7fdb04559ac36f124d912ec593
|
4
|
+
data.tar.gz: 9abd85b4352eda996544904ce4df984773ebedbdf580a1c8d4aec59ab31c1481
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44935ca3cca431137508364778578b588fb0bf3339315823b975c98995640c46e8fd67fe05bc92a5cea7233a1ea1be2bcd540bc1a0955513e667f3bda283fe38
|
7
|
+
data.tar.gz: a778b7fbc71ad035c93e08cf99ba559c67fc18c8bd6cb717583e35a092c57cef8bc8a99c97ce26ba0fc98fdbe53d907d5ef67a45a8bff5458f48ca963c18c96d
|
data/README.md
CHANGED
@@ -11,27 +11,27 @@ Realizing [DHH's vision](https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a
|
|
11
11
|
|
12
12
|
Svelte offers the simplest and most elegant soulution to building reactive, high-performance front-end components.
|
13
13
|
|
14
|
-
- **Seamless Integration**
|
15
|
-
- Works flawlessly with Hotwired/Turbo
|
16
|
-
- Enhances Hotwire’s capabilities
|
17
|
-
- **Developer-Friendly**
|
18
|
-
- Simple to learn, intuitive, and powerful
|
19
|
-
- Lightning-fast performance
|
20
14
|
- **Compared to Single Page Apps (SPAs)**
|
21
15
|
- Full-stack development delivers maximum value:
|
22
16
|
- Unified testing from database to frontend
|
23
17
|
- Single-source system delivery
|
24
18
|
- For the most HTML Hotwired is enough
|
19
|
+
- **Compared to integrated React or Vue**
|
20
|
+
- No virtual DOM, resulting in leaner packages and faster performance
|
21
|
+
- See Rich Harris’ [Rethinking Reactivity](https://svelte.dev/blog/svelte-3-rethinking-reactivity) (3:50–6:40) for a compelling comparison
|
22
|
+
- Easier to learn
|
23
|
+
- While React and Vue have larger communities, Svelte’s ecosystem is robust and growing, ideal for Rails integration
|
25
24
|
- **Compared to Hotwired**
|
26
|
-
- Stimulus is not a tool for frontend-apps
|
25
|
+
- Stimulus is a initializer, but not a tool for frontend-apps!
|
27
26
|
- Svelte eliminates redundant HTML initial state logic
|
28
27
|
- Consolidates component logic into a single file
|
29
28
|
- Offloads rendering to JavaScript by Frontend, Server side Rendering only where necessary, reducing server load
|
30
|
-
- **
|
31
|
-
|
32
|
-
-
|
33
|
-
|
34
|
-
|
29
|
+
- **Seamless Integration**
|
30
|
+
- Works flawlessly with Hotwired/Turbo
|
31
|
+
- Enhances Hotwire’s capabilities
|
32
|
+
- **Developer-Friendly**
|
33
|
+
- Simple to learn, intuitive, and powerful
|
34
|
+
- Lightning-fast performance
|
35
35
|
|
36
36
|
Svelte empowers Rails’ full-stack vision with modern, efficient front-end integration.
|
37
37
|
|
@@ -46,112 +46,34 @@ Svelte empowers Rails’ full-stack vision with modern, efficient front-end inte
|
|
46
46
|
|
47
47
|
see [issues](https://gitlab.com/sedl/svelte-on-rails/-/issues)
|
48
48
|
|
49
|
-
#
|
50
|
-
|
51
|
-
Rock-solid and seamless integration of Svelte Components into Rails views, based on `vite_rails`.
|
52
|
-
|
53
|
-
By default, and when installed together with `@hotwired/turbo-rails`, it renders
|
54
|
-
svelte components on the first request server side («SSR») and for subsequent
|
55
|
-
requests it provides a empty tag which is mounted on the frontend
|
56
|
-
by the associated npm package [@csedl/svelte-on-rails](https://www.npmjs.com/package/@csedl/svelte-on-rails).
|
57
|
-
|
58
|
-
This way svelte works perfectly together with turbo. You will never notice
|
59
|
-
this unpleasant «blink» on the frontend while the whole process is maximum
|
60
|
-
performance optimized.
|
49
|
+
# Description 👍
|
61
50
|
|
62
|
-
|
63
|
-
|
51
|
+
Renders Svelte components server-side and, together with [@csedl/svelte-on-rails](https://www.npmjs.com/package/@csedl/svelte-on-rails),
|
52
|
+
hydrates the component on the frontend.
|
64
53
|
|
65
|
-
|
66
|
-
|
67
|
-
on the systems of my two biggest customers.
|
54
|
+
Together with `turbo-rails` or `turbolinks`, if configured, it renders server-side only on initial requests
|
55
|
+
and delivers a empty tag that will be rendered by frontend.
|
68
56
|
|
69
57
|
If you have issues, please open one, and contributors are welcome!
|
70
58
|
|
71
59
|
## Requirements
|
72
60
|
|
73
|
-
-
|
74
|
-
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
- turbolinks and hotwired/turbo
|
61
|
+
- tested on
|
62
|
+
- ruby 3.2.2 and rails 7.1
|
63
|
+
- ruby 2.7.5 and rails 6.1
|
64
|
+
- vite@6 (v7 not supported, see issues)
|
65
|
+
- turbolinks and hotwired/turbo
|
79
66
|
- vite_rails (the installer will install it by option --full or --vite)
|
80
|
-
- svelte@5, vite-plugin-svelte@5 (see: [how to install svelte on rails/vite](https://dev.to/chmich/setup-inertia-and-svelte-on-rails-7-3glk))
|
67
|
+
- svelte@5, @sveltejs/vite-plugin-svelte@5 (see: [how to install svelte on rails/vite](https://dev.to/chmich/setup-inertia-and-svelte-on-rails-7-3glk))
|
81
68
|
- turbo (recommended / [how to install turbo on rails](https://github.com/hotwired/turbo-rails?tab=readme-ov-file#installation))
|
82
|
-
- if you use special packages (like pug) that requires commonjs, you may need
|
83
69
|
- npm on latest versions
|
84
|
-
- actual node installed.
|
85
|
-
|
86
|
-
|
87
|
-
|
70
|
+
- actual node installed.
|
71
|
+
- if `nvm` is installed it gets the path to the node-binary from there.
|
72
|
+
- When `.nvmrc` is present on projects root, it is respected
|
73
|
+
- If node is not included on the PATH you can configure your node path by environment variable `SVELTE_ON_RAILS_NODE_BIN`
|
88
74
|
|
89
|
-
## Installation from cero ⚙️
|
90
75
|
|
91
|
-
|
92
|
-
rails new my-test-app --skip-javascript
|
93
|
-
```
|
94
|
-
|
95
|
-
within the app add the gem
|
96
|
-
|
97
|
-
```bash
|
98
|
-
bundle add svelte-on-rails
|
99
|
-
```
|
100
|
-
|
101
|
-
and run the installer
|
102
|
-
|
103
|
-
```bash
|
104
|
-
rails g svelte_on_rails:install --full
|
105
|
-
```
|
106
|
-
|
107
|
-
You have done it! 👍
|
108
|
-
|
109
|
-
Start the server and you will see a svelte hello world component rendered on the browser.
|
110
|
-
|
111
|
-
**Explanation:**
|
112
|
-
|
113
|
-
The `--full` contains:
|
114
|
-
|
115
|
-
- `--vite`
|
116
|
-
- adds vite_rails gem and running the installer
|
117
|
-
- `--haml`
|
118
|
-
- adds the gem and converts existing views
|
119
|
-
- `svelte_on_rails`
|
120
|
-
- This is not a option, this is always done: Adds a config file and installs `@csedl/svelte-on-rails` by npm
|
121
|
-
- `--turbo`
|
122
|
-
- installs `@hotwired/turbo-rails` and adds import statement to application.js
|
123
|
-
- `--svelte`
|
124
|
-
- adds or updates `svelte`
|
125
|
-
- `--hello-world`
|
126
|
-
- adds a hello world component
|
127
|
-
|
128
|
-
You can also use the options you want instead of using `--full`.
|
129
|
-
And there is the option `--force` that would not ask you whether it should overwrite existing files, for automated processes like testing.
|
130
|
-
|
131
|
-
This means, if you want all, except haml, you can run:
|
132
|
-
|
133
|
-
```bash
|
134
|
-
rails g svelte_on_rails:install --vite --turbo --svelte --hello-world
|
135
|
-
```
|
136
|
-
|
137
|
-
The installer is written carefully: It does not overwrite a file without asking
|
138
|
-
and tells you all what he is doing.
|
139
|
-
|
140
|
-
At the end, you just have to (re-) start your server
|
141
|
-
and you will see a Svelte Hello World component.
|
142
|
-
|
143
|
-
This hello world has a second page where the most common import statements are demonstrated,
|
144
|
-
separated for server and client side rendering.
|
145
|
-
|
146
|
-
Then, you can run
|
147
|
-
|
148
|
-
```bash
|
149
|
-
rails svelte_on_rails:remove_hello_world
|
150
|
-
```
|
151
|
-
|
152
|
-
and start coding.
|
153
|
-
|
154
|
-
## Installation on a existing app ⚙️
|
76
|
+
## Installation
|
155
77
|
|
156
78
|
Required: `vite_rails` must be installed, it wants a `app/frontend` folder.
|
157
79
|
|
@@ -182,533 +104,12 @@ Restart the server, add a hello world component `app/frontend/javascript/HelloWo
|
|
182
104
|
Add it to the view
|
183
105
|
|
184
106
|
```erb
|
185
|
-
<%= svelte_component('HelloWorld', title: 'Hello World') %>
|
107
|
+
<%= svelte_component('HelloWorld', {title: 'Hello World'}) %>
|
186
108
|
```
|
187
109
|
|
188
|
-
|
189
110
|
And you should see "Svelte Hello World" on the browser! 👍 🤗
|
190
111
|
|
191
|
-
**Explanation**
|
192
|
-
|
193
|
-
this Minimal installer does:
|
194
|
-
|
195
|
-
- add `app/frontend/initializers/svelte.js`
|
196
|
-
- Adds a import statement for that initializer to `application.js`
|
197
|
-
- add `app/frontend/ssr/ssr.js`
|
198
|
-
- add `config/svelte_on_rails.yml`
|
199
|
-
- add `vite-ssr.config.ts`
|
200
|
-
- add command `npm run build:ssr` to package.json
|
201
|
-
- installs or updates npm packages to the latest:
|
202
|
-
- `@csedl/svelte-on-rails`
|
203
|
-
- `typescript`
|
204
|
-
- `@types/node`
|
205
|
-
|
206
|
-
**Troubleshooting**
|
207
|
-
|
208
|
-
In the first step, the installer runs the backend parts that are unlikely to fail.
|
209
|
-
Then it installs the npm packages that are more likely to fail because of dependencies.
|
210
|
-
If so, just check that all the
|
211
|
-
packages are installed on the latest versions and you should be fine. 🤓
|
212
|
-
|
213
|
-
The most importand rule is to firstly check all npm packages installed and passing before changing your view logik.
|
214
|
-
|
215
|
-
## Check if it all works
|
216
|
-
|
217
|
-
Server Side Rendering (SSR) is a parallel pipeline to client side rendering.
|
218
|
-
Both should return the same HTML. And your global styles should be applied same way
|
219
|
-
for both cases. For normal use cases this is.
|
220
|
-
|
221
|
-
For check the **ssr pipeline** you can pass the options `ssr: true` and `hydrate: false`
|
222
|
-
to the view helper. This way you will see a «dead» backend rendered HTML with no javascript applied.
|
223
|
-
|
224
|
-
For check the **client side pipeline** you can pass the option `ssr: false` and
|
225
|
-
`hydrate: true` to the view helper.
|
226
|
-
|
227
|
-
If both are looking similar, you are good to go. Then, remove theese options, the defaults are
|
228
|
-
`ssr: :auto` and `hydrate: true`.
|
229
|
-
|
230
|
-
### Import statements
|
231
|
-
|
232
|
-
The most importand import statements that are served by this gem are included in the
|
233
|
-
hello world component and by that they are also within the testing scope. So you can
|
234
|
-
be sure that they are working. If importand statements are missing there, pelase
|
235
|
-
tell me.
|
236
|
-
|
237
|
-
Among others, working statements are:
|
238
|
-
|
239
|
-
- `import svg from '../example.svg?raw'`
|
240
|
-
- `<script lang="ts">` (svelte component with typescript syntax)
|
241
|
-
- `import { someFunction } from '../customJavascript.js';`
|
242
|
-
- `import Child from './Child.svelte';`
|
243
|
-
|
244
|
-
### Precompile assets
|
245
|
-
|
246
|
-
Usual vite has a `vite.config.ts` file, that is used for the client side precompilation.
|
247
|
-
|
248
|
-
By running this installer it adds `vite-ssr.config.ts` and a npm runner so that you can do `npm run build:ssr`
|
249
|
-
which does the server side precompilation.
|
250
|
-
|
251
|
-
The same job is triggered alongside `rails assets:precompile` for production environments.
|
252
|
-
|
253
|
-
On development, when `watch_changes` is configured, the precompilation is triggered
|
254
|
-
after any `*.svelte` file within the configured `components_folder` changed.
|
255
|
-
|
256
|
-
## Option `ssr: :auto`
|
257
|
-
|
258
|
-
`ssr: :auto` is the default option, as set on config file and can be overridden on the view helper.
|
259
|
-
|
260
|
-
By passing the `ssr: :auto` option to the view helper,
|
261
|
-
it checks if the request is an initial request (request header `X-Turbo-Request-ID` is present // configurable by `non_ssr_request_header`):
|
262
|
-
|
263
|
-
On Initial-Request, it adds the attribute `data-svelte-on-rails-initialize-action="hydrate"` and
|
264
|
-
returns a server side rendered component that will be hydrated on the frontend by the npm package.
|
265
|
-
|
266
|
-
Otherwise it adds `mount` instead of hydrate, and renders a empty element, but provided with the
|
267
|
-
necessary attributes for the npm package.
|
268
|
-
|
269
|
-
More details to this point you can find on the npm package.
|
270
|
-
|
271
|
-
This works perfectly with hotwired/turbo because the javascript is only
|
272
|
-
loaded on very first load to the frontend, then the most work is done
|
273
|
-
in frontend and the server is relieved, except on initial request.
|
274
|
-
You will see no unpleasant «blink» on the page.
|
275
|
-
|
276
|
-
**Turbolinks**
|
277
|
-
|
278
|
-
If you are working on turbolinks, you can config the header or set something like
|
279
|
-
|
280
|
-
```
|
281
|
-
document.addEventListener('turbolinks:request-start', (event) => {
|
282
|
-
var xhr = event.data.xhr
|
283
|
-
xhr.setRequestHeader("X-Turbo-Request-ID", "any-content-for-skip-svelte-ssr")
|
284
|
-
});
|
285
|
-
```
|
286
|
-
|
287
|
-
to your javascript file.
|
288
|
-
|
289
|
-
|
290
|
-
**Tip: Performance optimisation for dropdowns**
|
291
|
-
|
292
|
-
For example, with dropdowns, you will never see the unpleasant «blink» effect because
|
293
|
-
the Svelte component is not visible for the first moment after rendering.
|
294
|
-
Server-side rendering is unnecessary here. You can pass 'ssr: false' to the view helper.
|
295
|
-
This relieves the server and reduces loading time.
|
296
|
-
|
297
|
-
**Tip: Testing**
|
298
|
-
|
299
|
-
Consider setting `ssr: false` for testing only for performance reasons.
|
300
|
-
Yo can override this on a attribute on the view-helper for specific components.
|
301
|
-
But, in normal cases it should not be neccessary testing ssr explicitly.
|
302
|
-
|
303
|
-
## ActiveRecord helpers
|
304
|
-
|
305
|
-
Adds the `#to_svelte` helper to your models, example:
|
306
|
-
|
307
|
-
```ruby
|
308
|
-
@book.to_svelte(
|
309
|
-
:name,
|
310
|
-
:calculation_method,
|
311
|
-
author: [:name],
|
312
|
-
editions: [
|
313
|
-
:date,
|
314
|
-
offset: 2,
|
315
|
-
limit: 1
|
316
|
-
]
|
317
|
-
)
|
318
|
-
```
|
319
|
-
|
320
|
-
would result in something like this:
|
321
|
-
|
322
|
-
```ruby
|
323
|
-
{
|
324
|
-
"values" => {
|
325
|
-
"name" => "Learning Ruby",
|
326
|
-
"calculation_method": "any-result",
|
327
|
-
"author" => {
|
328
|
-
"name" => "Michael Hartl"
|
329
|
-
},
|
330
|
-
"editions" => [
|
331
|
-
{
|
332
|
-
date: "2025-02-03"
|
333
|
-
}
|
334
|
-
]
|
335
|
-
},
|
336
|
-
"book_labels" => {
|
337
|
-
"name" => "Name", # translated by human_attribute_name
|
338
|
-
"calculation_method" => "Calculation method",
|
339
|
-
"author" => "Author"
|
340
|
-
},
|
341
|
-
"author_labels": {
|
342
|
-
"name" => "Name"
|
343
|
-
},
|
344
|
-
"edition_labels" => {
|
345
|
-
"date" => "Date"
|
346
|
-
}
|
347
|
-
}
|
348
|
-
```
|
349
|
-
|
350
|
-
This should ease transferring data you need within the component mostly.
|
351
|
-
|
352
|
-
The same method is applicable for:
|
353
|
-
|
354
|
-
- The model itself
|
355
|
-
- It returns only the labels: Same result like above, but without the `values` key
|
356
|
-
- `ActiveRecord::Relation`
|
357
|
-
- Same result like above, but `values` then is a Array.
|
358
|
-
|
359
|
-
If a association returns a empty result, the labels are still calculated.
|
360
|
-
|
361
|
-
`offset` and `limit` are reserved keys, so, columns with the same name would be ignored.
|
362
|
-
|
363
|
-
**Caching:**
|
364
|
-
|
365
|
-
Caching Capability is not implemented on this method, you easily can wrap it by `Redis`
|
366
|
-
|
367
|
-
If used on the `cached_svelte_component` view helper,
|
368
|
-
the component's attributes are used to generate a checksum, which serves as the
|
369
|
-
cache key for efficient storage and retrieval. So, this method is meant to
|
370
|
-
make it easier to exactly filter out only the information that is needed
|
371
|
-
on the component.
|
372
|
-
|
373
|
-
## Caching
|
374
|
-
|
375
|
-
Caching only is relevant for `ssr`
|
376
|
-
|
377
|
-
When using the `cached_svelte_component` helper you must have the `redis` gem installed.
|
378
|
-
|
379
|
-
This caches on a key like `svelte-on-rails:development:SvelteOnRailsHelloWorld.svelte-1xq5tnu-User1:fscyhz-18bm76a` which includes:
|
380
|
-
|
381
|
-
- Namespace, if configured, otherwise the default is gem-name and environment
|
382
|
-
- component filename
|
383
|
-
- checksum of the file-path
|
384
|
-
- `cache_key` if given as argument to the view-helper
|
385
|
-
- can be a array of active-record objects or strings or a single element instead of a array
|
386
|
-
- checksum of the last modification timestamp of the component (only when `watch_changes` is set to true)
|
387
|
-
- checksum of the given attributes
|
388
|
-
|
389
|
-
**Configuration**
|
390
|
-
|
391
|
-
Like usually you can configure your cache store on your environment by something like:
|
392
|
-
|
393
|
-
```ruby
|
394
|
-
config.cache_store = :redis_cache_store, { url: 'redis://localhost:6379/2',
|
395
|
-
expires_in: 90.minutes,
|
396
|
-
namespace: 'my-example-app' }
|
397
|
-
```
|
398
|
-
|
399
|
-
And you can override this by
|
400
|
-
|
401
|
-
```yaml
|
402
|
-
redis_cache_store:
|
403
|
-
expires_in: 2.hours
|
404
|
-
```
|
405
|
-
|
406
|
-
on the svelte-on-rails config file or pass the `expires_in` as argument to the view helper.
|
407
|
-
|
408
|
-
**Check if it works**
|
409
|
-
|
410
|
-
Pass `debug: true` to the helper and you will see on the logs how your configuration works.
|
411
|
-
|
412
|
-
## ActionCable vs. TurboStream
|
413
|
-
|
414
|
-
There are two ways that the server can talk to the client over Websocket:
|
415
|
-
|
416
|
-
- **ActionCable** transmits directly to the frontends javascript
|
417
|
-
- **TurboStreams** is a wrapper around ActionCable
|
418
|
-
- You always need a html part for communication by secured channels
|
419
|
-
- Makes sense when you want to transfer confidential data or separate onto privileged user channels
|
420
|
-
- Has [compatibility issues with Rails-UJS](https://github.com/hotwired/turbo-rails?tab=readme-ov-file#compatibility-with-rails-ujs)
|
421
|
-
|
422
|
-
|
423
|
-
## SvelteOnRails::ActionCable
|
424
|
-
|
425
|
-
**Setup**
|
426
|
-
|
427
|
-
Add `app/channels/svelte_on_rails_channel.rb`
|
428
|
-
|
429
|
-
```ruby
|
430
|
-
class SvelteOnRailsChannel < ApplicationCable::Channel
|
431
|
-
def subscribed
|
432
|
-
stream_from "svelte_on_rails_channel"
|
433
|
-
end
|
434
|
-
|
435
|
-
def unsubscribed
|
436
|
-
# Any cleanup needed when channel is unsubscribed
|
437
|
-
end
|
438
|
-
end
|
439
|
-
```
|
440
|
-
|
441
|
-
config
|
442
|
-
|
443
|
-
```yaml
|
444
|
-
action_cable:
|
445
|
-
channel: "svelte_on_rails_channel"
|
446
|
-
```
|
447
|
-
|
448
|
-
javascript
|
449
|
-
|
450
|
-
```shell
|
451
|
-
npm i @rails/actioncable
|
452
|
-
```
|
453
|
-
|
454
|
-
Add to `application.js`
|
455
|
-
|
456
|
-
```javascript
|
457
|
-
import { createConsumer } from "@rails/actioncable"
|
458
|
-
import { SvelteOnRails, dispatchSvelteStreamEvent, actionCableDebugLog } from '@csedl/svelte-on-rails'
|
459
|
-
SvelteOnRails.debug = true
|
460
|
-
|
461
|
-
const consumer = createConsumer()
|
462
|
-
|
463
|
-
consumer.subscriptions.create("SvelteOnRailsChannel", {
|
464
|
-
connected() {
|
465
|
-
actionCableDebugLog("Connected to SvelteOnRailsChannel")
|
466
|
-
},
|
467
|
-
disconnected() {
|
468
|
-
actionCableDebugLog("Disconnected from SvelteOnRailsChannel")
|
469
|
-
},
|
470
|
-
received(data) {
|
471
|
-
actionCableDebugLog("Received:", data)
|
472
|
-
dispatchSvelteStreamEvent(data)
|
473
|
-
}
|
474
|
-
})
|
475
|
-
```
|
476
|
-
|
477
|
-
**What dispatchSvelteStreamEvent does**
|
478
|
-
|
479
|
-
- Without the attribute `component` given,
|
480
|
-
it searches for all elements with the class `svelte-component` and fires the event `channel-action`
|
481
|
-
- When `selector` is given, it searches for all matching elements within each component.
|
482
|
-
- The event can be overriden by the argument `event`
|
483
|
-
|
484
|
-
**Usage**
|
485
|
-
|
486
|
-
`app/frontend/javascript/components/folder/MyComponent.svelte`
|
487
|
-
|
488
|
-
```sveltehtml
|
489
|
-
<script>
|
490
|
-
import {addComponentStreamListener} from '@csedl/svelte-on-rails/src/componentStreamListener'
|
491
|
-
|
492
|
-
function handleCableEvent(event) {
|
493
|
-
console.log('Event received by Turbo Stream', event.detail)
|
494
|
-
}
|
495
|
-
</script>
|
496
|
-
<!--on ANY element:-->
|
497
|
-
<h1 use:addComponentStreamListener={handleCableEvent}>Test TurboStreams Channel</h1>
|
498
|
-
```
|
499
|
-
|
500
|
-
The `addComponentStreamListener` adds the eventListener `stream-action` on the wrapping Element.
|
501
|
-
The «wrapping Element» is the Element from the view helper `svelte_component` with the class `svelte-component`.
|
502
|
-
|
503
|
-
Now you can dispatch events on the component by:
|
504
|
-
|
505
|
-
```ruby
|
506
|
-
SvelteOnRails::ActionCable.dispatch(
|
507
|
-
'folder/MyComponent',
|
508
|
-
{ message: "greetings from Server: äöü🤣🌴🌍漢字" }
|
509
|
-
)
|
510
|
-
```
|
511
|
-
|
512
|
-
And you will find the object, with the message key on the browser logs.
|
513
|
-
|
514
|
-
Without any arguments, just by `SvelteOnRails::ActionCable.dispatch` it would fire the `stream-action` event on all components.
|
515
|
-
|
516
|
-
The **#dispatch_by_selector** does not go over the component, it searches for any matching selector just
|
517
|
-
on the whole `document` and fires the given event there.
|
518
|
-
|
519
|
-
# SvelteOnRails::TurboStream
|
520
|
-
|
521
|
-
**Setup**
|
522
|
-
|
523
|
-
Please setup the `turbo-rails` gem and follow the chapter [Come alive with Turbo Streams](https://github.com/hotwired/turbo-rails?tab=readme-ov-file#come-alive-with-turbo-streams), which mainly is:
|
524
|
-
|
525
|
-
```shell
|
526
|
-
bundle add turbo-rails
|
527
|
-
rails turbo:install
|
528
|
-
```
|
529
|
-
|
530
|
-
make sure that `import "@hotwired/turbo-rails"` is on your application.js and set the view helper
|
531
|
-
`<%= turbo_stream_from 'authenticated' if current_user %>` to your view.
|
532
|
-
|
533
|
-
If a channel (e.g.: `authenticated`) is active and you have an HTML element with a HTML-ID (e.g.: `svelte-on-rails-stream-actions-box`),
|
534
|
-
you can test it by:
|
535
|
-
|
536
|
-
```ruby
|
537
|
-
Turbo::StreamsChannel.send(
|
538
|
-
"broadcast_append_to",
|
539
|
-
'authenticated',
|
540
|
-
target: 'svelte-on-rails-stream-actions-box',
|
541
|
-
content: "<div>Turbo-Streams are working!</div>"
|
542
|
-
)
|
543
|
-
```
|
544
|
-
|
545
|
-
When this works you are good to go.
|
546
|
-
|
547
|
-
**Configs**
|
548
|
-
|
549
|
-
Check the regarding keys and their commends on the [config file](https://gitlab.com/sedl/svelte-on-rails/-/blob/main/templates/config_base/config/svelte_on_rails.yml?ref_type=heads).
|
550
|
-
From there, you just need:
|
551
|
-
|
552
|
-
```yaml
|
553
|
-
turbo_stream:
|
554
|
-
target_html_id: 'svelte-on-rails-stream-actions-box'
|
555
|
-
channel: 'public'
|
556
|
-
```
|
557
|
-
|
558
|
-
**Minimal Usage Example**
|
559
|
-
|
560
|
-
And call this by:
|
561
|
-
|
562
|
-
```ruby
|
563
|
-
SvelteOnRails::TurboStream.dispatch
|
564
|
-
```
|
565
|
-
**What it does**
|
566
|
-
|
567
|
-
- A Stimulus controller is pushed to all subscribers of the configured channel.
|
568
|
-
- You can override the channel name by passing `channel` to the method.
|
569
|
-
|
570
|
-
**Usage Example, more detailed**
|
571
|
-
|
572
|
-
If you want to fire a event on a specific element within the component, you do not need `addComponentStreamListener`.
|
573
|
-
Just do something like:
|
574
|
-
|
575
|
-
```sveltehtml
|
576
|
-
<script>
|
577
|
-
function logStreamMessage(event) {
|
578
|
-
console.log(event.detail.message)
|
579
|
-
}
|
580
|
-
</script>
|
581
|
-
<button class="counter-button" onclick="{logStreamMessage}">Show Contents</button>
|
582
|
-
```
|
583
|
-
|
584
|
-
within the svelte component.
|
585
|
-
|
586
|
-
Then, call it by:
|
587
|
-
|
588
|
-
```ruby
|
589
|
-
SvelteOnRails::TurboStream.dispatch(
|
590
|
-
channel: 'my-custom-stream',
|
591
|
-
component: '/javascript/components/TurboStreamsChannel',
|
592
|
-
selector: '.counter-button',
|
593
|
-
event: 'click',
|
594
|
-
event_detail: { message: "Greetings from Server: äöü🤣🌴🌍漢字" }
|
595
|
-
)
|
596
|
-
```
|
597
|
-
|
598
|
-
The **#dispatch_by_selector** does not go over the component, it searches for any matching selector just
|
599
|
-
on the whole `document` and fires the given event there.
|
600
|
-
|
601
|
-
## More rake tasks
|
602
|
-
|
603
|
-
This tasks are more for testing/playground purposes
|
604
|
-
|
605
|
-
```bash
|
606
|
-
rails svelte_on_rails:add_hello_world
|
607
|
-
```
|
608
|
-
|
609
|
-
```bash
|
610
|
-
rails svelte_on_rails:remove_hello_world
|
611
|
-
```
|
612
|
-
|
613
|
-
## Deploy
|
614
|
-
|
615
|
-
For example, by deploying with capistrano, you may add a step like
|
616
|
-
|
617
|
-
```ruby
|
618
|
-
before 'deploy:assets:precompile', 'deploy:npm:install'
|
619
|
-
namespace :deploy do
|
620
|
-
namespace :npm do
|
621
|
-
desc 'Install Node.js dependencies'
|
622
|
-
task :install do
|
623
|
-
on roles(:app) do
|
624
|
-
within release_path do
|
625
|
-
execute :npm, 'install'
|
626
|
-
end
|
627
|
-
end
|
628
|
-
end
|
629
|
-
end
|
630
|
-
end
|
631
|
-
```
|
632
|
-
|
633
|
-
to `deploy.rb` for making sure the ssr compilation is working.
|
634
|
-
|
635
|
-
## Contributors Guide
|
636
|
-
|
637
|
-
Contributors welcome!
|
638
|
-
|
639
|
-
**Download the code and run the tests**
|
640
|
-
|
641
|
-
Download the source code from the repository, and within the project folder run:
|
642
|
-
|
643
|
-
```bash
|
644
|
-
rake svelte_on_rails:create_contributor_configs_file
|
645
|
-
```
|
646
|
-
and define a `generated_test_app_folder_path` (required) for apps, generated for the testings.
|
647
|
-
|
648
|
-
For development of the **npm package @csedl/svelte-on-rails** (optional) please download the source code of the
|
649
|
-
npm package and set `local_npm_package_path` on the config file to the path to the npm package on your local machine.
|
650
|
-
This will cause the installer, to install the npm package from a local path instead from the npm registry.
|
651
|
-
|
652
|
-
Then run the tests and start contributing.
|
653
|
-
|
654
|
-
**RUN THE FIRST TEST (!)**: Testing is complex here because of the design based on the installer test.
|
655
|
-
On Problems, i always run the `Installer > destroy and create rails app > FIRST TEST > [...] check if javascript works`.
|
656
|
-
If there are problems, open the generated app on a IDE and check errors there.
|
657
|
-
|
658
|
-
When this passes, all the others passing mostly.
|
659
|
-
|
660
|
-
At the end of the most tests it leaves the rails server running, so that you can see the result on `localhost:3000`.
|
661
|
-
|
662
|
-
NOTE: Theese tests are dependend on your environment, including the running ruby version!
|
663
|
-
I am working on rvm. If you work on a different environment, some (not many) changes may be necessary.
|
664
|
-
Thats your part :)
|
665
|
-
|
666
|
-
The current test cases including (among others):
|
667
|
-
|
668
|
-
create a completely new rails app, running the full installer and check if a hello World component is visible and javascript is working.
|
669
|
-
run assets:precompile within a rails app and check if the gem does its precompiling too.
|
670
|
-
|
671
|
-
## Understanding the process
|
672
|
-
|
673
|
-
**Why not client and server rendering in one process?**
|
674
|
-
|
675
|
-
That was my idea! `application.js` which is the usual entry point for the client
|
676
|
-
side could live on the same assets and manifest like svelte components that are
|
677
|
-
compiled as chunks which each is its own entry point. This failed:
|
678
|
-
|
679
|
-
- The `vite-plugin-ruby` did not support this: it constrained all to one entry point.
|
680
|
-
- See how fat the `vite-ssr.config.ts` is. For the client side this is not necessary.
|
681
|
-
|
682
|
-
At the end, I decided to split the process. For now it is cleaner. But that is not the last decision.
|
683
|
-
|
684
|
-
**Why not compiling server side purely by rollup?**
|
685
|
-
|
686
|
-
Advantages would be much slimmer packages and faster compilation. On the first
|
687
|
-
step i did this and i backed up a working and tested code on the branch [rollup-ssr](https://gitlab.com/sedl/svelte-on-rails/-/blob/rollup-ssr/BRANCH_INFO.md?ref_type=heads).
|
688
|
-
|
689
|
-
I decided to use Vite to bring the client and server side rendering
|
690
|
-
closer together.
|
691
|
-
|
692
|
-
But this, too, is not the last decision.
|
693
|
-
|
694
|
-
For now we proceed with vite.
|
695
|
-
|
696
|
-
**How does it work now?**
|
697
|
-
|
698
|
-
Client side rendering is done by vite like usual.
|
699
|
-
|
700
|
-
Server side rendering is triggered, similar to [vite_rails](https://vite-ruby.netlify.app/guide/deployment.html)
|
701
|
-
on `assets:precompile`, and, if `watch_changes` is configured,
|
702
|
-
which is default for development, it is triggered
|
703
|
-
on every change of a `*.svelte` file within the configured `components_folder`.
|
704
|
-
|
705
|
-
On the server side only the `*.svelte` files are served. Theyr included
|
706
|
-
assets are linked to the client side assets folder, which is mapped by `manifest.json`.
|
707
|
-
|
708
|
-
Then, vite has two output folders: `vite-dev` for development and `vite` for production.
|
709
|
-
Within `vite-ssr.config.ts`, by the `RAILS_ENV` variable, is decided which one is used.
|
710
|
-
|
711
112
|
|
712
113
|
## Licence
|
713
114
|
|
714
|
-
|
115
|
+
Copyright © 2025-2028 sedlmair.ch. Distributed by [MIT License](https://svelte-on-rails-docs-51acfa.gitlab.io/license.html)
|
@@ -8,32 +8,33 @@ module SvelteOnRails
|
|
8
8
|
# end
|
9
9
|
|
10
10
|
# Returns a hash of attributes, methods, and associations formatted for Svelte components
|
11
|
-
def to_svelte(*attributes)
|
11
|
+
def to_svelte(*attributes, **associations)
|
12
12
|
@to_svelte ||= begin
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
cl = SvelteOnRails::Lib::SvelteAttributes
|
15
|
+
r = cl.new.calculate_instance(self, attributes, associations)
|
16
|
+
r.first.merge({self.class.to_s.underscore => r[1]})
|
16
17
|
|
17
|
-
|
18
|
+
end
|
18
19
|
end
|
19
20
|
|
20
21
|
end
|
21
22
|
|
22
23
|
module ActiveRecordClassExtensions
|
23
|
-
def to_svelte(*attributes)
|
24
|
+
def to_svelte(*attributes, **associations)
|
24
25
|
@to_svelte ||= begin
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
cl = SvelteOnRails::Lib::SvelteAttributes
|
27
|
+
cl.new.calculate_class(self, attributes, associations)
|
28
|
+
end
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
32
|
module ActiveRecordRelationExtensions
|
32
|
-
def to_svelte(*attributes)
|
33
|
+
def to_svelte(*attributes, **associations)
|
33
34
|
@to_svelte ||= begin
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
cl = SvelteOnRails::Lib::SvelteAttributes
|
36
|
+
cl.new.calculate_relation(self, attributes, associations)
|
37
|
+
end
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
@@ -6,7 +6,7 @@ module SvelteOnRails
|
|
6
6
|
@labels = {}
|
7
7
|
end
|
8
8
|
|
9
|
-
def calculate_instance(record, attributes, call_stack: 0, offset: nil, limit: nil)
|
9
|
+
def calculate_instance(record, attributes, associations, call_stack: 0, offset: nil, limit: nil)
|
10
10
|
|
11
11
|
next_stack = call_stack + 1
|
12
12
|
|
@@ -26,10 +26,10 @@ module SvelteOnRails
|
|
26
26
|
record
|
27
27
|
end
|
28
28
|
|
29
|
-
set_labels(record.first, attributes)
|
29
|
+
# set_labels(record.first, attributes)
|
30
30
|
|
31
31
|
values = recs2.map do |rec|
|
32
|
-
calculate_instance(rec, attributes, call_stack: next_stack)
|
32
|
+
calculate_instance(rec, attributes, associations, call_stack: next_stack)
|
33
33
|
end
|
34
34
|
|
35
35
|
else
|
@@ -38,81 +38,82 @@ module SvelteOnRails
|
|
38
38
|
|
39
39
|
values = {}
|
40
40
|
|
41
|
-
set_labels(record, attributes)
|
41
|
+
set_labels(record, attributes, associations)
|
42
42
|
|
43
43
|
attributes.each do |attr|
|
44
|
+
raise 'Invalid attribute' unless [Symbol, String].include?(attr.class)
|
45
|
+
raise "[svelte-on-rails:to_svelte] #{record.class} does not respond to: #{attr}" unless record.respond_to?(attr)
|
46
|
+
_key = attr.to_s
|
44
47
|
|
45
|
-
|
46
|
-
|
47
|
-
# we have associations
|
48
|
-
attr.each do |key, value|
|
49
|
-
next if ['offset', 'limit'].include?(key.to_s)
|
50
|
-
raise "[svelte-on-rails:to_svelte] #{record.class} does not respond to: #{key}" unless record.respond_to?(key)
|
51
|
-
_offset, _limit, _value = extract_limit(value)
|
52
|
-
|
53
|
-
_key = key.to_s
|
54
|
-
|
55
|
-
# inspect association and set_labels
|
56
|
-
|
57
|
-
reflect = record.class.reflect_on_association(_key)
|
58
|
-
raise "invalid association: #{_key}" unless reflect
|
59
|
-
set_labels(reflect, value)
|
60
|
-
|
61
|
-
# values
|
48
|
+
values[_key] = record.send(_key)
|
49
|
+
end
|
62
50
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
51
|
+
associations.each do |key, val|
|
52
|
+
|
53
|
+
# we have associations
|
54
|
+
val.each do |_key|
|
55
|
+
next if ['offset', 'limit'].include?(_key.to_s)
|
56
|
+
raise "[svelte-on-rails:to_svelte] #{record.class} does not respond to: #{key}" unless record.respond_to?(key)
|
57
|
+
_offset, _limit, _value = extract_limit(val)
|
58
|
+
|
59
|
+
_key = key.to_s
|
60
|
+
|
61
|
+
# inspect association and set_labels
|
62
|
+
|
63
|
+
reflect = record.class.reflect_on_association(_key)
|
64
|
+
raise "invalid association: #{_key}" unless reflect
|
65
|
+
set_labels(reflect, val)
|
66
|
+
|
67
|
+
# values
|
68
|
+
|
69
|
+
recs = record.send(_key)
|
70
|
+
if recs.present?
|
71
|
+
if recs.respond_to?(:each)
|
72
|
+
values[_key] = calculate_instance(
|
73
|
+
recs,
|
74
|
+
val.reject{|v|v.is_a?(Hash)},
|
75
|
+
{},
|
76
|
+
call_stack: next_stack,
|
77
|
+
offset: _offset,
|
78
|
+
limit: _limit
|
79
|
+
)
|
80
|
+
else
|
81
|
+
values[_key] = calculate_instance(
|
82
|
+
recs,
|
83
|
+
val,
|
84
|
+
{},
|
85
|
+
call_stack: next_stack
|
86
|
+
)
|
80
87
|
end
|
81
88
|
end
|
82
|
-
|
83
|
-
else
|
84
|
-
# we have attributes
|
85
|
-
raise 'Invalid attribute' unless [Symbol, String].include?(attr.class)
|
86
|
-
raise "[svelte-on-rails:to_svelte] #{record.class} does not respond to: #{attr}" unless record.respond_to?(attr)
|
87
|
-
_key = attr.to_s
|
88
|
-
|
89
|
-
values[_key] = record.send(_key)
|
90
89
|
end
|
91
|
-
|
92
90
|
end
|
93
91
|
end
|
94
92
|
|
95
93
|
if call_stack >= 1
|
96
94
|
values
|
97
95
|
else
|
98
|
-
|
96
|
+
[
|
97
|
+
@labels,
|
98
|
+
values
|
99
|
+
]
|
99
100
|
end
|
100
101
|
|
101
102
|
end
|
102
103
|
|
103
|
-
def calculate_class(model, attributes, call_stack: 0)
|
104
|
+
def calculate_class(model, attributes, associations, call_stack: 0)
|
104
105
|
|
105
106
|
next_stack = call_stack + 1
|
106
107
|
|
107
|
-
set_labels(model, attributes)
|
108
|
+
set_labels(model, attributes, associations)
|
108
109
|
|
109
|
-
|
110
|
-
hash.each do |key, value|
|
110
|
+
associations.each do |key, value|
|
111
111
|
reflect = model.reflect_on_association(key.to_s)
|
112
112
|
if reflect
|
113
113
|
calculate_class(
|
114
114
|
reflect,
|
115
115
|
value,
|
116
|
+
{},
|
116
117
|
call_stack: next_stack
|
117
118
|
)
|
118
119
|
end
|
@@ -123,14 +124,16 @@ module SvelteOnRails
|
|
123
124
|
end
|
124
125
|
end
|
125
126
|
|
126
|
-
def calculate_relation(relation, attributes,
|
127
|
+
def calculate_relation(relation, attributes, associations)
|
127
128
|
set_labels(relation.klass, attributes)
|
128
|
-
|
129
|
-
calculate_instance(rec, attributes)
|
129
|
+
r = relation.map do |rec|
|
130
|
+
calculate_instance(rec, attributes, associations, call_stack: 1)
|
130
131
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
132
|
+
|
133
|
+
@labels.merge({
|
134
|
+
relation.klass.to_s.underscore.pluralize => r
|
135
|
+
})
|
136
|
+
|
134
137
|
end
|
135
138
|
|
136
139
|
private
|
@@ -164,11 +167,9 @@ module SvelteOnRails
|
|
164
167
|
]
|
165
168
|
end
|
166
169
|
|
167
|
-
def set_labels(record, keys)
|
170
|
+
def set_labels(record, keys, assoc = {})
|
168
171
|
|
169
|
-
|
170
|
-
_keys = keys.reject { |element| element.is_a?(Hash) }
|
171
|
-
_keys += first_hash.keys if first_hash
|
172
|
+
_keys = keys.reject { |element| element.is_a?(Hash) } + assoc.keys
|
172
173
|
|
173
174
|
_keys.each do |attr|
|
174
175
|
unless attr.respond_to?(:each)
|
@@ -3,19 +3,23 @@ module SvelteOnRails
|
|
3
3
|
|
4
4
|
module Lib
|
5
5
|
class ViewHelperSupport
|
6
|
-
attr_reader :filename, :
|
6
|
+
attr_reader :filename, :options, :html_options, :request, :conf
|
7
7
|
|
8
|
-
def initialize(file,
|
8
|
+
def initialize(file, props, html_options, options, request, caching = false)
|
9
9
|
|
10
10
|
@start_time = Time.now
|
11
11
|
@conf = SvelteOnRails::Configuration.instance
|
12
12
|
utils = SvelteOnRails::Lib::Utils
|
13
13
|
utils.validate_filename(file) if @conf.watch_changes?
|
14
14
|
@filename = (file.match(/\.svelte$/) ? file[0..-8] : file)
|
15
|
-
@args_checksum =
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
@args_checksum = [
|
16
|
+
Zlib.crc32(props.to_json).to_s(36),
|
17
|
+
Zlib.crc32(options.to_json).to_s(36)
|
18
|
+
].join('-')
|
19
|
+
@svelte_props = props ||= {}
|
20
|
+
@options, @html_options = prepare_options(
|
21
|
+
options,
|
22
|
+
html_options,
|
19
23
|
%i[ssr hydrate debug cache_key expires_in]
|
20
24
|
)
|
21
25
|
@request = request
|
@@ -32,8 +36,8 @@ module SvelteOnRails
|
|
32
36
|
if caching
|
33
37
|
raise '[svelte-on-rails] Caching required but Redis is not defined' unless defined?(Redis)
|
34
38
|
else
|
35
|
-
raise '[svelte-on-rails] :expires_in is not allowed for this helper' if @
|
36
|
-
raise '[svelte-on-rails] :cache_key is not allowed for this helper' if @
|
39
|
+
raise '[svelte-on-rails] :expires_in is not allowed for this helper' if @options.key?(:expires_in)
|
40
|
+
raise '[svelte-on-rails] :cache_key is not allowed for this helper' if @options.key?(:cache_key)
|
37
41
|
return
|
38
42
|
end
|
39
43
|
|
@@ -42,11 +46,11 @@ module SvelteOnRails
|
|
42
46
|
end
|
43
47
|
|
44
48
|
def debug?
|
45
|
-
@
|
49
|
+
@options[:debug]
|
46
50
|
end
|
47
51
|
|
48
52
|
def redis_expiration_seconds
|
49
|
-
(conf.redis_cache_store[:expires_in] || @
|
53
|
+
(conf.redis_cache_store[:expires_in] || @options[:expires_in] || 1.hour).to_i
|
50
54
|
end
|
51
55
|
|
52
56
|
def elapsed_milliseconds
|
@@ -89,8 +93,8 @@ module SvelteOnRails
|
|
89
93
|
end
|
90
94
|
|
91
95
|
def custom_cache_key
|
92
|
-
if @
|
93
|
-
k2 = (@
|
96
|
+
if @options[:cache_key]
|
97
|
+
k2 = (@options[:cache_key])
|
94
98
|
keys = k2.is_a?(Array) ? k2 : [k2]
|
95
99
|
keys.map do |k|
|
96
100
|
if k.is_a?(ActiveRecord::Base)
|
@@ -109,13 +113,13 @@ module SvelteOnRails
|
|
109
113
|
private
|
110
114
|
|
111
115
|
def determine_ssr
|
112
|
-
_ssr = if @
|
116
|
+
_ssr = if @options.key?(:ssr)
|
113
117
|
if @conf.watch_changes?
|
114
|
-
unless [true, false, :auto].include?(@
|
118
|
+
unless [true, false, :auto].include?(@options[:ssr])
|
115
119
|
raise "Only true, false, or :auto are allowed for the argument #ssr"
|
116
120
|
end
|
117
121
|
end
|
118
|
-
@
|
122
|
+
@options[:ssr]
|
119
123
|
else
|
120
124
|
@conf.ssr
|
121
125
|
end
|
@@ -147,32 +151,36 @@ module SvelteOnRails
|
|
147
151
|
|
148
152
|
end
|
149
153
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
154
|
+
def prepare_options(options, html_options, available_options)
|
155
|
+
|
156
|
+
# html options
|
157
|
+
|
158
|
+
ht_opts = html_options.dup.deep_merge(
|
159
|
+
{
|
160
|
+
data: {
|
161
|
+
svelte_component: "/#{conf.components_folder + filename}",
|
162
|
+
controller: 'svelte-on-rails'
|
163
|
+
}
|
157
164
|
}
|
158
|
-
|
165
|
+
)
|
166
|
+
ht_opts[:class] = "#{ht_opts[:class]} svelte-component".strip
|
167
|
+
ht_opts[:data][:props] = @svelte_props.to_json
|
168
|
+
ht_opts[:data][:svelte_status] = 'do-not-hydrate-me' if options[:hydrate] == false
|
159
169
|
|
160
|
-
|
170
|
+
# render options
|
171
|
+
|
172
|
+
opts = {}
|
173
|
+
options.each do |k, v|
|
161
174
|
_k = k.to_sym
|
162
|
-
if
|
163
|
-
|
164
|
-
elsif html_options.include?(_k)
|
165
|
-
ht_opts[_k] = v
|
175
|
+
if available_options.include?(_k)
|
176
|
+
opts[_k] = v
|
166
177
|
else
|
167
|
-
|
178
|
+
raise("Unknown option: #{k}")
|
168
179
|
end
|
169
180
|
end
|
170
181
|
|
171
|
-
|
172
|
-
ht_opts
|
173
|
-
ht_opts[:data][:svelte_status] = 'do-not-hydrate-me' if hlp_opts[:hydrate] == false
|
174
|
-
|
175
|
-
[hlp_opts, ht_opts, prp]
|
182
|
+
|
183
|
+
[opts, ht_opts]
|
176
184
|
end
|
177
185
|
|
178
186
|
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
module SvelteOnRails
|
3
3
|
module ViewHelpers
|
4
4
|
|
5
|
-
def svelte_component(
|
5
|
+
def svelte_component(path, props = {}, html: {}, options: {})
|
6
6
|
|
7
|
-
support = SvelteOnRails::Lib::ViewHelperSupport.new(
|
7
|
+
support = SvelteOnRails::Lib::ViewHelperSupport.new(path, props, html, options, request, false)
|
8
8
|
|
9
|
-
support.debug_log("Rendering component: #{
|
9
|
+
support.debug_log("Rendering component: #{path}")
|
10
10
|
log_message = '?'
|
11
11
|
|
12
12
|
log_message = "Rendered #{support.filename}.svelte #{'as empty element that will be mounted on the client side' unless support.ssr?}"
|
@@ -18,9 +18,9 @@ module SvelteOnRails
|
|
18
18
|
|
19
19
|
end
|
20
20
|
|
21
|
-
def cached_svelte_component(
|
21
|
+
def cached_svelte_component(path, props = {}, html: {}, options: {})
|
22
22
|
|
23
|
-
support = SvelteOnRails::Lib::ViewHelperSupport.new(
|
23
|
+
support = SvelteOnRails::Lib::ViewHelperSupport.new(path, props, html, options, request, true)
|
24
24
|
|
25
25
|
log_message = '?'
|
26
26
|
redis = support.conf.redis_instance
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<h3>Always rendered server side</h3>
|
24
24
|
<div class="ssr-only">
|
25
25
|
<% components.each do |component| %>
|
26
|
-
<%= svelte_component(component, ssr: true, hydrate: false
|
26
|
+
<%= svelte_component(component, {title: component}, options: {ssr: true, hydrate: false}) %>
|
27
27
|
<% end %>
|
28
28
|
</div>
|
29
29
|
|
@@ -32,6 +32,6 @@
|
|
32
32
|
<p>Here you will see a unpleasant «blink» on the initial request.</p>
|
33
33
|
<div class="client-only">
|
34
34
|
<% components.each do |component| %>
|
35
|
-
<%= svelte_component(component, ssr: false, hydrate: true
|
35
|
+
<%= svelte_component(component, {title: component}, options: {ssr: false, hydrate: true}) %>
|
36
36
|
<% end %>
|
37
37
|
</div>
|
data/templates/all_features_test/app/views/svelte_on_rails_hello_world/ssr_auto_rendered.html.erb
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
<h3>Rendered server side only on initial request</h3>
|
21
21
|
<div class="ssr-auto">
|
22
22
|
<% components.each do |component| %>
|
23
|
-
<%= svelte_component(component, title: component) %>
|
23
|
+
<%= svelte_component(component, { title: component }) %>
|
24
24
|
<% end %>
|
25
25
|
</div>
|
26
26
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: svelte-on-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 7.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Sedlmair
|
@@ -78,13 +78,13 @@ files:
|
|
78
78
|
- templates/config_base/app/frontend/ssr/ssr.js
|
79
79
|
- templates/config_base/config/svelte_on_rails.yml
|
80
80
|
- templates/config_base/vite-ssr.config.ts
|
81
|
-
homepage: https://
|
81
|
+
homepage: https://svelte-on-rails-docs-51acfa.gitlab.io/
|
82
82
|
licenses:
|
83
83
|
- MIT
|
84
84
|
metadata:
|
85
|
-
homepage_uri: https://
|
86
|
-
source_code_uri: https://
|
87
|
-
changelog_uri: https://
|
85
|
+
homepage_uri: https://svelte-on-rails-docs-51acfa.gitlab.io/
|
86
|
+
source_code_uri: https://svelte-on-rails-docs-51acfa.gitlab.io/
|
87
|
+
changelog_uri: https://svelte-on-rails-docs-51acfa.gitlab.io/
|
88
88
|
post_install: ruby -r svelte_on_rails/install -e 'SvelteOnRails::Install.run'
|
89
89
|
rdoc_options: []
|
90
90
|
require_paths:
|