svelte-on-rails 20.0.18 → 21.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/lib/svelte_on_rails/configuration.rb +44 -10
- data/lib/svelte_on_rails/lib/utils.rb +8 -7
- data/lib/svelte_on_rails/lib/view_helper_support.rb +25 -10
- data/lib/svelte_on_rails/railtie.rb +2 -2
- data/lib/svelte_on_rails/ssr_server.rb +110 -70
- data/lib/svelte_on_rails/view_helpers.rb +11 -3
- data/lib/tasks/svelte_on_rails_tasks.rake +83 -15
- data/templates/config_base/config/svelte_on_rails.yml +34 -13
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d147f7ea42b28a7d979d93b2ba336fe2076763d1b05d248ff6b2d17dc293ef10
|
|
4
|
+
data.tar.gz: b33ff7321a8db1c2b58350d8a433d75329727f625030e96a53e70ecaf093b572
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 063f55d363284af5924f1a07966b1da39f1b17c123ad93060b29fa6832941d77337de852d9a9f9b38fe9cf4489a3d59852bc50b1a067176ddd1ca67db5a11b15
|
|
7
|
+
data.tar.gz: 06fc37fa2b5fa1cebdfb8fc4758b9fa27e87a0d7b09484df648dee441ffc2df5a79f48da79d6549adf06c139289b233a0628f5d4c07d353eb6df54f753d4c403
|
|
@@ -16,6 +16,7 @@ module SvelteOnRails
|
|
|
16
16
|
|
|
17
17
|
@boot_key = SecureRandom.hex(3) # currently not used, more for debugging purposes
|
|
18
18
|
@configs = redis_cache_store_configs
|
|
19
|
+
set_config_defaults
|
|
19
20
|
|
|
20
21
|
@component_paths_cache = {}
|
|
21
22
|
@request_metrics = {}
|
|
@@ -36,9 +37,11 @@ module SvelteOnRails
|
|
|
36
37
|
merged_configs = configs_base.deep_merge(env_configs || {})
|
|
37
38
|
|
|
38
39
|
# cleanup
|
|
39
|
-
|
|
40
|
+
polished_configs = merged_configs.reject { |k, _| environments.include?(k.to_s) }
|
|
41
|
+
@configs = @configs.merge(polished_configs)
|
|
42
|
+
|
|
43
|
+
# DEFAULTS
|
|
40
44
|
|
|
41
|
-
# caching defaults
|
|
42
45
|
if @configs[:redis_cache_store]
|
|
43
46
|
store = @configs[:redis_cache_store]
|
|
44
47
|
if store['expires_in'].is_a?(String)
|
|
@@ -54,10 +57,18 @@ module SvelteOnRails
|
|
|
54
57
|
@redis_instance = Redis.new(url: redis_cache_store[:url])
|
|
55
58
|
end
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
validate_serialized_configs
|
|
61
|
+
custom_config_validations
|
|
58
62
|
validate_npm_packages
|
|
59
63
|
end
|
|
60
64
|
|
|
65
|
+
def set_config_defaults
|
|
66
|
+
@configs[:ssr_server] = true
|
|
67
|
+
@configs[:ssr_server_workers] = 1
|
|
68
|
+
@configs[:ssr_fallback_renderer] = false
|
|
69
|
+
@configs[:ssr_error_tag] = true
|
|
70
|
+
end
|
|
71
|
+
|
|
61
72
|
def redis_cache_store
|
|
62
73
|
(@configs[:redis_cache_store] || {}).transform_keys(&:to_sym)
|
|
63
74
|
end
|
|
@@ -139,7 +150,9 @@ module SvelteOnRails
|
|
|
139
150
|
end
|
|
140
151
|
|
|
141
152
|
def ssr_server_bin_path
|
|
142
|
-
|
|
153
|
+
wk = @configs[:ssr_server_workers]
|
|
154
|
+
str = wk > 1 ? 'cluster' : 'server'
|
|
155
|
+
Rails.root.join(Pathname("node_modules/@csedl/svelte-on-rails/bin/svelte-ssr-#{str}.js"))
|
|
143
156
|
end
|
|
144
157
|
|
|
145
158
|
def initialize_request_metrics(request_uuid)
|
|
@@ -184,7 +197,7 @@ module SvelteOnRails
|
|
|
184
197
|
ActiveSupport::Duration.build(number.send(unit))
|
|
185
198
|
end
|
|
186
199
|
|
|
187
|
-
def
|
|
200
|
+
def validate_serialized_configs
|
|
188
201
|
|
|
189
202
|
# custom validations
|
|
190
203
|
|
|
@@ -196,9 +209,13 @@ module SvelteOnRails
|
|
|
196
209
|
|
|
197
210
|
keys = {
|
|
198
211
|
vite_source_dir: { required: :string },
|
|
199
|
-
use_ssr_server: { required: :boolean },
|
|
200
212
|
use_caching: { required: :boolean },
|
|
201
213
|
|
|
214
|
+
ssr_server: { optional: :boolean },
|
|
215
|
+
ssr_server_workers: { optional: :integer },
|
|
216
|
+
ssr_fallback_renderer: { optional: :boolean },
|
|
217
|
+
ssr_error_tag: { optional: :boolean },
|
|
218
|
+
|
|
202
219
|
cache_expiration_hours: { optional: :integer },
|
|
203
220
|
components_subdir: { optional: :string },
|
|
204
221
|
default_components_root: { required: :string },
|
|
@@ -262,6 +279,15 @@ module SvelteOnRails
|
|
|
262
279
|
|
|
263
280
|
end
|
|
264
281
|
|
|
282
|
+
def custom_config_validations
|
|
283
|
+
if !@configs[:ssr_server] && !@configs[:ssr_fallback_renderer]
|
|
284
|
+
raise "[SOR] Configuration error: Either :ssr_server or :ssr_fallback_renderer must be set to true"
|
|
285
|
+
end
|
|
286
|
+
if @configs[:ssr_server_workers] < 1
|
|
287
|
+
raise "[SOR] Configuration error: :ssr_server_workers must be greater than 0"
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
265
291
|
def validate_npm_packages
|
|
266
292
|
pkg = JSON.parse(File.read(Rails.root.join('package.json')))
|
|
267
293
|
deps = (pkg['dependencies'] || {}).merge(pkg['devDependencies'] || {})
|
|
@@ -280,12 +306,20 @@ module SvelteOnRails
|
|
|
280
306
|
raise "[Svelte-on-Rails] NPM-Package @csedl/svelte-on-rails: Could not detect version from: «#{sor}»"
|
|
281
307
|
end
|
|
282
308
|
|
|
283
|
-
|
|
284
|
-
|
|
309
|
+
# npm package version
|
|
310
|
+
|
|
311
|
+
if Gem::Version.new(version) < Gem::Version.new('15.0.0')
|
|
312
|
+
raise "[Svelte-on-Rails] NPM-Package @csedl/svelte-on-rails: At least version 15.0.0 is required, but found: «#{sor}»"
|
|
285
313
|
end
|
|
286
314
|
|
|
287
|
-
|
|
288
|
-
|
|
315
|
+
# check version
|
|
316
|
+
if @configs[:ssr]
|
|
317
|
+
unless node_version_test.to_s.match(/v\d+\.\d+\.\d+/)
|
|
318
|
+
raise "[Svelte-on-Rails] Node Version could not be detected.\nPlease check your Node.js installation:\n#{@node_bin_path_log.join("\n")}"
|
|
319
|
+
end
|
|
320
|
+
if Gem::Version.new(node_version_test.to_s.delete_prefix('v')) < Gem::Version.new('22.0.0')
|
|
321
|
+
raise "[Svelte-on-Rails] Node Version (#{}) is too old, at least version 22.0.0 is required"
|
|
322
|
+
end
|
|
289
323
|
end
|
|
290
324
|
end
|
|
291
325
|
|
|
@@ -153,16 +153,17 @@ module SvelteOnRails
|
|
|
153
153
|
trace[0..last_match_index]
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
+
# Log for debugging purposes
|
|
156
157
|
def self.secure_debug_log(line)
|
|
157
|
-
base = Rails.root.join('tmp')
|
|
158
|
+
# base = Rails.root.join('tmp')
|
|
158
159
|
|
|
159
|
-
FileUtils.mkdir_p(base) unless File.directory?(base)
|
|
160
|
-
path = File.join(base.to_s, '
|
|
160
|
+
# FileUtils.mkdir_p(base) unless File.directory?(base)
|
|
161
|
+
# path = File.join(base.to_s, 'svelte-on-rails.log')
|
|
161
162
|
|
|
162
|
-
File.open(path, 'a') do |f|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
end
|
|
163
|
+
# File.open(path, 'a') do |f|
|
|
164
|
+
# f.sync = true
|
|
165
|
+
# f.puts("[#{Time.now.utc.iso8601(3)}] [pid=#{Process.pid}] #{line}")
|
|
166
|
+
# end
|
|
166
167
|
rescue => e
|
|
167
168
|
# Last resort: write the failure itself to stderr; never raise.
|
|
168
169
|
warn "[SOR] sor_debug_log failed: #{e.class}: #{e.message}"
|
|
@@ -164,16 +164,8 @@ module SvelteOnRails
|
|
|
164
164
|
caller_locations.find { |loc| loc.to_s.include?(v_path) }.to_s.match(/^[\s\S]+(:[0-9]+(?=:in))/).to_s
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
-
r =
|
|
168
|
-
|
|
169
|
-
component_paths,
|
|
170
|
-
cached_props,
|
|
171
|
-
debug?,
|
|
172
|
-
caller_loc
|
|
173
|
-
)
|
|
174
|
-
else
|
|
175
|
-
SvelteOnRails::Lib::FallbackRenderer.render(component_paths, cached_props, debug?)
|
|
176
|
-
end
|
|
167
|
+
r = render_with_ssr_server(caller_loc) || render_with_fallback
|
|
168
|
+
|
|
177
169
|
if r[:success]
|
|
178
170
|
set_request_metrics(:rendered)
|
|
179
171
|
else
|
|
@@ -237,6 +229,24 @@ module SvelteOnRails
|
|
|
237
229
|
|
|
238
230
|
private
|
|
239
231
|
|
|
232
|
+
def render_with_ssr_server(caller_loc)
|
|
233
|
+
return {} unless @conf.configs[:ssr_server]
|
|
234
|
+
|
|
235
|
+
result = SvelteOnRails::SsrServer.instance.render(
|
|
236
|
+
component_paths,
|
|
237
|
+
cached_props,
|
|
238
|
+
debug?,
|
|
239
|
+
caller_loc
|
|
240
|
+
)
|
|
241
|
+
result[:success] ? result : render_with_fallback
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def render_with_fallback
|
|
245
|
+
return {} unless @conf.configs[:ssr_fallback_renderer]
|
|
246
|
+
|
|
247
|
+
SvelteOnRails::Lib::FallbackRenderer.render(component_paths, cached_props, debug?)
|
|
248
|
+
end
|
|
249
|
+
|
|
240
250
|
def run_vite_build
|
|
241
251
|
r = @conf.vite_build
|
|
242
252
|
|
|
@@ -298,6 +308,11 @@ module SvelteOnRails
|
|
|
298
308
|
end
|
|
299
309
|
|
|
300
310
|
def resolve_ssr?(request)
|
|
311
|
+
|
|
312
|
+
# Setting SSR to false at the global level will disable SSR.
|
|
313
|
+
# Reason: In emergency cases, the developer may want to disable SSR.
|
|
314
|
+
return false if @conf.configs[:ssr] == false
|
|
315
|
+
|
|
301
316
|
_ssr = if !@options[:ssr].nil?
|
|
302
317
|
@options[:ssr]
|
|
303
318
|
elsif !@conf.configs[:ssr].nil?
|
|
@@ -18,9 +18,9 @@ module SvelteOnRails
|
|
|
18
18
|
app.config.after_initialize do
|
|
19
19
|
|
|
20
20
|
if defined?(Rake) && Rake.respond_to?(:application) && Rake.application.top_level_tasks.any?
|
|
21
|
-
puts "[Skipping svelte-on-rails because of top level task: #{Rake.application.top_level_tasks.join(', ')}]"
|
|
21
|
+
puts "[Skipping svelte-on-rails SSR-Server because of top level task: #{Rake.application.top_level_tasks.join(', ')}]"
|
|
22
22
|
$stdout.flush
|
|
23
|
-
|
|
23
|
+
elsif SvelteOnRails::Configuration.instance.configs[:ssr]
|
|
24
24
|
SvelteOnRails::Lib::Utils.secure_debug_log("Initializing SSR client")
|
|
25
25
|
SvelteOnRails::SsrServer.instance
|
|
26
26
|
SvelteOnRails::Lib::Utils.secure_debug_log("Initializing SSR client finished")
|
|
@@ -28,13 +28,12 @@ module SvelteOnRails
|
|
|
28
28
|
@logfile_path = Rails.root.join("log/svelte-ssr-server-#{Rails.env}.log")
|
|
29
29
|
@logfile_max_lines = build_logfile_max_lines
|
|
30
30
|
@socket_path = @helpers.socket_path
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@ssr_server_bin_path = SvelteOnRails::Configuration.instance.ssr_server_bin_path
|
|
31
|
+
@ssr_server = SvelteOnRails::Configuration.instance.configs[:ssr_server]
|
|
32
|
+
@ssr_server_bin_path = @helpers.ssr_server_bin_path
|
|
34
33
|
|
|
35
34
|
File.delete(restart_marker_file) if File.exist?(restart_marker_file)
|
|
36
35
|
|
|
37
|
-
if @
|
|
36
|
+
if @ssr_server
|
|
38
37
|
# if !defined?(Rails::Server)
|
|
39
38
|
# SvelteOnRails::Lib::Utils.secure_debug_log("Rails Server is not yet defined, SSR Server cannot be started because of initializing too early")
|
|
40
39
|
# else
|
|
@@ -48,8 +47,8 @@ module SvelteOnRails
|
|
|
48
47
|
File.delete(restart_marker_file) if File.exist?(restart_marker_file)
|
|
49
48
|
end
|
|
50
49
|
else
|
|
51
|
-
SvelteOnRails::Lib::Utils.secure_debug_log("SSR Server NOT used => @
|
|
52
|
-
SvelteOnRails::Lib::Utils.secure_debug_log("SSR Server NOT used => @
|
|
50
|
+
SvelteOnRails::Lib::Utils.secure_debug_log("SSR Server NOT used => @ssr_server #{@ssr_server}")
|
|
51
|
+
SvelteOnRails::Lib::Utils.secure_debug_log("SSR Server NOT used => @ssr_server #{defined?(Rails::Server)}")
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
end
|
|
@@ -149,7 +148,6 @@ module SvelteOnRails
|
|
|
149
148
|
end
|
|
150
149
|
|
|
151
150
|
def ping?
|
|
152
|
-
@alive = false
|
|
153
151
|
shorten_logfile
|
|
154
152
|
unless File.exist?(socket_path)
|
|
155
153
|
return false
|
|
@@ -169,7 +167,7 @@ module SvelteOnRails
|
|
|
169
167
|
unless r['version'].split('.').first.to_i >= 14
|
|
170
168
|
puts "[SOR] WARNING: Required SSR Server version is at least 14.x, you have installed @csedl/svelte-on-rails: #{r['version']}"
|
|
171
169
|
end
|
|
172
|
-
|
|
170
|
+
r['status'] == 'alive'
|
|
173
171
|
end
|
|
174
172
|
end
|
|
175
173
|
|
|
@@ -182,17 +180,11 @@ module SvelteOnRails
|
|
|
182
180
|
File.write(@helpers.ssr_server_ppid_file, Process.ppid.to_s)
|
|
183
181
|
|
|
184
182
|
start = Time.now
|
|
185
|
-
cmd = [
|
|
186
|
-
"SVELTE_SSR_SERVER_STARTED_BY=#{started_by}",
|
|
187
|
-
@node_bin,
|
|
188
|
-
@ssr_server_bin_path,
|
|
189
|
-
@socket_path
|
|
190
|
-
].join(' ')
|
|
191
183
|
|
|
192
184
|
@node_server_output = {}
|
|
193
185
|
Thread.new do
|
|
194
186
|
stdout, stderr, status = Open3.capture3(
|
|
195
|
-
|
|
187
|
+
@helpers.start_node_server_command(started_by),
|
|
196
188
|
chdir: Rails.root
|
|
197
189
|
)
|
|
198
190
|
@node_server_output = {
|
|
@@ -201,57 +193,30 @@ module SvelteOnRails
|
|
|
201
193
|
status: status
|
|
202
194
|
}
|
|
203
195
|
end
|
|
204
|
-
start_server_log(start, "Node Version: #{Configuration.instance.node_version_test}, package version: #{Configuration.instance.npm_package_version_test}")
|
|
205
|
-
start_server_log(start, "Start running Svelte-SSR-Server... (#{started_by})")
|
|
196
|
+
@helpers.start_server_log(start, "Node Version: #{Configuration.instance.node_version_test}, package version: #{Configuration.instance.npm_package_version_test}")
|
|
197
|
+
@helpers.start_server_log(start, "Start running Svelte-SSR-Server... (#{started_by})")
|
|
206
198
|
|
|
207
199
|
until node_server_pid do
|
|
208
200
|
sleep 0.12
|
|
209
|
-
start_server_log(start, "Waiting for node server process-id")
|
|
201
|
+
@helpers.start_server_log(start, "Waiting for node server process-id")
|
|
210
202
|
start_server_timeout!(start)
|
|
211
203
|
end
|
|
212
204
|
|
|
213
205
|
until File.exist?(socket_path) do
|
|
214
206
|
sleep 0.1
|
|
215
|
-
start_server_log(start, "Waiting for socket to be created")
|
|
207
|
+
@helpers.start_server_log(start, "Waiting for socket to be created")
|
|
216
208
|
start_server_timeout!(start)
|
|
217
209
|
end
|
|
218
|
-
start_server_log(start, "Socket created: #{socket_path}")
|
|
210
|
+
@helpers.start_server_log(start, "Socket created: #{socket_path}")
|
|
219
211
|
|
|
220
212
|
until ping? do
|
|
221
|
-
start_server_log(start, "Waiting for successful server-response")
|
|
213
|
+
@helpers.start_server_log(start, "Waiting for successful server-response")
|
|
222
214
|
start_server_timeout!(start)
|
|
223
215
|
sleep 0.1
|
|
224
216
|
end
|
|
225
|
-
start_server_log(start, "Server responded successfully")
|
|
226
|
-
|
|
227
|
-
kill_leftover_processes
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def kill_leftover_processes
|
|
231
|
-
stdout, stderr, status = Open3.capture3(
|
|
232
|
-
"ps aux | grep svelte"
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
leftover = stdout.split("\n").map do |line|
|
|
236
|
-
if line.match?(/\/#{@socket_namespace}-[0-9]+.sock$/)
|
|
237
|
-
unless line.match?(/\/#{@socket_namespace}-#{Process.ppid}.sock$/)
|
|
238
|
-
line.match(/[0-9]+/).to_s.to_i
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end.compact
|
|
242
|
-
|
|
243
|
-
stdout, stderr, status = Open3.capture3(
|
|
244
|
-
"kill #{leftover.join(' ')}"
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
start_server_log(nil, "Killed #{leftover.length} leftover processes") if leftover.length >= 1
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def kill_my_server
|
|
251
|
-
Process.kill("SIGINT", (node_server_pid || 0)) if node_server_pid
|
|
252
|
-
File.delete(@helpers.ssr_server_ppid_file)
|
|
253
|
-
rescue
|
|
217
|
+
@helpers.start_server_log(start, "Server responded successfully")
|
|
254
218
|
|
|
219
|
+
@helpers.cleanup_leftover_processes
|
|
255
220
|
end
|
|
256
221
|
|
|
257
222
|
def restart_my_server(tag = nil)
|
|
@@ -274,10 +239,6 @@ module SvelteOnRails
|
|
|
274
239
|
end
|
|
275
240
|
end
|
|
276
241
|
|
|
277
|
-
def alive?
|
|
278
|
-
@alive
|
|
279
|
-
end
|
|
280
|
-
|
|
281
242
|
def node_server_pid
|
|
282
243
|
stdout, stderr, status = Open3.capture3(
|
|
283
244
|
"ps aux | grep svelte"
|
|
@@ -320,21 +281,6 @@ module SvelteOnRails
|
|
|
320
281
|
end
|
|
321
282
|
end
|
|
322
283
|
|
|
323
|
-
def start_server_log(start_time, msg)
|
|
324
|
-
line =
|
|
325
|
-
if start_time
|
|
326
|
-
ms = ((Time.now - start_time) * 1000).round(1)
|
|
327
|
-
"[SOR] #{msg} (#{ms}ms)"
|
|
328
|
-
else
|
|
329
|
-
"[SOR] #{msg}"
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
puts line
|
|
333
|
-
$stdout.flush
|
|
334
|
-
|
|
335
|
-
SvelteOnRails::Lib::Utils.secure_debug_log(line)
|
|
336
|
-
end
|
|
337
|
-
|
|
338
284
|
def stop_server_timeout!(start_time, timeout: 2000)
|
|
339
285
|
ms = ((Time.now - start_time) * 1000).round(1)
|
|
340
286
|
if ms > timeout
|
|
@@ -385,7 +331,8 @@ module SvelteOnRails
|
|
|
385
331
|
# For that reason they are within a separate class
|
|
386
332
|
|
|
387
333
|
attr_reader :socket_namespace,
|
|
388
|
-
:socket_path
|
|
334
|
+
:socket_path,
|
|
335
|
+
:ssr_server_bin_path
|
|
389
336
|
|
|
390
337
|
def initialize
|
|
391
338
|
|
|
@@ -395,6 +342,8 @@ module SvelteOnRails
|
|
|
395
342
|
Rails.env,
|
|
396
343
|
].join("-")
|
|
397
344
|
|
|
345
|
+
@ssr_server_bin_path = SvelteOnRails::Configuration.instance.ssr_server_bin_path
|
|
346
|
+
|
|
398
347
|
end
|
|
399
348
|
|
|
400
349
|
def ssr_server_ppid_file
|
|
@@ -417,6 +366,97 @@ module SvelteOnRails
|
|
|
417
366
|
fallback
|
|
418
367
|
end
|
|
419
368
|
|
|
369
|
+
def start_node_server_command(started_by, ppid: Process.ppid)
|
|
370
|
+
workers = SvelteOnRails::Configuration.instance.configs[:ssr_server_workers]
|
|
371
|
+
[
|
|
372
|
+
(started_by ? "SVELTE_SSR_SERVER_STARTED_BY=#{started_by}" : nil),
|
|
373
|
+
SvelteOnRails::Configuration.instance.node_bin_path,
|
|
374
|
+
@ssr_server_bin_path,
|
|
375
|
+
socket_path(ppid: ppid),
|
|
376
|
+
(workers > 1 ? "--workers=#{workers}" : nil)
|
|
377
|
+
].compact.join(' ')
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
SsrProcess = Struct.new(:pid, :ppid, :socket_path, keyword_init: true)
|
|
381
|
+
|
|
382
|
+
def own_processes(current_ppid: Process.ppid)
|
|
383
|
+
scan_processes.select { |p| p.ppid == current_ppid }
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def leftover_processes(current_ppid: Process.ppid)
|
|
387
|
+
scan_processes.reject { |p| p.ppid == current_ppid }
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def leftover_sockets(current_ppid: Process.ppid)
|
|
391
|
+
scan_sockets.reject { |s| s[:ppid] == current_ppid }
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def cleanup_leftover_processes(current_ppid: Process.ppid)
|
|
395
|
+
terminate_processes(leftover_processes(current_ppid: current_ppid), label: "leftover")
|
|
396
|
+
delete_sockets(leftover_sockets(current_ppid: current_ppid).map { |s| s[:path] }, label: "leftover")
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def shutdown(current_ppid: Process.ppid)
|
|
400
|
+
terminate_processes(own_processes(current_ppid: current_ppid), label: "own")
|
|
401
|
+
ensure
|
|
402
|
+
File.delete(ssr_server_ppid_file) if File.exist?(ssr_server_ppid_file)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def start_server_log(start_time, msg)
|
|
406
|
+
line = start_time ? "[SOR] #{msg} (#{((Time.now - start_time) * 1000).round(1)}ms)" : "[SOR] #{msg}"
|
|
407
|
+
puts line
|
|
408
|
+
$stdout.flush
|
|
409
|
+
SvelteOnRails::Lib::Utils.secure_debug_log(line)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
private
|
|
413
|
+
|
|
414
|
+
# Cluster mode: a primary + N workers share one socket path.
|
|
415
|
+
# SIGINT the primary first (lowest pid per socket group); survivors get SIGINT too.
|
|
416
|
+
def terminate_processes(processes, label:)
|
|
417
|
+
return if processes.empty?
|
|
418
|
+
|
|
419
|
+
processes.group_by(&:socket_path).values.flat_map { |g| g.sort_by(&:pid) }.each do |p|
|
|
420
|
+
Process.kill("SIGINT", p.pid) rescue Errno::ESRCH || Errno::EPERM
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
start_server_log(nil, "Killed #{processes.length} #{label} process#{'es' if processes.length != 1}")
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def delete_sockets(socket_paths, label:)
|
|
427
|
+
return if socket_paths.empty?
|
|
428
|
+
|
|
429
|
+
socket_paths.uniq.each do |path|
|
|
430
|
+
File.delete(path) if File.exist?(path)
|
|
431
|
+
rescue Errno::ENOENT
|
|
432
|
+
# raced with primary's own cleanup — fine
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
start_server_log(nil, "Deleted #{socket_paths.size} #{label} socket#{'s' if socket_paths.size != 1}")
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# All running SSR processes matching our namespace (any ppid).
|
|
439
|
+
def scan_processes
|
|
440
|
+
stdout, = Open3.capture3("ps aux | grep svelte | grep -v grep")
|
|
441
|
+
regex = %r{(/\S*#{Regexp.escape(@socket_namespace)}-(\d+)\.sock)\b}
|
|
442
|
+
|
|
443
|
+
stdout.each_line.filter_map do |line|
|
|
444
|
+
m = line.match(regex) or next
|
|
445
|
+
pid = Integer(line.split(/\s+/, 12)[1], exception: false) or next
|
|
446
|
+
SsrProcess.new(pid: pid, ppid: m[2].to_i, socket_path: m[1])
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# All socket files on disk matching our namespace (any ppid).
|
|
451
|
+
# Looks in both the app's tmp/sockets dir and the macOS/BSD fallback dir.
|
|
452
|
+
def scan_sockets
|
|
453
|
+
dirs = [Rails.root.join("tmp/sockets").to_s, "/tmp/svelte"]
|
|
454
|
+
regex = /#{Regexp.escape(@socket_namespace)}-(\d+)\.sock\z/
|
|
455
|
+
|
|
456
|
+
dirs.flat_map { |d| Dir.glob(File.join(d, "#{@socket_namespace}-*.sock")) }
|
|
457
|
+
.filter_map { |path| (m = path.match(regex)) && { path: path, ppid: m[1].to_i } }
|
|
458
|
+
end
|
|
459
|
+
|
|
420
460
|
end
|
|
421
461
|
|
|
422
462
|
class SsrError < StandardError; end
|
|
@@ -48,8 +48,10 @@ module SvelteOnRails
|
|
|
48
48
|
concat(rnd[:head].html_safe)
|
|
49
49
|
concat(rnd[:html].html_safe)
|
|
50
50
|
support.log_completed("returned from cache, with uncached props#{', (Fallback!)' if rnd[:fallback]}")
|
|
51
|
-
|
|
51
|
+
elsif config.configs[:ssr_error_tag]
|
|
52
52
|
sor_error_tag(support, wrapper_html, component_props, 'server-side-svelte-render-error', 'SVELTE RENDER FAILED')
|
|
53
|
+
else
|
|
54
|
+
render_empty_tag(support, wrapper_html, component_props)
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
end
|
|
@@ -74,7 +76,11 @@ module SvelteOnRails
|
|
|
74
76
|
r
|
|
75
77
|
else
|
|
76
78
|
log = "ERROR"
|
|
77
|
-
|
|
79
|
+
if config.configs[:ssr_error_tag]
|
|
80
|
+
sor_error_tag(support, wrapper_html, component_props, 'server-side-svelte-render-error', 'SVELTE RENDER FAILED')
|
|
81
|
+
else
|
|
82
|
+
render_empty_tag(support, wrapper_html, component_props)
|
|
83
|
+
end
|
|
78
84
|
end
|
|
79
85
|
end
|
|
80
86
|
support.log_completed(log)
|
|
@@ -92,8 +98,10 @@ module SvelteOnRails
|
|
|
92
98
|
concat(rnd[:head].html_safe)
|
|
93
99
|
concat(rnd[:html].html_safe)
|
|
94
100
|
end
|
|
95
|
-
|
|
101
|
+
elsif config.configs[:ssr_error_tag]
|
|
96
102
|
sor_error_tag(support, wrapper_html, component_props, 'server-side-svelte-render-error', 'SVELTE RENDER FAILED')
|
|
103
|
+
else
|
|
104
|
+
render_empty_tag(support, wrapper_html, component_props)
|
|
97
105
|
end
|
|
98
106
|
|
|
99
107
|
support.log_completed("Rendered#{rnd[:fallback] ? ' (Fallback!)' : ''} #{'as empty element that will be mounted on the client side' unless support.ssr?}")
|
|
@@ -7,18 +7,31 @@ namespace :svelte_on_rails do
|
|
|
7
7
|
desc "check ssr requirements and status"
|
|
8
8
|
task :check do
|
|
9
9
|
|
|
10
|
+
line_width = 100
|
|
10
11
|
configs = SvelteOnRails::Configuration.instance
|
|
11
12
|
ppid_file = Rails.root.join("tmp", "pids", 'svelte_on_rails.ppid')
|
|
12
13
|
ppid = (ppid_file.exist? ? File.read(ppid_file).chomp : nil)
|
|
13
14
|
server_helpers = SvelteOnRails::SsrServerHelpers.new
|
|
14
15
|
socket_path = server_helpers.socket_path(ppid: ppid)
|
|
16
|
+
curl_installed = system('which curl > /dev/null 2>&1')
|
|
17
|
+
if curl_installed
|
|
18
|
+
curl_cmd = "curl --unix-socket #{socket_path} http://localhost"
|
|
19
|
+
stdout, stderr, status = Open3.capture3(curl_cmd + '/health')
|
|
20
|
+
srv_details = (JSON.parse(stdout) rescue {})
|
|
21
|
+
end
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
env_check = rails_env_check(socket_path)
|
|
17
24
|
|
|
18
|
-
puts
|
|
19
|
-
puts
|
|
20
|
-
puts "Rails
|
|
21
|
-
puts
|
|
25
|
+
puts
|
|
26
|
+
puts '=' * line_width
|
|
27
|
+
puts "Svelte on Rails SSR Server Status:"
|
|
28
|
+
puts " Rails env: #{Rails.env.upcase}"
|
|
29
|
+
if srv_details['uptime']
|
|
30
|
+
puts " Uptime: #{srv_details['uptime'].round(0)}sec"
|
|
31
|
+
else
|
|
32
|
+
puts " Status: not running"
|
|
33
|
+
end
|
|
34
|
+
puts '=' * line_width
|
|
22
35
|
puts
|
|
23
36
|
puts 'Node'
|
|
24
37
|
puts label('Node bin') + configs.node_bin_path.to_s
|
|
@@ -29,41 +42,96 @@ namespace :svelte_on_rails do
|
|
|
29
42
|
puts 'Rails instance'
|
|
30
43
|
if !server_helpers.ssr_server_ppid_file.exist?
|
|
31
44
|
puts label('Process.ppid') + "#{server_helpers.ssr_server_ppid_file} not found, rails seems not to run or the SsrServer is not initialized yet."
|
|
32
|
-
|
|
45
|
+
elsif env_check == :clearly_correct
|
|
33
46
|
puts label('Process.ppid') + ppid.to_s
|
|
34
47
|
puts label('Socket') + socket_path.to_s
|
|
35
|
-
|
|
36
|
-
puts
|
|
48
|
+
else
|
|
49
|
+
puts 'Rails env could not be determinated, compared to the socket path, so, no socket path could be determined.'
|
|
37
50
|
end
|
|
38
51
|
puts
|
|
39
|
-
puts '=' *
|
|
52
|
+
puts '=' * line_width
|
|
53
|
+
puts
|
|
40
54
|
puts 'MORE DETAILS:'
|
|
41
55
|
puts
|
|
42
56
|
puts 'How was the node bin path determined:'
|
|
43
57
|
configs.instance_variable_get(:@node_bin_path_log).each do |line|
|
|
44
58
|
puts " • #{line}"
|
|
59
|
+
puts " • kill #{line}"
|
|
45
60
|
end
|
|
46
61
|
puts
|
|
62
|
+
puts 'Commands:'
|
|
63
|
+
puts " • node path/to/svelte-ssr-server.js path/to/socket.sock:"
|
|
64
|
+
puts " • #{server_helpers.start_node_server_command(nil)}"
|
|
65
|
+
if srv_details['pid']
|
|
66
|
+
puts " • #{curl_cmd}"
|
|
67
|
+
puts " • kill #{srv_details['pid']}" if srv_details['pid']
|
|
68
|
+
end
|
|
69
|
+
puts
|
|
70
|
+
if env_check == :clearly_correct
|
|
71
|
+
puts '=' * line_width
|
|
72
|
+
puts
|
|
73
|
+
puts 'NODE INSTANCE INFO:'
|
|
74
|
+
puts ' (output of the above mentioned curl command)'
|
|
75
|
+
puts
|
|
76
|
+
puts '-' * line_width
|
|
77
|
+
if curl_installed
|
|
78
|
+
stdout, stderr, status = Open3.capture3(curl_cmd)
|
|
79
|
+
if status.success?
|
|
80
|
+
puts stdout
|
|
81
|
+
else
|
|
82
|
+
puts "curl exited with status #{status.exitstatus}"
|
|
83
|
+
puts stderr unless stderr.to_s.strip.empty?
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
puts "curl is not installed on this machine, skipping execution."
|
|
87
|
+
end
|
|
88
|
+
puts
|
|
89
|
+
puts '=' * line_width
|
|
90
|
+
puts
|
|
91
|
+
end
|
|
47
92
|
|
|
48
93
|
end
|
|
49
94
|
|
|
50
|
-
def
|
|
95
|
+
def rails_env_check(socket_path)
|
|
96
|
+
|
|
97
|
+
line_width = 100
|
|
98
|
+
|
|
51
99
|
envs = guessed_rails_envs(socket_path.dirname)
|
|
100
|
+
|
|
52
101
|
if envs == [Rails.env]
|
|
53
|
-
|
|
102
|
+
|
|
103
|
+
:clearly_correct
|
|
104
|
+
|
|
105
|
+
elsif envs.empty?
|
|
106
|
+
|
|
107
|
+
puts
|
|
108
|
+
puts '*' * line_width
|
|
109
|
+
puts "WARNING: This Rails app seems not to run."
|
|
110
|
+
puts '*' * line_width
|
|
111
|
+
puts
|
|
112
|
+
:possible_correct
|
|
113
|
+
|
|
54
114
|
elsif envs.include?(Rails.env)
|
|
55
|
-
|
|
115
|
+
|
|
116
|
+
puts
|
|
117
|
+
puts '*' * line_width
|
|
56
118
|
puts "INFO: Based on the socket paths #{socket_path.dirname},"
|
|
57
119
|
puts "this Rails app may run in #{envs.join(' or ')}."
|
|
58
120
|
puts "You are running as #{Rails.env} (Rails.env) which may be right."
|
|
59
|
-
puts '*' *
|
|
121
|
+
puts '*' * line_width
|
|
60
122
|
puts
|
|
123
|
+
:possible_correct
|
|
124
|
+
|
|
61
125
|
else
|
|
62
|
-
|
|
126
|
+
|
|
127
|
+
puts
|
|
128
|
+
puts '*' * line_width
|
|
63
129
|
puts "WARNING: You are running as #{Rails.env} (Rails.env)"
|
|
64
130
|
puts "But the rails app seems to run in #{envs.join(' or ')}"
|
|
65
|
-
puts '*' *
|
|
131
|
+
puts '*' * line_width
|
|
66
132
|
puts
|
|
133
|
+
:possible_wrong
|
|
134
|
+
|
|
67
135
|
end
|
|
68
136
|
end
|
|
69
137
|
|
|
@@ -18,28 +18,42 @@ aliases:
|
|
|
18
18
|
#'@frontend': app/frontend/javascript
|
|
19
19
|
#'@views': app/views
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
#
|
|
21
|
+
skip_ssr_header: 'X-Turbo-Request-ID'
|
|
22
|
+
# when ssr: :auto =>
|
|
23
|
+
# Skip SSR when this request header is present and non-empty (Turbo / prefetch / XHR requests)
|
|
24
|
+
|
|
25
|
+
ssr: :auto
|
|
26
|
+
# options: true, false, :auto
|
|
27
|
+
# :auto: render server side if request is initial request (works only with turbo, because it checks for the X-Turbo-Request-ID header)
|
|
28
|
+
# when not server-side rendered the components must be built as custom elements, see node-package @csedl/svelte-on-rails
|
|
29
|
+
# false disables SSR for all and overrides the ssr argument of the view helper.
|
|
30
|
+
|
|
31
|
+
ssr_server: true
|
|
32
|
+
# Node server on the backend for speedup server side rendering
|
|
33
|
+
# default: true for all environments, except test
|
|
34
|
+
# docs: https://svelte-on-rails.dev/configuration/svelte_ssr_server.html
|
|
35
|
+
|
|
36
|
+
ssr_server_workers: 1
|
|
37
|
+
# number of Node cluster workers; 1 = no cluster
|
|
38
|
+
|
|
39
|
+
ssr_fallback_renderer: false
|
|
40
|
+
# Node script that does not need a running node server
|
|
41
|
+
# default: false
|
|
42
|
+
|
|
43
|
+
ssr_error_tag: true
|
|
44
|
+
# Render the error tag when server side rendering fails
|
|
45
|
+
# default: true, except for production
|
|
24
46
|
|
|
25
47
|
# redis_cache_store:
|
|
26
48
|
# url: 'redis://localhost:6379/2'
|
|
27
49
|
# expires_in: 90.minutes (=> default / fallback: 1 hour)
|
|
28
50
|
# namespace: 'my-app-svelte-on-rails'
|
|
29
51
|
|
|
52
|
+
|
|
30
53
|
use_caching: false
|
|
31
54
|
# this is the default for the #svelte view-helper.
|
|
32
55
|
# When set to true, the redis gem is required.
|
|
33
56
|
|
|
34
|
-
ssr: :auto
|
|
35
|
-
# options: true, false, :auto
|
|
36
|
-
# :auto: render server side if request is initial request (works only with turbo, because it checks for the X-Turbo-Request-ID header)
|
|
37
|
-
# when not server-side rendered the components must be built as custom elements, see node-package @csedl/svelte-on-rails
|
|
38
|
-
|
|
39
|
-
skip_ssr_header: 'X-Turbo-Request-ID'
|
|
40
|
-
# when ssr: :auto =>
|
|
41
|
-
# Skip SSR when this request header is present and non-empty (Turbo / prefetch / XHR requests)
|
|
42
|
-
|
|
43
57
|
debug: true
|
|
44
58
|
# provides deeper logging with timestamps for performance improvements
|
|
45
59
|
|
|
@@ -81,4 +95,11 @@ test:
|
|
|
81
95
|
watch_changes: true
|
|
82
96
|
cache_expiration_hours: 15
|
|
83
97
|
|
|
84
|
-
|
|
98
|
+
staging:
|
|
99
|
+
ssr_error_tag: true
|
|
100
|
+
ssr_fallback_renderer: false
|
|
101
|
+
|
|
102
|
+
production:
|
|
103
|
+
ssr_error_tag: false
|
|
104
|
+
ssr_fallback_renderer: true
|
|
105
|
+
ssr_server_workers: 3
|