isomorfeus-preact 10.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +62 -0
  3. data/lib/browser/delegate_native.rb +70 -0
  4. data/lib/browser/element.rb +176 -0
  5. data/lib/browser/element/canvas.rb +17 -0
  6. data/lib/browser/element/media.rb +78 -0
  7. data/lib/browser/event.rb +92 -0
  8. data/lib/browser/event_target.rb +39 -0
  9. data/lib/browser/file_list.rb +125 -0
  10. data/lib/browser/iterable.rb +15 -0
  11. data/lib/isomorfeus-preact.rb +109 -0
  12. data/lib/isomorfeus/preact/config.rb +189 -0
  13. data/lib/isomorfeus/preact/memcached_component_cache.rb +19 -0
  14. data/lib/isomorfeus/preact/redis_component_cache.rb +19 -0
  15. data/lib/isomorfeus/preact/thread_local_component_cache.rb +17 -0
  16. data/lib/isomorfeus/preact_view_helper.rb +188 -0
  17. data/lib/isomorfeus/props/validate_hash_proxy.rb +186 -0
  18. data/lib/isomorfeus/props/validator.rb +159 -0
  19. data/lib/isomorfeus/top_level.rb +101 -0
  20. data/lib/isomorfeus/top_level_ssr.rb +27 -0
  21. data/lib/isomorfeus_preact/lucid_app/api.rb +22 -0
  22. data/lib/isomorfeus_preact/lucid_app/base.rb +7 -0
  23. data/lib/isomorfeus_preact/lucid_app/mixin.rb +16 -0
  24. data/lib/isomorfeus_preact/lucid_app/native_component_constructor.rb +91 -0
  25. data/lib/isomorfeus_preact/lucid_component/api.rb +68 -0
  26. data/lib/isomorfeus_preact/lucid_component/app_store_proxy.rb +37 -0
  27. data/lib/isomorfeus_preact/lucid_component/base.rb +7 -0
  28. data/lib/isomorfeus_preact/lucid_component/class_store_proxy.rb +44 -0
  29. data/lib/isomorfeus_preact/lucid_component/initializer.rb +14 -0
  30. data/lib/isomorfeus_preact/lucid_component/instance_store_proxy.rb +44 -0
  31. data/lib/isomorfeus_preact/lucid_component/mixin.rb +15 -0
  32. data/lib/isomorfeus_preact/lucid_component/native_component_constructor.rb +84 -0
  33. data/lib/isomorfeus_preact/lucid_component/styles_api.rb +31 -0
  34. data/lib/isomorfeus_preact/lucid_component/styles_wrapper.rb +40 -0
  35. data/lib/isomorfeus_preact/lucid_func/base.rb +7 -0
  36. data/lib/isomorfeus_preact/lucid_func/initializer.rb +11 -0
  37. data/lib/isomorfeus_preact/lucid_func/mixin.rb +12 -0
  38. data/lib/isomorfeus_preact/lucid_func/native_component_constructor.rb +55 -0
  39. data/lib/isomorfeus_preact/preact/function_component/api.rb +123 -0
  40. data/lib/isomorfeus_preact/preact/function_component/base.rb +9 -0
  41. data/lib/isomorfeus_preact/preact/function_component/initializer.rb +10 -0
  42. data/lib/isomorfeus_preact/preact/function_component/mixin.rb +12 -0
  43. data/lib/isomorfeus_preact/preact/function_component/native_component_constructor.rb +48 -0
  44. data/lib/lucid_app/context.rb +24 -0
  45. data/lib/lucid_prop_declaration/mixin.rb +126 -0
  46. data/lib/preact.rb +309 -0
  47. data/lib/preact/component/api.rb +124 -0
  48. data/lib/preact/component/base.rb +9 -0
  49. data/lib/preact/component/callbacks.rb +102 -0
  50. data/lib/preact/component/elements.rb +64 -0
  51. data/lib/preact/component/initializer.rb +11 -0
  52. data/lib/preact/component/mixin.rb +15 -0
  53. data/lib/preact/component/native_component_constructor.rb +65 -0
  54. data/lib/preact/component/params.rb +18 -0
  55. data/lib/preact/component/props.rb +55 -0
  56. data/lib/preact/component/resolution.rb +97 -0
  57. data/lib/preact/component/state.rb +58 -0
  58. data/lib/preact/context_wrapper.rb +46 -0
  59. data/lib/preact/native_constant_wrapper.rb +29 -0
  60. data/lib/preact/options.rb +98 -0
  61. data/lib/preact/ref.rb +17 -0
  62. data/lib/preact/version.rb +3 -0
  63. metadata +301 -0
@@ -0,0 +1,39 @@
1
+ module Browser
2
+ module EventTarget
3
+ # Add the block as a handler for the specified event name. Will use either
4
+ # `addEventListener` or `addListener` if they exist.
5
+ #
6
+ # @param event_name [String] the name of the event
7
+ # @return [Proc] the block to pass to `off` to remove this handler
8
+ # @yieldparam event [Browser::Event] the event object
9
+ def on event_name, &block
10
+ wrapper = proc { |event| block.call Event.new(event) }
11
+
12
+ if `#@native.addEventListener !== undefined`
13
+ `#@native.addEventListener(event_name, wrapper)`
14
+ elsif `#@native.addListener !== undefined`
15
+ `#@native.addListener(event_name, wrapper)`
16
+ else
17
+ warn "[Browser] Not entirely sure how to add an event listener to #{self}"
18
+ end
19
+
20
+ wrapper
21
+ end
22
+
23
+ # Remove an event handler
24
+ #
25
+ # @param event_name [String] the name of the event
26
+ # @block the handler to remove, as returned from `on`
27
+ def off event_name, &block
28
+ if `#@native.removeEventListener !== undefined`
29
+ `#@native.removeEventListener(event_name, block)`
30
+ elsif `#@native.removeListener !== undefined`
31
+ `#@native.removeListener(event_name, block)`
32
+ else
33
+ warn "[Browser] Not entirely sure how to remove an event listener from #{self}"
34
+ end
35
+
36
+ nil
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,125 @@
1
+ module Browser
2
+ class FileList
3
+ include Enumerable
4
+
5
+ # @param native [JS] the native FileList object to wrap
6
+ def initialize native
7
+ @native = `#{native} || []`
8
+ @files = length.times.each_with_object([]) { |index, array|
9
+ array[index] = File.new(`#@native[index]`)
10
+ }
11
+ end
12
+
13
+ # @param index [Integer] the index of the file in the list
14
+ # @return [Browser::FileList::File] the file at the specified index
15
+ def [] index
16
+ @files[index]
17
+ end
18
+
19
+ # @return [Integer] the number of files in this list
20
+ def length
21
+ `#@native.length`
22
+ end
23
+ alias size length
24
+
25
+ # Call the given block for each file in the list
26
+ #
27
+ # @yieldparam file [Browser::FileList::File]
28
+ def each &block
29
+ @files.each do |file|
30
+ block.call file
31
+ end
32
+ end
33
+
34
+ # Convert this FileList into an array
35
+ def to_a
36
+ @files.dup # Don't return a value that can mutate our internal state
37
+ end
38
+ alias to_ary to_a
39
+
40
+ # @return [String] a string representation of this FileList
41
+ def to_s
42
+ @files.to_s
43
+ end
44
+
45
+ # An individual item in a FileList
46
+ class File
47
+ attr_reader :data
48
+
49
+ # @param native [JS] the native File object to wrap
50
+ def initialize native
51
+ @native = native
52
+ @data = nil
53
+ end
54
+
55
+ # @return [String] the filename
56
+ def name
57
+ `#@native.name`
58
+ end
59
+
60
+ # @return [Integer] the size of this file on disk
61
+ def size
62
+ `#@native.size`
63
+ end
64
+
65
+ # @return [String] the MIME type of the file, detected by the browser
66
+ def type
67
+ `#@native.type`
68
+ end
69
+
70
+ # @return [Time] the timestamp of the file
71
+ def last_modified
72
+ `#@native.lastModifiedDate`
73
+ end
74
+
75
+ # Read the file from disk into memory
76
+ #
77
+ # @return [Promise] a promise that resolves when finished loading and
78
+ # rejects if an error occurs while loading.
79
+ def read
80
+ promise = Promise.new
81
+ reader = FileReader.new
82
+ reader.on :load do
83
+ result = reader.result
84
+
85
+ @data = result
86
+ promise.resolve result
87
+ end
88
+
89
+ reader.on :error do
90
+ promise.reject reader.result
91
+ end
92
+
93
+ reader.read_as_binary_string self
94
+
95
+ promise
96
+ end
97
+
98
+ # Convert to the native object
99
+ #
100
+ # @return [JS.HTMLElement] the underlying native element
101
+ def to_n
102
+ @native
103
+ end
104
+
105
+ # The object that reads the file from disk.
106
+ #
107
+ # @api private
108
+ class FileReader
109
+ include EventTarget
110
+
111
+ def initialize
112
+ @native = `new FileReader()`
113
+ end
114
+
115
+ def result
116
+ `#@native.result`
117
+ end
118
+
119
+ def read_as_binary_string file
120
+ `#@native.readAsBinaryString(#{file.to_n})`
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,15 @@
1
+ module Browser
2
+ class Iterable
3
+ include Enumerable
4
+
5
+ def initialize js_iterable
6
+ @js_iterable = js_iterable
7
+ end
8
+
9
+ def each
10
+ `#@js_iterable.length`.times do |i|
11
+ yield `#@js_iterable[i]`
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,109 @@
1
+ if RUBY_ENGINE == 'opal'
2
+ require 'isomorfeus-redux'
3
+ require 'active_support/core_ext/string'
4
+ require 'zeitwerk'
5
+
6
+ if on_browser?
7
+ require 'browser/event'
8
+ require 'browser/event_target'
9
+ require 'browser/delegate_native'
10
+ require 'browser/element'
11
+ end
12
+
13
+ require 'isomorfeus/preact/config'
14
+
15
+ # allow mounting of components
16
+ if on_browser?
17
+ require 'isomorfeus/top_level'
18
+ else
19
+ require 'isomorfeus/top_level_ssr'
20
+ end
21
+
22
+ # preact
23
+ require 'preact/version'
24
+ require 'preact'
25
+ require 'preact/ref'
26
+
27
+ # props
28
+ require 'isomorfeus/props/validate_hash_proxy'
29
+ require 'isomorfeus/props/validator'
30
+ require 'lucid_prop_declaration/mixin'
31
+ require 'preact/component/props'
32
+
33
+ # HTML Elements and Fragment support
34
+ require 'preact/component/elements'
35
+
36
+ # Preact Wrappers
37
+ require 'preact/context_wrapper'
38
+ require 'preact/native_constant_wrapper'
39
+
40
+ # Preact::Component
41
+ require 'preact/component/api'
42
+ require 'preact/component/callbacks'
43
+ require 'preact/component/initializer'
44
+ require 'preact/component/native_component_constructor'
45
+ require 'preact/component/state'
46
+ require 'preact/component/params'
47
+ require 'preact/component/resolution'
48
+ require 'preact/component/mixin'
49
+ require 'preact/component/base'
50
+
51
+ # init LucidApplicationContext (Store Provider and Consumer)
52
+ require 'lucid_app/context'
53
+ LucidApp::Context.create_application_context
54
+
55
+ class Object
56
+ include Preact::Component::Resolution
57
+ end
58
+
59
+ Isomorfeus.zeitwerk = Zeitwerk::Loader.new
60
+
61
+ Isomorfeus.zeitwerk.push_dir('isomorfeus_preact')
62
+ require_tree 'isomorfeus_preact', :autoload
63
+
64
+ Isomorfeus.zeitwerk.push_dir('components')
65
+ else
66
+ require 'uri'
67
+ require 'oj'
68
+ require 'opal'
69
+ require 'opal-activesupport'
70
+ require 'opal-zeitwerk'
71
+ require 'opal-webpack-loader'
72
+ require 'isomorfeus-redux'
73
+ require 'isomorfeus-speednode'
74
+ require 'preact/version'
75
+ require 'isomorfeus/preact/config'
76
+
77
+ # props
78
+ require 'isomorfeus/props/validate_hash_proxy'
79
+ require 'isomorfeus/props/validator'
80
+ require 'lucid_prop_declaration/mixin'
81
+
82
+ Isomorfeus.env = ENV['RACK_ENV']
83
+
84
+ if Isomorfeus.development?
85
+ require 'net/http'
86
+ Isomorfeus.ssr_hot_asset_url = 'http://localhost:3036/assets/'
87
+ end
88
+
89
+ Isomorfeus.server_side_rendering = true
90
+
91
+ # caches
92
+ require 'isomorfeus/preact/thread_local_component_cache'
93
+ require 'isomorfeus/preact/memcached_component_cache'
94
+ require 'isomorfeus/preact/redis_component_cache'
95
+ require 'isomorfeus/preact_view_helper'
96
+
97
+ Isomorfeus.component_cache_init do
98
+ Isomorfeus::ThreadLocalComponentCache.new
99
+ end
100
+
101
+ Opal.append_path(__dir__.untaint)
102
+
103
+ require 'concurrent'
104
+ require 'zeitwerk'
105
+
106
+ Isomorfeus.zeitwerk = Zeitwerk::Loader.new
107
+ Isomorfeus.zeitwerk_lock = Concurrent::ReentrantReadWriteLock.new if Isomorfeus.development?
108
+ # nothing to push_dir to zeitwerk here, as components are available only within browser/SSR
109
+ end
@@ -0,0 +1,189 @@
1
+ module Isomorfeus
2
+ if RUBY_ENGINE == 'opal'
3
+ class << self
4
+ attr_accessor :current_user_sid
5
+ attr_accessor :initial_state_fetched
6
+ attr_accessor :top_component
7
+ attr_accessor :ssr_response_status
8
+ attr_reader :initialized
9
+ attr_reader :env
10
+ attr_accessor :zeitwerk
11
+
12
+ def init
13
+ return if initialized
14
+ @initialized = true
15
+ Isomorfeus.init_store
16
+ execute_init_classes
17
+ end
18
+
19
+ def force_init!
20
+ unless Isomorfeus.initial_state_fetched
21
+ Isomorfeus.initial_state_fetched = true
22
+ Redux::Store.preloaded_state = Isomorfeus.store.get_state
23
+ end
24
+ Isomorfeus.force_init_store!
25
+ execute_init_classes
26
+ end
27
+
28
+ def add_client_init_class_name(init_class_name)
29
+ client_init_class_names << init_class_name
30
+ end
31
+
32
+ def add_client_init_after_store_class_name(init_class_name)
33
+ client_init_after_store_class_names << init_class_name
34
+ end
35
+
36
+ def add_client_option(key, value = nil)
37
+ self.class.attr_accessor(key)
38
+ self.send("#{key}=", value)
39
+ end
40
+
41
+ # only used for SSR
42
+ def cached_component_classes
43
+ @cached_component_classes ||= {}
44
+ end
45
+
46
+ # only used for SSR
47
+ def cached_component_class(class_name)
48
+ return cached_component_classes[class_name] if cached_component_classes.key?(class_name)
49
+ cached_component_classes[class_name] = "::#{class_name}".constantize
50
+ end
51
+
52
+ def execute_init_classes
53
+ client_init_class_names.each do |constant|
54
+ constant.constantize.send(:init)
55
+ end
56
+ end
57
+
58
+ def execute_init_after_store_classes
59
+ client_init_after_store_class_names.each do |constant|
60
+ constant.constantize.send(:init)
61
+ end
62
+ end
63
+
64
+ def env=(env_string)
65
+ @env = env_string ? env_string : 'development'
66
+ @development = (@env == 'development') ? true : false
67
+ @production = (@env == 'production') ? true : false
68
+ @test = (@env == 'test') ? true : false
69
+ end
70
+
71
+ def development?
72
+ @development
73
+ end
74
+
75
+ def production?
76
+ @production
77
+ end
78
+
79
+ def test?
80
+ @test
81
+ end
82
+
83
+ def start_app!
84
+ Isomorfeus.zeitwerk.setup
85
+ Isomorfeus::TopLevel.mount!
86
+ end
87
+
88
+ def force_render
89
+ begin
90
+ if Isomorfeus.top_component
91
+ Preact.find_dom_node(Isomorfeus.top_component) if on_browser? || on_desktop? # if not mounted will raise
92
+ if `typeof Opal.global.deepForceUpdate === 'undefined'`
93
+ Isomorfeus.top_component.JS.forceUpdate()
94
+ else
95
+ `Opal.global.deepForceUpdate(#{Isomorfeus.top_component})`
96
+ end
97
+ end
98
+ rescue Exception => e
99
+ # TODO try mount first
100
+ # if it fails
101
+ `console.error("force_render failed'! Error: " + #{e.message} + "! Reloading page.")`
102
+ `location.reload()` if on_browser?
103
+ end
104
+ nil
105
+ end
106
+ end
107
+
108
+ self.add_client_option(:client_init_class_names, [])
109
+ self.add_client_option(:client_init_after_store_class_names, [])
110
+ else
111
+ class << self
112
+ attr_reader :component_cache_init_block
113
+ attr_accessor :server_side_rendering
114
+ attr_accessor :ssr_hot_asset_url
115
+ attr_reader :env
116
+ attr_accessor :zeitwerk
117
+ attr_accessor :zeitwerk_lock
118
+
119
+ def component_cache_init(&block)
120
+ @component_cache_init_block = block
121
+ end
122
+
123
+ def configuration(&block)
124
+ block.call(self)
125
+ end
126
+
127
+ def env=(env_string)
128
+ @env = env_string ? env_string.to_s : 'development'
129
+ @development = (@env == 'development') ? true : false
130
+ @production = (@env == 'production') ? true : false
131
+ @test = (@env == 'test') ? true : false
132
+ end
133
+
134
+ def development?
135
+ @development
136
+ end
137
+
138
+ def production?
139
+ @production
140
+ end
141
+
142
+ def test?
143
+ @test
144
+ end
145
+
146
+ def ssr_contexts
147
+ @ssr_contexts ||= {}
148
+ end
149
+
150
+ def version
151
+ Isomorfeus::VERSION
152
+ end
153
+
154
+ def load_configuration(directory)
155
+ Dir.glob(File.join(directory, '*.rb')).sort.each do |file|
156
+ require File.expand_path(file)
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ class << self
163
+ def raise_error(error_class: nil, message: nil, stack: nil)
164
+ error_class = RuntimeError unless error_class
165
+ execution_environment = if on_browser? then 'on Browser'
166
+ elsif on_ssr? then 'in Server Side Rendering'
167
+ elsif on_server? then 'on Server'
168
+ elsif on_mobile? then 'on Mobile'
169
+ elsif on_database? then 'on Database'
170
+ else
171
+ 'on Client'
172
+ end
173
+ error = error_class.new("Isomorfeus in #{env} #{execution_environment}:\n#{message}")
174
+ error.set_backtrace(stack) if stack
175
+
176
+ if Isomorfeus.development?
177
+ if RUBY_ENGINE == 'opal'
178
+ ecn = error_class ? error_class.name : ''
179
+ m = message ? message : ''
180
+ s = stack ? stack : ''
181
+ `console.error(ecn, m, s)`
182
+ else
183
+ STDERR.puts "#{ecn}: #{m}\n #{s}"
184
+ end
185
+ end
186
+ raise error
187
+ end
188
+ end
189
+ end