svelte-on-rails 3.1.1 → 4.0.0
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 +51 -1
- data/lib/generators/svelte_on_rails/install/install_generator.rb +5 -2
- data/lib/svelte-on-rails.rb +1 -0
- data/lib/svelte_on_rails/configuration.rb +61 -43
- data/lib/svelte_on_rails/installer/npm.rb +6 -44
- data/lib/svelte_on_rails/installer/svelte.rb +3 -3
- data/lib/svelte_on_rails/installer/vite.rb +4 -4
- data/lib/svelte_on_rails/lib/view_helper_support.rb +193 -0
- data/lib/svelte_on_rails/renderer/renderer.rb +0 -4
- data/lib/svelte_on_rails/view_helpers.rb +56 -77
- data/templates/config_base/config/svelte_on_rails.yml +6 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ea6749866c7f8dc1f8a29c24c617d359e9b92c9c2d62557b32d475a4bbccdc8
|
4
|
+
data.tar.gz: 81b36d010aab7e8691a78f25a6b98acc69dab5eb1d238ab7c342afab81d170e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89a88fb47bb30c0b7119ea47b05734d5bdbed9dbe82413099316a29251ccdbabb55092760cf0473d6629326c89a4b50b585a2c023f62c48b771712a6c43395a4
|
7
|
+
data.tar.gz: ac545bd664f6ac91a9245c447954d01821d70af98443c5764efc4d5c84c063b83d592049a857ac85f65bd24c3f8f200964b20a7e784644e1d67346b3a5728968
|
data/README.md
CHANGED
@@ -28,6 +28,7 @@ On every app there are parts where you want it to shine. This is where Svelte co
|
|
28
28
|
- One Logic in one file!
|
29
29
|
- Render logic and script logic is in one file and, maybe, some css too.
|
30
30
|
- Stimulus is great for rails views with some javascript, but, complex parts: You will love svelte.
|
31
|
+
- Improved Backend Response Time: Certain HTML components can be rendered directly by frontend JavaScript.
|
31
32
|
- **Compared to React**
|
32
33
|
- No more shadow dom and all those packages that are supposed to improve performance (e.g. useCallback...)
|
33
34
|
- Slimmer packages
|
@@ -82,7 +83,7 @@ If you have issues, please open one, and contributors are welcome!
|
|
82
83
|
- vite@6 (v7 not supported, see issues)
|
83
84
|
- turbolinks and hotwired/turbo
|
84
85
|
- vite_rails (the installer will install it by option --full or --vite)
|
85
|
-
- svelte
|
86
|
+
- 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))
|
86
87
|
- turbo (recommended / [how to install turbo on rails](https://github.com/hotwired/turbo-rails?tab=readme-ov-file#installation))
|
87
88
|
- if you use special packages (like pug) that requires commonjs, you may need
|
88
89
|
- npm on latest versions
|
@@ -299,6 +300,55 @@ the Svelte component is not visible for the first moment after rendering.
|
|
299
300
|
Server-side rendering is unnecessary here. You can pass 'ssr: false' to the view helper.
|
300
301
|
This relieves the server and reduces loading time.
|
301
302
|
|
303
|
+
## Caching
|
304
|
+
|
305
|
+
Caching only is relevant for `ssr`
|
306
|
+
|
307
|
+
When using the `cached_svelte_component` helper you must have the `redis` gem installed.
|
308
|
+
|
309
|
+
This caches on a key like `svelte-on-rails:development:SvelteOnRailsHelloWorld.svelte-1xq5tnu-User1:fscyhz-18bm76a` which includes:
|
310
|
+
|
311
|
+
- gem name and environment
|
312
|
+
- component filename
|
313
|
+
- hash of the file-path
|
314
|
+
- `cache_key` if given as argument to the view-helper
|
315
|
+
- can be a array of active-record objects or strings or a single element instead of a array
|
316
|
+
- hash of the last modification timestamp of the component (only when `watch_changes` is set to true)
|
317
|
+
- hash of the given properties
|
318
|
+
|
319
|
+
**Caching strategy (!)**
|
320
|
+
|
321
|
+
Everytime the properties are changing, which are the last two elements of the hash key, which means all after the last colon,
|
322
|
+
Previous hash keys are cleared by, for example: `svelte-on-rails:development:SvelteOnRailsHelloWorld.svelte-1xq5tnu-User1:*`
|
323
|
+
|
324
|
+
That means: Please be aware to set your `cache_key` precisely for not invalidating cache keys that should not be cleared.
|
325
|
+
|
326
|
+
On the other hand, please be aware that generating lots of record dependent cache keys will consume your memory, dependent on your
|
327
|
+
redis configuration.
|
328
|
+
|
329
|
+
**Cache configuration**
|
330
|
+
|
331
|
+
Like usually you can configure your cache store on your environment by something like:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
config.cache_store = :redis_cache_store, { url: 'redis://localhost:6379/2',
|
335
|
+
expires_in: 90.minutes,
|
336
|
+
namespace: 'arcdigital_demo_cache' }
|
337
|
+
```
|
338
|
+
|
339
|
+
And you can override this by
|
340
|
+
|
341
|
+
```yaml
|
342
|
+
redis_cache_store:
|
343
|
+
expires_in: 2.hours
|
344
|
+
```
|
345
|
+
|
346
|
+
on the svelte-on-rails config file.
|
347
|
+
|
348
|
+
**Tip**
|
349
|
+
|
350
|
+
Pass `debug: true` to the helper and you will see on the logs how your configuration works.
|
351
|
+
|
302
352
|
## More rake tasks
|
303
353
|
|
304
354
|
This tasks are more for testing/playground purposes
|
@@ -43,7 +43,7 @@ module SvelteOnRails
|
|
43
43
|
puts '-' * 80
|
44
44
|
puts '• INSTALLING VITE-RAILS'
|
45
45
|
vite_i = SvelteOnRails::Installer::Vite
|
46
|
-
vite_i.install_vite(
|
46
|
+
vite_i.install_vite(version_specifier: '6')
|
47
47
|
end
|
48
48
|
|
49
49
|
def svelte_on_rails
|
@@ -98,7 +98,10 @@ module SvelteOnRails
|
|
98
98
|
|
99
99
|
puts '-' * 80
|
100
100
|
svelte_i = SvelteOnRails::Installer::Svelte
|
101
|
-
svelte_i.install_svelte
|
101
|
+
svelte_i.install_svelte(
|
102
|
+
svelte_version_specifier: '5',
|
103
|
+
vite_plugin_svelte_version_specifier: '5'
|
104
|
+
)
|
102
105
|
end
|
103
106
|
|
104
107
|
def hello_world
|
data/lib/svelte-on-rails.rb
CHANGED
@@ -2,6 +2,7 @@ require "svelte_on_rails/configuration"
|
|
2
2
|
require "svelte_on_rails/view_helpers"
|
3
3
|
require "svelte_on_rails/renderer/renderer"
|
4
4
|
require "svelte_on_rails/lib/utils"
|
5
|
+
require "svelte_on_rails/lib/view_helper_support"
|
5
6
|
require "svelte_on_rails/railtie" if defined?(Rails)
|
6
7
|
|
7
8
|
# installer
|
@@ -10,23 +10,49 @@ module SvelteOnRails
|
|
10
10
|
attr_accessor :configs
|
11
11
|
|
12
12
|
def initialize
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
|
14
|
+
@configs = redis_cache_store_configs
|
15
|
+
|
16
|
+
return unless defined?(Rails.root)
|
17
|
+
|
18
|
+
config_path = Rails.root.join("config", "svelte_on_rails.yml")
|
19
|
+
return unless File.exist?(config_path)
|
20
|
+
|
21
|
+
environments = Dir[Rails.root.join('config', 'environments', '*.rb')]
|
22
|
+
.map { |file| File.basename(file, '.rb') }
|
23
|
+
|
24
|
+
configs_base = YAML.load_file(config_path)
|
25
|
+
|
26
|
+
@configs = @configs.deep_merge(configs_base.reject { |k, _| environments.include?(k) })
|
27
|
+
|
28
|
+
@configs = @configs.deep_merge(configs_base[Rails.env] || {})
|
29
|
+
|
30
|
+
@configs = @configs.symbolize_keys
|
31
|
+
|
32
|
+
if @configs[:redis_cache_store]
|
33
|
+
if @configs[:redis_cache_store]['expires_in'].is_a?(String)
|
34
|
+
@configs[:redis_cache_store]['expires_in'] = parse_duration(
|
35
|
+
@configs[:redis_cache_store]['expires_in']
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if defined? Redis
|
41
|
+
@redis_instance = Redis.new(url: redis_cache_store[:url])
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def redis_cache_store
|
47
|
+
(@configs[:redis_cache_store] || {}).with_indifferent_access
|
48
|
+
end
|
49
|
+
|
50
|
+
def redis_instance
|
51
|
+
@redis_instance
|
22
52
|
end
|
23
53
|
|
24
54
|
def watch_changes?
|
25
|
-
|
26
|
-
@configs[Rails.env]['watch_changes'] == true
|
27
|
-
else
|
28
|
-
false
|
29
|
-
end
|
55
|
+
@configs[:watch_changes] == true
|
30
56
|
end
|
31
57
|
|
32
58
|
def rails_root(root_url = nil)
|
@@ -34,25 +60,21 @@ module SvelteOnRails
|
|
34
60
|
end
|
35
61
|
|
36
62
|
def frontend_folder
|
37
|
-
Pathname.new(@configs[
|
63
|
+
Pathname.new(@configs[:frontend_folder].to_s)
|
38
64
|
end
|
39
65
|
|
40
66
|
def frontend_folder_full
|
41
|
-
rails_root.join(@configs[
|
67
|
+
rails_root.join(@configs[:frontend_folder].to_s)
|
42
68
|
end
|
43
69
|
|
44
70
|
def components_folder
|
45
|
-
Pathname.new(@configs[
|
71
|
+
Pathname.new(@configs[:components_folder].to_s)
|
46
72
|
end
|
47
73
|
|
48
74
|
def components_folder_full
|
49
75
|
Pathname.new(frontend_folder_full).join(components_folder.to_s)
|
50
76
|
end
|
51
77
|
|
52
|
-
def assets_folder
|
53
|
-
dist_folder.join('assets')
|
54
|
-
end
|
55
|
-
|
56
78
|
def ssr_manifest
|
57
79
|
file = rails_root.join('public', 'vite-ssr', 'manifest.json')
|
58
80
|
|
@@ -68,14 +90,8 @@ module SvelteOnRails
|
|
68
90
|
|
69
91
|
end
|
70
92
|
|
71
|
-
# def manifest=(manifest_hash)
|
72
|
-
# file = dist_folder.join('manifest.json')
|
73
|
-
# @manifest = manifest_hash
|
74
|
-
# File.write(file, JSON.pretty_generate(manifest_hash))
|
75
|
-
# end
|
76
|
-
|
77
93
|
def ssr
|
78
|
-
rss = @configs[
|
94
|
+
rss = @configs[:ssr]
|
79
95
|
if rss == false || rss == :auto
|
80
96
|
rss
|
81
97
|
else
|
@@ -84,7 +100,7 @@ module SvelteOnRails
|
|
84
100
|
end
|
85
101
|
|
86
102
|
def non_ssr_request_header
|
87
|
-
rss = @configs[
|
103
|
+
rss = @configs[:non_ssr_request_header]
|
88
104
|
if rss.present?
|
89
105
|
rss
|
90
106
|
else
|
@@ -92,10 +108,6 @@ module SvelteOnRails
|
|
92
108
|
end
|
93
109
|
end
|
94
110
|
|
95
|
-
# def dist_folder(app_root = nil)
|
96
|
-
# rails_root(app_root).join('public', 'svelteDist')
|
97
|
-
# end
|
98
|
-
|
99
111
|
def client_dist_folder(app_root = nil)
|
100
112
|
if Rails.env.development?
|
101
113
|
rails_root(app_root).join('public', 'vite')
|
@@ -157,19 +169,25 @@ module SvelteOnRails
|
|
157
169
|
|
158
170
|
private
|
159
171
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
return
|
172
|
+
def redis_cache_store_configs
|
173
|
+
if defined?(Rails.application) && Rails.application.config.cache_store.is_a?(Array) && Rails.application.config.cache_store.first == :redis_cache_store
|
174
|
+
{ 'redis_cache_store' => Rails.application.config.cache_store.second.stringify_keys }
|
175
|
+
else
|
176
|
+
{}
|
166
177
|
end
|
178
|
+
end
|
167
179
|
|
168
|
-
|
180
|
+
def parse_duration(string)
|
181
|
+
# Extract number and unit
|
182
|
+
match = string.match(/^(\d+)\.(\w+)$/)
|
183
|
+
raise ArgumentError, "Invalid duration format: #{string}" unless match
|
169
184
|
|
170
|
-
|
171
|
-
|
172
|
-
|
185
|
+
number, unit = match[1].to_i, match[2]
|
186
|
+
# Ensure the unit is a valid ActiveSupport duration method
|
187
|
+
valid_units = %w[seconds minutes hours days weeks months years]
|
188
|
+
raise ArgumentError, "Invalid unit: #{unit} (valid: #{valid_units})" unless valid_units.include?(unit)
|
189
|
+
ActiveSupport::Duration.build(number.send(unit))
|
173
190
|
end
|
191
|
+
|
174
192
|
end
|
175
193
|
end
|
@@ -2,24 +2,21 @@ module SvelteOnRails
|
|
2
2
|
module Installer
|
3
3
|
module Npm
|
4
4
|
|
5
|
-
def self.install_or_update_package(package_name,
|
5
|
+
def self.install_or_update_package(package_name, version_specifier: 'latest', dev_dependency: false)
|
6
6
|
pkg = inspect_package(package_name)
|
7
|
-
puts "#{package_name} already installed (#{pkg[:version].join('.')})" if pkg
|
7
|
+
puts "#{package_name} already installed (#{pkg[:version].join('.')}, specified: #{version_specifier})" if pkg
|
8
|
+
major_version = version_specifier.to_s.match(/^\d+(?=\.|$)/)
|
8
9
|
|
9
10
|
to_do = if !pkg
|
10
11
|
true
|
11
12
|
elsif major_version
|
12
|
-
r = pkg[:version].first != major_version
|
13
|
+
r = pkg[:version].first != major_version.to_s.to_i
|
13
14
|
if r
|
14
15
|
puts "updating to major version #{major_version}"
|
15
16
|
else
|
16
17
|
puts "nothing to do (major version #{major_version} OK)"
|
17
18
|
end
|
18
19
|
r
|
19
|
-
elsif minimal_version
|
20
|
-
r = pkg[:version].first < minimal_version.first || pkg[:version].second < minimal_version.second
|
21
|
-
puts "updating to minimal version #{minimal_version}" if r
|
22
|
-
r
|
23
20
|
else
|
24
21
|
puts 'nothing to do'
|
25
22
|
false
|
@@ -28,13 +25,7 @@ module SvelteOnRails
|
|
28
25
|
save_dev = (dev_dependency ? ' --save-dev' : '')
|
29
26
|
if to_do
|
30
27
|
|
31
|
-
cmd = if
|
32
|
-
"npm install #{package_name}@#{major_version}#{save_dev}"
|
33
|
-
elsif update_to_latest
|
34
|
-
"npm install #{package_name}@latest#{save_dev}"
|
35
|
-
else
|
36
|
-
raise "ERROR: not implemented"
|
37
|
-
end
|
28
|
+
cmd = "npm install #{package_name}#{'@' + version_specifier if version_specifier}#{save_dev}"
|
38
29
|
|
39
30
|
stdout, stderr, status = Open3.capture3(cmd)
|
40
31
|
|
@@ -55,8 +46,7 @@ module SvelteOnRails
|
|
55
46
|
puts notice
|
56
47
|
|
57
48
|
else
|
58
|
-
|
59
|
-
puts "#{package_name} already installed (#{pkg[:version].join('.')}#{min_str}), skipping."
|
49
|
+
puts "#{package_name} already installed (#{pkg[:version].join('.')}, required: «#{version_specifier}»), skipping."
|
60
50
|
end
|
61
51
|
end
|
62
52
|
|
@@ -99,34 +89,6 @@ module SvelteOnRails
|
|
99
89
|
|
100
90
|
end
|
101
91
|
|
102
|
-
# def self.check_version(current_version, minimal_version)
|
103
|
-
# if !current_version
|
104
|
-
# return false
|
105
|
-
# elsif !minimal_version.present?
|
106
|
-
# return true
|
107
|
-
# else
|
108
|
-
# compare_version_arrays(current_version, minimal_version)
|
109
|
-
# end
|
110
|
-
# end
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
# def self.compare_version_arrays(current_version, minimal_version)
|
115
|
-
# raise "ERROR: current_version must be an array" unless current_version.is_a?(Array)
|
116
|
-
#
|
117
|
-
# current_version.each_with_index do |v, i|
|
118
|
-
# raise "ERROR: current_version entries must be an integer" unless v.is_a?(Integer)
|
119
|
-
#
|
120
|
-
# if minimal_version[i]
|
121
|
-
# raise "ERROR: minimal_version entries must be an integer" unless minimal_version[i].is_a?(Integer)
|
122
|
-
# if v < minimal_version[i]
|
123
|
-
# return false
|
124
|
-
# end
|
125
|
-
# end
|
126
|
-
# end
|
127
|
-
# true
|
128
|
-
# end
|
129
|
-
|
130
92
|
end
|
131
93
|
end
|
132
94
|
end
|
@@ -2,18 +2,18 @@ module SvelteOnRails
|
|
2
2
|
module Installer
|
3
3
|
module Svelte
|
4
4
|
|
5
|
-
def self.install_svelte(
|
5
|
+
def self.install_svelte(svelte_version_specifier: 'latest', vite_plugin_svelte_version_specifier: 'latest')
|
6
6
|
puts '-' * 80
|
7
7
|
|
8
8
|
# check npm package version
|
9
9
|
|
10
10
|
npm_i = SvelteOnRails::Installer::Npm
|
11
|
-
npm_i.install_or_update_package('svelte',
|
11
|
+
npm_i.install_or_update_package('svelte', version_specifier: svelte_version_specifier)
|
12
12
|
|
13
13
|
# configure vite
|
14
14
|
|
15
15
|
vite_i = SvelteOnRails::Installer::Vite
|
16
|
-
vite_i.configure_for_svelte
|
16
|
+
vite_i.configure_for_svelte(vite_plugin_svelte_version_specifier: vite_plugin_svelte_version_specifier)
|
17
17
|
|
18
18
|
end
|
19
19
|
|
@@ -4,7 +4,7 @@ module SvelteOnRails
|
|
4
4
|
|
5
5
|
module Vite
|
6
6
|
|
7
|
-
def self.install_vite(
|
7
|
+
def self.install_vite(version_specifier: 'latest')
|
8
8
|
iu = SvelteOnRails::Installer::Utils
|
9
9
|
|
10
10
|
puts '-' * 80
|
@@ -44,18 +44,18 @@ module SvelteOnRails
|
|
44
44
|
# check npm package version
|
45
45
|
|
46
46
|
ni = SvelteOnRails::Installer::Npm
|
47
|
-
ni.install_or_update_package('vite',
|
47
|
+
ni.install_or_update_package('vite', version_specifier: version_specifier)
|
48
48
|
|
49
49
|
end
|
50
50
|
|
51
|
-
def self.configure_for_svelte(
|
51
|
+
def self.configure_for_svelte(vite_plugin_svelte_version_specifier: nil)
|
52
52
|
|
53
53
|
# add import statement
|
54
54
|
|
55
55
|
js_i = SvelteOnRails::Installer::Javascript
|
56
56
|
pkg = '@sveltejs/vite-plugin-svelte'
|
57
57
|
npm_i = SvelteOnRails::Installer::Npm
|
58
|
-
npm_i.install_or_update_package(pkg)
|
58
|
+
npm_i.install_or_update_package(pkg, version_specifier: vite_plugin_svelte_version_specifier)
|
59
59
|
|
60
60
|
# add plugin
|
61
61
|
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# lib/svelte_on_rails/view_helper_support.rb
|
2
|
+
module SvelteOnRails
|
3
|
+
|
4
|
+
module Lib
|
5
|
+
class ViewHelperSupport
|
6
|
+
attr_reader :filename, :helper_options, :html_options, :request, :conf
|
7
|
+
|
8
|
+
def initialize(file, props, request, caching = false)
|
9
|
+
|
10
|
+
@start_time = Time.now
|
11
|
+
@filename = (file.match(/\.svelte$/) ? file[0..-8] : file)
|
12
|
+
@conf = SvelteOnRails::Configuration.instance
|
13
|
+
@helper_options, @html_options, @props, @props_json = split_props(
|
14
|
+
props,
|
15
|
+
%i[class id style],
|
16
|
+
%i[ssr hydrate debug cache_key expires_in]
|
17
|
+
)
|
18
|
+
@request = request
|
19
|
+
@ssr = determine_ssr
|
20
|
+
validate_file if @conf.watch_changes?
|
21
|
+
|
22
|
+
# precompile
|
23
|
+
|
24
|
+
if !Dir.exist?(@conf.ssr_dist_folder) || @conf.watch_changes?
|
25
|
+
SvelteOnRails::Lib::Utils.watch_changes_and_precompile
|
26
|
+
end
|
27
|
+
|
28
|
+
# caching
|
29
|
+
|
30
|
+
if caching
|
31
|
+
raise '[svelte-on-rails] Caching required but Redis is not defined' unless defined?(Redis)
|
32
|
+
else
|
33
|
+
raise '[svelte-on-rails] :expires_in is not allowed for this helper' if @helper_options.key?(:expires_in)
|
34
|
+
raise '[svelte-on-rails] :cache_key is not allowed for this helper' if @helper_options.key?(:cache_key)
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
return unless ssr?
|
39
|
+
|
40
|
+
generate_cache_key
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def debug?
|
45
|
+
@helper_options[:debug]
|
46
|
+
end
|
47
|
+
|
48
|
+
def redis_expiration_seconds
|
49
|
+
(conf.redis_cache_store[:expires_in] || 1.hour).to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
def split_props(props, html_options, helper_options)
|
53
|
+
prp = {}
|
54
|
+
hlp_opts = {}
|
55
|
+
ht_opts = {
|
56
|
+
data: {
|
57
|
+
svelte_component: "/#{conf.components_folder + filename}",
|
58
|
+
controller: 'svelte-on-rails',
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
props.each do |k, v|
|
63
|
+
_k = k.to_sym
|
64
|
+
if helper_options.include?(_k)
|
65
|
+
hlp_opts[_k] = v
|
66
|
+
elsif html_options.include?(_k)
|
67
|
+
ht_opts[_k] = v
|
68
|
+
else
|
69
|
+
prp[_k] = v
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
ht_opts[:class] = "#{ht_opts[:class]} svelte-component".strip
|
74
|
+
prp_js = prp.to_json
|
75
|
+
ht_opts[:data][:props] = prp_js
|
76
|
+
ht_opts[:data][:svelte_status] = 'do-not-hydrate-me' if hlp_opts[:hydrate] == false
|
77
|
+
|
78
|
+
[hlp_opts, ht_opts, prp, prp_js]
|
79
|
+
end
|
80
|
+
|
81
|
+
def elapsed_milliseconds
|
82
|
+
((Time.now - @start_time) * 1000).round(1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def cache_key_primary
|
86
|
+
@cache_key_primary
|
87
|
+
end
|
88
|
+
|
89
|
+
def cache_key
|
90
|
+
@cache_key
|
91
|
+
end
|
92
|
+
|
93
|
+
def ssr?
|
94
|
+
@ssr
|
95
|
+
end
|
96
|
+
|
97
|
+
def file_not_found_message
|
98
|
+
ff = conf.frontend_folder
|
99
|
+
cf = conf.components_folder
|
100
|
+
"svelte-on-rails gem, view helper #svelte_component\n\nFile not found:\n" +
|
101
|
+
"#{conf.components_folder_full + "#{filename}.svelte"}\n\n" +
|
102
|
+
"Your configurations are:\n\nfrontend_folder: «#{ff}»\ncomponents_folder: «#{cf}»\n.. and the filename attribute for the view helper: «#{filename}»\n"
|
103
|
+
end
|
104
|
+
|
105
|
+
def file_case_sensitive_message
|
106
|
+
ff = conf.frontend_folder
|
107
|
+
cf = conf.components_folder
|
108
|
+
"svelte-on-rails gem, view helper #svelte_component\n\n" +
|
109
|
+
"File found but Upper and lower case letters are incorrect:\n" +
|
110
|
+
"(This check is only on development environments when watch_changes is true)\n\n" +
|
111
|
+
"#{conf.components_folder_full + "#{filename}.svelte"}\n\n" +
|
112
|
+
"Your configurations are:\nfrontend_folder: «#{ff}»\ncomponents_folder: «#{cf}»\n.. and the filename attribute for the view helper: «#{filename}»\n"
|
113
|
+
end
|
114
|
+
|
115
|
+
def log_rendering(message)
|
116
|
+
|
117
|
+
Rails.logger.info " #{message} (#{elapsed_milliseconds}ms)"
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def debug_log(message)
|
122
|
+
if debug?
|
123
|
+
Rails.logger.debug(" [svelte_component] #{message} (#{elapsed_milliseconds} ms)")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def render_ssr
|
128
|
+
renderer = SvelteOnRails::Renderer.new(filename)
|
129
|
+
renderer.render(@props)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def validate_file
|
135
|
+
file_path = conf.components_folder_full + "#{filename}.svelte"
|
136
|
+
unless File.exist?(file_path)
|
137
|
+
raise file_not_found_message
|
138
|
+
end
|
139
|
+
if conf.watch_changes? && !SvelteOnRails::Lib::Utils.file_exist_case_sensitive?(conf.components_folder_full, "#{filename}.svelte")
|
140
|
+
raise file_case_sensitive_message
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def determine_ssr
|
145
|
+
_ssr = if @helper_options.key?(:ssr)
|
146
|
+
if @conf.watch_changes?
|
147
|
+
unless [true, false, :auto].include?(@helper_options[:ssr])
|
148
|
+
raise "Only true, false, or :auto are allowed for the argument #ssr"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@helper_options[:ssr]
|
152
|
+
else
|
153
|
+
@conf.ssr
|
154
|
+
end
|
155
|
+
_ssr == :auto ? request.headers[conf.non_ssr_request_header].blank? : _ssr
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def generate_cache_key
|
161
|
+
|
162
|
+
mtime_file = @conf.ssr_dist_folder.join('last_mtime')
|
163
|
+
mtime = File.read(mtime_file)
|
164
|
+
|
165
|
+
key2 = if @helper_options[:cache_key]
|
166
|
+
k2 = (@helper_options[:cache_key])
|
167
|
+
keys = k2.is_a?(Array) ? k2 : [k2]
|
168
|
+
keys.map { |k| k.is_a?(ActiveRecord::Base) ? "#{k.class.name}#{k.id}" : k.to_s }.join('-')
|
169
|
+
end
|
170
|
+
|
171
|
+
filename_part = [
|
172
|
+
"#{filename.split('/').last}.svelte",
|
173
|
+
Zlib.crc32(filename).to_s(36),
|
174
|
+
key2
|
175
|
+
].join('-')
|
176
|
+
|
177
|
+
@cache_key_primary = [
|
178
|
+
conf.redis_cache_store[:namespace] ? conf.redis_cache_store[:namespace] : "svelte-on-rails:#{Rails.env}",
|
179
|
+
filename_part,
|
180
|
+
].join(':')
|
181
|
+
|
182
|
+
last_part = [
|
183
|
+
(@conf.watch_changes? ? Zlib.crc32(mtime).to_s(36) : nil),
|
184
|
+
Zlib.crc32(@props_json).to_s(36)
|
185
|
+
].compact.join('-')
|
186
|
+
|
187
|
+
@cache_key = [@cache_key_primary, last_part].join(':')
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -11,10 +11,6 @@ module SvelteOnRails
|
|
11
11
|
raise "Node.js not found at '#{config.node_bin_path}'. Please configure SvelteOnRails.node_bin (e.g., to ~/.nvm/versions/node/vX.Y.Z/bin/node) or ensure 'node' is in the PATH. If using NVM, run `nvm which default` to find the path."
|
12
12
|
end
|
13
13
|
|
14
|
-
if !Dir.exist?(config.ssr_dist_folder) || config.watch_changes?
|
15
|
-
SvelteOnRails::Lib::Utils.watch_changes_and_precompile
|
16
|
-
end
|
17
|
-
|
18
14
|
utils = SvelteOnRails::Lib::Utils
|
19
15
|
@component_files = utils.component_files(component_name, base_path: base_path)
|
20
16
|
|
@@ -1,105 +1,84 @@
|
|
1
|
+
# lib/svelte_on_rails/view_helpers.rb
|
1
2
|
module SvelteOnRails
|
2
3
|
module ViewHelpers
|
3
4
|
|
4
5
|
def svelte_component(filename, props = {})
|
5
6
|
|
6
|
-
|
7
|
-
conf = SvelteOnRails::Configuration.instance
|
8
|
-
cff = conf.components_folder_full
|
9
|
-
file_path = cff + "#{filename}.svelte"
|
10
|
-
if !File.exist?(file_path)
|
11
|
-
raise file_not_found_messsage(filename)
|
12
|
-
elsif conf.watch_changes? && !SvelteOnRails::Lib::Utils.file_exist_case_sensitive?(cff, filename + '.svelte')
|
13
|
-
# on development environments we check case sensitivity too
|
14
|
-
msg = "File found but Upper and lower case letters are incorrect\n" +
|
15
|
-
"(This check is only on development environments when watch_changes is true):\n."
|
16
|
-
raise file_case_sensitive_messsage(filename)
|
17
|
-
end
|
7
|
+
support = SvelteOnRails::Lib::ViewHelperSupport.new(filename, props, request, false)
|
18
8
|
|
19
|
-
|
9
|
+
support.debug_log("Rendering component: #{filename}")
|
10
|
+
log_message = '?'
|
20
11
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
log_message = "Rendered #{support.filename}.svelte #{'as empty element that will be mounted on the client side' unless support.ssr?}"
|
13
|
+
res = render_component(support)
|
14
|
+
|
15
|
+
support.log_rendering(log_message)
|
16
|
+
|
17
|
+
res
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def cached_svelte_component(filename, props = {})
|
22
|
+
|
23
|
+
support = SvelteOnRails::Lib::ViewHelperSupport.new(filename, props, request, true)
|
24
|
+
|
25
|
+
support.debug_log("Rendering component: #{filename}")
|
26
|
+
log_message = '?'
|
27
|
+
redis = support.conf.redis_instance
|
28
|
+
|
29
|
+
support.debug_log("Redis configuration: #{support.conf.redis_cache_store}")
|
30
|
+
|
31
|
+
# TTL
|
32
|
+
if support.debug?
|
33
|
+
ttl = redis.ttl(support.cache_key)
|
34
|
+
support.debug_log("Cache key: #{support.cache_key} (ttl was: #{ttl} seconds, now set to: #{support.redis_expiration_seconds} seconds)")
|
28
35
|
end
|
36
|
+
redis.expire(support.cache_key, support.redis_expiration_seconds)
|
37
|
+
|
38
|
+
cached_content = redis.get(support.cache_key)
|
29
39
|
|
30
|
-
|
31
|
-
|
40
|
+
res = if cached_content
|
41
|
+
log_message = "Returned #{support.filename}.svelte from cache"
|
42
|
+
cached_content.html_safe
|
32
43
|
else
|
33
|
-
|
44
|
+
log_message = "Rendered #{support.filename}.svelte and stored to cache"
|
45
|
+
support.debug_log("cache recalculating for key: #{support.cache_key}")
|
46
|
+
r = render_component(support)
|
47
|
+
support.debug_log("cache recalculated")
|
48
|
+
|
49
|
+
redis.scan_each(match: "#{support.cache_key_primary}:*") do |key|
|
50
|
+
redis.del(key)
|
51
|
+
support.debug_log("deleted cache: #{key}")
|
52
|
+
end
|
53
|
+
|
54
|
+
redis.set(support.cache_key, r)
|
55
|
+
r
|
34
56
|
end
|
35
57
|
|
36
|
-
|
37
|
-
props[:hydrate]
|
38
|
-
else
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
|
-
# separate hashes
|
43
|
-
|
44
|
-
props.delete(:hydrate)
|
45
|
-
props.delete(:ssr)
|
46
|
-
options = props.slice(:class, :id, :style)
|
47
|
-
props.delete(:class)
|
48
|
-
props.delete(:id)
|
49
|
-
props.delete(:style)
|
58
|
+
support.log_rendering(log_message)
|
50
59
|
|
51
|
-
|
60
|
+
res
|
52
61
|
|
53
|
-
|
54
|
-
options[:data] ||= {}
|
55
|
-
options[:data][:svelte_status] = 'do-not-hydrate-me' unless hydrate
|
56
|
-
options[:data][:props] = props.to_json
|
57
|
-
options[:data][:svelte_component] = "/#{conf.components_folder + filename}"
|
58
|
-
options[:data][:controller] = 'svelte-on-rails'
|
62
|
+
end
|
59
63
|
|
60
|
-
|
64
|
+
private
|
61
65
|
|
62
|
-
|
66
|
+
def render_component(support)
|
63
67
|
|
64
|
-
|
65
|
-
rend = SvelteOnRails::Renderer.new(filename)
|
66
|
-
res = rend.render(props)
|
67
|
-
time = Time.now - start_time
|
68
|
-
Rails.logger.info " Rendered #{filename}.svelte server-side: #{time.round(3)}ms"
|
68
|
+
if support.ssr?
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
ssr_result = support.render_ssr
|
71
|
+
content_tag(:div, support.html_options) do
|
72
|
+
concat(content_tag(:style, ssr_result['css'], type: 'text/css'))
|
73
|
+
concat(ssr_result['html'].html_safe)
|
73
74
|
end
|
74
75
|
|
75
76
|
else
|
76
77
|
|
77
|
-
|
78
|
-
Rails.logger.info " Rendered #{filename}.svelte as empty element that will be mounted on the client side"
|
79
|
-
content_tag(:div, options) {}
|
78
|
+
content_tag(:div, support.html_options) {}
|
80
79
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
84
|
-
def file_not_found_messsage(filename)
|
85
|
-
conf = SvelteOnRails::Configuration.instance
|
86
|
-
ff = conf.frontend_folder
|
87
|
-
cf = conf.components_folder
|
88
|
-
"svelte-on-rails gem, view helper #svelte_component\n\nFile not found:\n" +
|
89
|
-
"#{conf.components_folder_full + "#{filename}.svelte"}\n\n" +
|
90
|
-
"Your configurations are:\n\nfrontend_folder: «#{ff}»\ncomponents_folder: «#{cf}»\n.. and the filename attribute for the view helper: «#{filename}»\n"
|
91
|
-
end
|
92
|
-
|
93
|
-
def file_case_sensitive_messsage(filename)
|
94
|
-
conf = SvelteOnRails::Configuration.instance
|
95
|
-
ff = conf.frontend_folder
|
96
|
-
cf = conf.components_folder
|
97
|
-
"svelte-on-rails gem, view helper #svelte_component\n\n" +
|
98
|
-
"File found but Upper and lower case letters are incorrect:\n" +
|
99
|
-
"(This check is only on development environments when watch_changes is true)\n\n" +
|
100
|
-
"#{conf.components_folder_full + "#{filename}.svelte"}\n\n" +
|
101
|
-
"Your configurations are:\nfrontend_folder: «#{ff}»\ncomponents_folder: «#{cf}»\n.. and the filename attribute for the view helper: «#{filename}»\n"
|
102
|
-
end
|
103
|
-
|
104
83
|
end
|
105
84
|
end
|
@@ -16,6 +16,11 @@ non_ssr_request_header: 'X-Turbo-Request-ID'
|
|
16
16
|
# when this header contains any value (not blank), we are skipping server-side rendering
|
17
17
|
# when this header is empty we assume initial request and doing server-side rendering
|
18
18
|
|
19
|
+
# redis_cache_store:
|
20
|
+
# url: 'redis://localhost:6379/2'
|
21
|
+
# expires_in: 90.minutes (=> default / fallback: 1 hour)
|
22
|
+
# namespace: 'my-app-svelte-on-rails'
|
23
|
+
|
19
24
|
development:
|
20
25
|
watch_changes: true
|
21
26
|
# Check on every request if any file within the svelte components folder have changed, for recompiling
|
@@ -24,5 +29,6 @@ development:
|
|
24
29
|
|
25
30
|
test:
|
26
31
|
watch_changes: true
|
32
|
+
cache_expiration_hours: 15
|
27
33
|
|
28
34
|
production:
|
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: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Sedlmair
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib/svelte_on_rails/installer/vite.rb
|
43
43
|
- lib/svelte_on_rails/lib/development_utils.rb
|
44
44
|
- lib/svelte_on_rails/lib/utils.rb
|
45
|
+
- lib/svelte_on_rails/lib/view_helper_support.rb
|
45
46
|
- lib/svelte_on_rails/railtie.rb
|
46
47
|
- lib/svelte_on_rails/renderer/render.js
|
47
48
|
- lib/svelte_on_rails/renderer/renderer.rb
|