svelte-on-rails 22.3.0 → 22.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a95b04eb2bc4fe9f3b9cf489af8550377bb35bd8297b7c8c9849fae0b4985e89
4
- data.tar.gz: d648e0c9240757feb39c42904165bbd985ee24c19de78e2b1d5068c60c2b9539
3
+ metadata.gz: e4e1180e9510fa0b8592b0b17be5ae1f75348124861184022648f2ec6b96865d
4
+ data.tar.gz: 737ff843db6d9fe9ae04d34fd24b214089368d2b34357ac0e64a14ff90c13d52
5
5
  SHA512:
6
- metadata.gz: f32d6e15c061f5c95abaffdf1e74ac295bd89058a9eb226e8ead4ac34321890509f0dddfe2af0e3e8c5a0e0d0696ac4236eb224c41928ca1ed2cd2f06ac51aca
7
- data.tar.gz: 588239e64c3a43f660c3099710d0b0a41f59fc0636be72d7458756a8b55fd6e773fae911937de0099d5b7d4d30466d2856a62b0405f5e2a6ca2e19fe23e208b7
6
+ metadata.gz: 769b3c284cdddd35fb86895a29a6ae3a7bce52f501cb689856c1d166892fd360a264eafe274f2cc41944ec57d1a044d25c2c3a1934149ce9e0d1afb972f4501a
7
+ data.tar.gz: 2d067a4983fb063fc1e38a1a68e235050d405dad829bea0727ceed9a6d0a35752909d2ae2aba99177e73af21ef01e0cd848d51c30612838ea48fda5163c95c90
@@ -18,6 +18,7 @@ require "svelte_on_rails/lib/to_svelte_values"
18
18
  require "svelte_on_rails/lib/to_svelte_support"
19
19
  require "svelte_on_rails/lib/to_svelte_translations"
20
20
  require "svelte_on_rails/lib/cache_warmer"
21
+ require "svelte_on_rails/lib/boot_context"
21
22
 
22
23
  # installer
23
24
  require 'svelte_on_rails/installer/utils'
@@ -44,14 +44,9 @@ module SvelteOnRails
44
44
 
45
45
  if @configs[:redis_cache_store]
46
46
  store = @configs[:redis_cache_store]
47
- if store['expires_in'].is_a?(String)
48
- @configs[:redis_cache_store]['expires_in'] = parse_duration(
49
- store['expires_in']
50
- )
51
- end
52
47
  @redis_namespace = store[:namespace]
53
48
  end
54
- @redis_namespace ||= "svelte-on-rails:#{Rails.env rescue 'no-env'}"
49
+ @redis_namespace ||= "#{app_name}:svelte-on-rails:#{Rails.env rescue 'no-env'}"
55
50
  @configs[:skip_ssr_header] ||= 'X-Turbo-Request-ID'
56
51
  if defined? Redis
57
52
  @redis_instance = Redis.new(url: redis_cache_store[:url])
@@ -60,6 +55,7 @@ module SvelteOnRails
60
55
  validate_serialized_configs
61
56
  custom_config_validations
62
57
  validate_npm_packages
58
+ process_configs
63
59
  end
64
60
 
65
61
  def set_config_defaults
@@ -175,11 +171,18 @@ module SvelteOnRails
175
171
  nil
176
172
  end
177
173
 
174
+ def process_configs
175
+ ex = @configs[:cache_expires_in]
176
+ if ex
177
+ @configs[:cache_expires_in] = ex.split('.').first.to_i.send(ex.split('.').last)
178
+ end
179
+ end
180
+
178
181
  private
179
182
 
180
183
  def redis_cache_store_configs
181
184
  if defined?(Rails.application) && Rails.application.config.cache_store.is_a?(Array) && Rails.application.config.cache_store.first == :redis_cache_store
182
- { redis_cache_store: Rails.application.config.cache_store.second.stringify_keys }
185
+ { redis_cache_store: Rails.application.config.cache_store.second.deep_symbolize_keys }
183
186
  else
184
187
  {}
185
188
  end
@@ -228,7 +231,7 @@ module SvelteOnRails
228
231
  ssr: { options_required: [:auto, true, false] },
229
232
  watch_changes: { options_optional: [true, false] },
230
233
 
231
- redis_cache_store: { hash_of_strings_optional: [:url, :namespace, :expires_in] },
234
+ redis_cache_store: { hash_of_strings_optional: [:url, :namespace] },
232
235
  turbo_stream: { hash_of_strings_optional: [:target_html_id, :channel] },
233
236
  action_cable: { hash_of_strings_optional: [:channel] },
234
237
 
@@ -245,6 +248,11 @@ module SvelteOnRails
245
248
  elsif k == :default_components_root
246
249
  available_keys = ['components_subdir'] + @configs[:extra_component_paths].to_a
247
250
  raise "[SOR] Invalid configuration: #{k} must be one of: [«#{available_keys.join('», «')}»]" unless available_keys.include?(v)
251
+ elsif k == :cache_expires_in
252
+ reg = /^[0-9]+\.(minutes|hours|days)$/
253
+ unless v.match(reg)
254
+ raise "#{k} must match #{reg}"
255
+ end
248
256
  elsif validation[:required]
249
257
  if validation[:required] == :string
250
258
  raise "[SOR] Missing configuration: #{k} must contain a string" if !v.present? || !v.is_a?(String)
@@ -262,6 +270,8 @@ module SvelteOnRails
262
270
  raise "[SOR] Invalid configuration: #{k} must be one of #{validation[:options_optional]}" unless validation[:options_optional].include?(v)
263
271
  elsif validation[:hash_of_strings_optional]
264
272
 
273
+ raise "[SOR] #{k} is empty. Either remove this key or provide a hash" unless v.present?
274
+
265
275
  _available_keys = validation[:hash_of_strings_optional]
266
276
  v.each do |_k, _v|
267
277
  raise "[SOR] Invalid configuration: #{k}/#{_k} («#{_v}» / #{_v.class}) must be a string" unless _v.is_a?(String)
@@ -367,5 +377,12 @@ module SvelteOnRails
367
377
  end
368
378
  end
369
379
 
380
+ def app_name
381
+ # Same logic Rails uses internally for e.g. cache_store default namespace
382
+ Rails.application.class&.module_parent_name.to_s[0..30].underscore.presence || 'app'
383
+ rescue StandardError
384
+ 'app'
385
+ end
386
+
370
387
  end
371
388
  end
@@ -0,0 +1,98 @@
1
+ module SvelteOnRails
2
+ module BootContext
3
+ module_function
4
+
5
+ # We deliberately use a BLACKLIST + program-name check, not a whitelist of
6
+ # loaded constants. Reason: `puma` is typically in the app's Gemfile default
7
+ # group, so `defined?(::Puma::Launcher)` is truthy even inside `rails console`
8
+ # or `rails assets:precompile`.
9
+ def serving_http?
10
+ # Explicit overrides for tests / edge cases:
11
+ return true if ENV["SVELTE_ON_RAILS_FORCE_SSR"] == "1"
12
+ return false if ENV["SVELTE_ON_RAILS_DISABLE_SSR"] == "1"
13
+
14
+ return false if rake_task?
15
+ return false if rails_console?
16
+ return false if rails_runner?
17
+ return false if rails_dbconsole?
18
+ return false if rails_generator?
19
+ return false if test_runner?
20
+
21
+ # Positive signal: `rails server` sets this constant during command dispatch.
22
+ return true if defined?(Rails::Server)
23
+
24
+ # Positive signal: launched directly via a server binary (bundle exec puma,
25
+ # rackup, unicorn, falcon, passenger). $PROGRAM_NAME is set by the launcher
26
+ # before it requires the app, so this is reliable.
27
+ return true if server_program_name?
28
+
29
+ # Default: assume this is NOT an HTTP-serving context. Better to be silent
30
+ # than to spawn an unwanted Node process; users can force with the ENV var.
31
+ false
32
+ end
33
+
34
+ def description
35
+ return "rake tasks: #{Rake.application.top_level_tasks.join(', ')}" if rake_task?
36
+ return "rails console" if rails_console?
37
+ return "rails runner" if rails_runner?
38
+ return "rails dbconsole" if rails_dbconsole?
39
+ return "rails generator" if rails_generator?
40
+ return "test runner (#{test_runner_name})" if test_runner?
41
+ return "rails server" if defined?(Rails::Server)
42
+ "program=#{File.basename($PROGRAM_NAME)}"
43
+ end
44
+
45
+ # --- detectors -----------------------------------------------------------
46
+
47
+ def rake_task?
48
+ defined?(Rake) && Rake.respond_to?(:application) && Rake.application.top_level_tasks.any?
49
+ end
50
+
51
+ def rails_console?
52
+ defined?(Rails::Console) ||
53
+ program_basename == "irb" ||
54
+ (defined?(IRB) && $PROGRAM_NAME.to_s.include?("irb"))
55
+ end
56
+
57
+ def rails_runner?
58
+ defined?(Rails::Command::RunnerCommand) &&
59
+ Rails::Command.const_defined?(:RunnerCommand) &&
60
+ current_rails_command?("runner")
61
+ end
62
+
63
+ def rails_dbconsole?
64
+ defined?(Rails::DBConsole) || current_rails_command?("dbconsole")
65
+ end
66
+
67
+ def rails_generator?
68
+ defined?(Rails::Generators) && current_rails_command?("generate", "destroy", "new")
69
+ end
70
+
71
+ def test_runner?
72
+ !test_runner_name.nil?
73
+ end
74
+
75
+ def test_runner_name
76
+ return "rspec" if $PROGRAM_NAME.to_s.include?("rspec")
77
+ return "minitest" if defined?(Minitest) && Minitest.respond_to?(:run) && !defined?(Rails::Server)
78
+ return "cucumber" if $PROGRAM_NAME.to_s.include?("cucumber")
79
+ nil
80
+ end
81
+
82
+ def server_program_name?
83
+ %w[puma rackup unicorn unicorn_rails falcon passenger].include?(program_basename)
84
+ end
85
+
86
+ def program_basename
87
+ File.basename($PROGRAM_NAME.to_s)
88
+ end
89
+
90
+ # `rails <command>` sets ARGV[0] to the command name before Rails::Command
91
+ # dispatches. This is stable across Rails 6/7/8.
92
+ def current_rails_command?(*names)
93
+ argv0 = (defined?(ARGV) && ARGV.first) || nil
94
+ return false unless argv0
95
+ names.include?(argv0.to_s)
96
+ end
97
+ end
98
+ end
@@ -5,7 +5,14 @@ module SvelteOnRails
5
5
  class ViewHelperSupport
6
6
 
7
7
  UNCACHED_PROPS_PLACEHOLDER = "**Uncached Props are available only after hydration**"
8
- RENDER_OPTIONS = { ssr: [true, false, :auto], hydrate: :bool, debug: :bool, cache_key: :string_array, expires_in: :integer, cached: :bool }
8
+ RENDER_OPTIONS = {
9
+ ssr: [:oneof, [true, false, :auto]],
10
+ hydrate: [:oneof, [true, false]],
11
+ debug: [:oneof, [true, false]],
12
+ cache_key: [:typeof, [String, Array]],
13
+ cache_expires_in: [:typeof, [Integer, ActiveSupport::Duration]],
14
+ cached: [:oneof, [true, false]]
15
+ }
9
16
  attr_reader :conf
10
17
 
11
18
  def initialize(
@@ -191,7 +198,7 @@ module SvelteOnRails
191
198
  @conf.debug_log(debug?, component_paths[:name], "rendered and stored to cache (key: «#{cache_key}»)") do
192
199
  r = view_context.instance_eval(&block)
193
200
  unless @skip_caching
194
- @conf.redis_instance.set(cache_key, r)
201
+ @conf.redis_instance.set(cache_key, r, ex: redis_expiration_seconds)
195
202
  end
196
203
  r
197
204
  end
@@ -289,18 +296,17 @@ module SvelteOnRails
289
296
  raise "Unknown options: #{@options.keys - RENDER_OPTIONS.keys}. Valid options are: :#{RENDER_OPTIONS.keys.join(', :')}"
290
297
  end
291
298
  @options.each do |k, v|
292
- _v = RENDER_OPTIONS[k]
293
- if _v == :bool
294
- unless [true, false].include?(v)
295
- raise "[SOR] Invalid option: :#{k} must be true or false (boolean)."
296
- end
297
- elsif _v == :string_array
298
- unless [String, Array].include?(v.class)
299
- raise "[SOR] Invalid option: :#{k} must be a string or Array (list of strings)."
299
+ validation = RENDER_OPTIONS[k]
300
+ _v_type = validation.first
301
+ _v_opts = validation.second
302
+
303
+ if validation.first == :oneof
304
+ unless validation.second.include? v
305
+ raise "[SOR] Invalid option: :#{k} must be one of #{validation.second.join(', ')}"
300
306
  end
301
- elsif _v.is_a?(Array)
302
- unless _v.include?(v)
303
- raise "[SOR] Invalid option: :#{k} must be one of #{_v.join(', ')}"
307
+ elsif validation.first == :typeof
308
+ unless validation.second.include? v.class
309
+ raise "[SOR] Invalid option: :#{k} must be one of #{validation.second.join(', ')}"
304
310
  end
305
311
  else
306
312
  raise "[SOR] Unknown validation for option"
@@ -337,16 +343,17 @@ module SvelteOnRails
337
343
  @cache_key = begin
338
344
  fingerprint = (@conf.watch_changes? ? @conf.fingerprint(component_paths[:path], debug?, component_paths[:name]) : '')
339
345
 
340
- cache_key_raw = @view_dir + @component_name + @options.to_s +
346
+ cache_key_raw = @options.to_s +
341
347
  @cached_props.to_s + fingerprint + @options[:cache_key].to_s +
342
348
  (@cached_props.key?(:_uncached) ? "" : @html_options.to_s)
343
349
 
344
- "#{@conf.redis_namespace}:#{Digest::XXH64.hexdigest(cache_key_raw)}"
350
+ "#{@conf.redis_namespace}:#{@component_name.gsub('/','-')}:#{Digest::XXH64.hexdigest(cache_key_raw)}"
345
351
  end
346
352
  end
347
353
 
348
354
  def redis_expiration_seconds
349
- (conf.redis_cache_store[:expires_in] || @options[:expires_in] || 1.hour).to_i
355
+ ex = @options[:cache_expires_in] || conf.configs[:cache_expires_in] || 3.days
356
+ ex.to_i
350
357
  end
351
358
 
352
359
  end
@@ -13,12 +13,14 @@ module SvelteOnRails
13
13
 
14
14
  # Spawn the Node SSR child process only when we're really about to serve
15
15
  # HTTP. `after_initialize` runs at the end of boot, so Puma/Unicorn/etc.
16
- # are already loaded and the detection in `start_ssr_server?` is reliable.
16
+ # are already loaded and the detection in `serving_http?` is reliable.
17
17
  initializer "svelte-on-rails.initialize_ssr_client" do |app|
18
18
  app.config.after_initialize do
19
19
 
20
- if defined?(Rake) && Rake.respond_to?(:application) && Rake.application.top_level_tasks.any?
21
- SvelteOnRails::Lib::Utils.ssr_server_log("Skipping svelte-on-rails SSR-Server because of top level task: #{Rake.application.top_level_tasks.join(', ')}")
20
+ if !SvelteOnRails::BootContext.serving_http?
21
+ SvelteOnRails::Lib::Utils.ssr_server_log(
22
+ "Skipping svelte-on-rails SSR-Server (#{SvelteOnRails::BootContext.description})"
23
+ )
22
24
  $stdout.flush
23
25
  elsif SvelteOnRails::Configuration.instance.configs[:ssr]
24
26
  SvelteOnRails::SsrClient.instance
@@ -53,6 +55,5 @@ module SvelteOnRails
53
55
  load File.expand_path("../../tasks/svelte_on_rails_tasks.rake", __FILE__)
54
56
  load File.expand_path("../../tasks/cache_tasks.rake", __FILE__)
55
57
  end
56
-
57
58
  end
58
59
  end
@@ -143,15 +143,18 @@ module SvelteOnRails
143
143
  end
144
144
 
145
145
  def calling_view_dir
146
- rails_views_path = Rails.root.join('app', 'views').to_s
147
- raw_path_full = caller_locations.find do |loc|
148
- loc.to_s.include?(rails_views_path)
149
- end.to_s
150
-
151
- if raw_path_full.present?
152
- raw_path = raw_path_full.split(/:[0-9]+:in/).first
153
- File.dirname(raw_path[(rails_views_path.length + 1)..-1])
154
- end.to_s
146
+ rails_views_path = SvelteOnRails::ViewHelpers.rails_views_path
147
+ prefix_len = rails_views_path.length + 1
148
+
149
+ # Walk only a bounded slice of the stack; use loc.path (no string formatting).
150
+ loc = caller_locations(1, 60)&.find { |l| l.path&.start_with?(rails_views_path) }
151
+ return ''.freeze unless loc
152
+
153
+ File.dirname(loc.path[prefix_len..-1])
154
+ end
155
+
156
+ def self.rails_views_path
157
+ @rails_views_path ||= Rails.root.join('app', 'views').to_s
155
158
  end
156
159
 
157
160
  end
@@ -48,7 +48,6 @@ ssr_error_tag: true
48
48
 
49
49
  # redis_cache_store:
50
50
  # url: 'redis://localhost:6379/2'
51
- # expires_in: 90.minutes (=> default / fallback: 1 hour)
52
51
  # namespace: 'my-app-svelte-on-rails'
53
52
 
54
53
 
@@ -56,6 +55,10 @@ use_caching: false
56
55
  # this is the default for the #svelte view-helper.
57
56
  # When set to true, the redis gem is required.
58
57
 
58
+ cache_expires_in: 3.days
59
+ # ttl for cache keys
60
+ # default: 3.days
61
+
59
62
  debug: true
60
63
  # provides deeper logging with timestamps for performance improvements
61
64
 
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: 22.3.0
4
+ version: 22.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Sedlmair
@@ -84,6 +84,7 @@ files:
84
84
  - lib/svelte_on_rails/installer/npm_sandbox.rb
85
85
  - lib/svelte_on_rails/installer/svelte.rb
86
86
  - lib/svelte_on_rails/installer/utils.rb
87
+ - lib/svelte_on_rails/lib/boot_context.rb
87
88
  - lib/svelte_on_rails/lib/cache_warmer.rb
88
89
  - lib/svelte_on_rails/lib/development_utils.rb
89
90
  - lib/svelte_on_rails/lib/fallback_renderer.rb