mayu-live 0.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.
Files changed (204) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +661 -0
  3. data/README.md +598 -0
  4. data/exe/mayu +33 -0
  5. data/lib/mayu/app_metrics.rb +93 -0
  6. data/lib/mayu/banner.rb +12 -0
  7. data/lib/mayu/client/README.md +17 -0
  8. data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js +1 -0
  9. data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js.br +0 -0
  10. data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js.map +1 -0
  11. data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js.map.br +0 -0
  12. data/lib/mayu/client/dist/custom-elements/mayu-alert-cd7ad2a4.js +1 -0
  13. data/lib/mayu/client/dist/custom-elements/mayu-alert-cd7ad2a4.js.map +1 -0
  14. data/lib/mayu/client/dist/custom-elements/mayu-disconnected-9f349f46.js +1 -0
  15. data/lib/mayu/client/dist/custom-elements/mayu-disconnected-9f349f46.js.map +1 -0
  16. data/lib/mayu/client/dist/custom-elements/mayu-exception-63df4e8c.js +1 -0
  17. data/lib/mayu/client/dist/custom-elements/mayu-exception-63df4e8c.js.map +1 -0
  18. data/lib/mayu/client/dist/custom-elements/mayu-ping-c498c2a6.js +1 -0
  19. data/lib/mayu/client/dist/custom-elements/mayu-ping-c498c2a6.js.map +1 -0
  20. data/lib/mayu/client/dist/custom-elements/mayu-progress-bar-eb3e1ac8.js +1 -0
  21. data/lib/mayu/client/dist/custom-elements/mayu-progress-bar-eb3e1ac8.js.map +1 -0
  22. data/lib/mayu/client/dist/entries.json +3 -0
  23. data/lib/mayu/client/dist/main-4b49dbc4.js +1 -0
  24. data/lib/mayu/client/dist/main-4b49dbc4.js.br +0 -0
  25. data/lib/mayu/client/dist/main-4b49dbc4.js.map +1 -0
  26. data/lib/mayu/client/dist/main-4b49dbc4.js.map.br +0 -0
  27. data/lib/mayu/client/package.json +39 -0
  28. data/lib/mayu/client/rollup.config.js +81 -0
  29. data/lib/mayu/client/src/DecompressionStream.ts +15 -0
  30. data/lib/mayu/client/src/DecompressionStreamPolyfill.ts +43 -0
  31. data/lib/mayu/client/src/MimeTypes.ts +4 -0
  32. data/lib/mayu/client/src/NodeTree.ts +445 -0
  33. data/lib/mayu/client/src/custom-elements/mayu-alert.html +137 -0
  34. data/lib/mayu/client/src/custom-elements/mayu-alert.ts +62 -0
  35. data/lib/mayu/client/src/custom-elements/mayu-disconnected.html +134 -0
  36. data/lib/mayu/client/src/custom-elements/mayu-disconnected.ts +51 -0
  37. data/lib/mayu/client/src/custom-elements/mayu-exception.html +79 -0
  38. data/lib/mayu/client/src/custom-elements/mayu-exception.ts +28 -0
  39. data/lib/mayu/client/src/custom-elements/mayu-log.html +70 -0
  40. data/lib/mayu/client/src/custom-elements/mayu-log.ts +42 -0
  41. data/lib/mayu/client/src/custom-elements/mayu-ping.html +36 -0
  42. data/lib/mayu/client/src/custom-elements/mayu-ping.ts +53 -0
  43. data/lib/mayu/client/src/custom-elements/mayu-progress-bar.html +44 -0
  44. data/lib/mayu/client/src/custom-elements/mayu-progress-bar.ts +40 -0
  45. data/lib/mayu/client/src/custom-elements/types.d.ts +4 -0
  46. data/lib/mayu/client/src/global.d.ts +26 -0
  47. data/lib/mayu/client/src/h.ts +27 -0
  48. data/lib/mayu/client/src/logger.ts +56 -0
  49. data/lib/mayu/client/src/main.ts +271 -0
  50. data/lib/mayu/client/src/serializeEvent.ts +90 -0
  51. data/lib/mayu/client/src/stream.ts +175 -0
  52. data/lib/mayu/client/src/types.ts +1 -0
  53. data/lib/mayu/client/src/utils.ts +71 -0
  54. data/lib/mayu/client/tsconfig.json +18 -0
  55. data/lib/mayu/colors.rb +34 -0
  56. data/lib/mayu/commands/base.rb +22 -0
  57. data/lib/mayu/commands/build.rb +82 -0
  58. data/lib/mayu/commands.rb +53 -0
  59. data/lib/mayu/component/base.rb +177 -0
  60. data/lib/mayu/component/handler_ref.rb +99 -0
  61. data/lib/mayu/component/helpers.rb +93 -0
  62. data/lib/mayu/component/interface.rb +18 -0
  63. data/lib/mayu/component/wrapper.rb +165 -0
  64. data/lib/mayu/component.rb +54 -0
  65. data/lib/mayu/configuration.rb +195 -0
  66. data/lib/mayu/disable_sorbet.rb +23 -0
  67. data/lib/mayu/environment.rb +151 -0
  68. data/lib/mayu/event_stream.rb +158 -0
  69. data/lib/mayu/fetch.rb +88 -0
  70. data/lib/mayu/html.rb +53 -0
  71. data/lib/mayu/html.yaml +767 -0
  72. data/lib/mayu/message_cipher.rb +172 -0
  73. data/lib/mayu/message_cipher.test.rb +16 -0
  74. data/lib/mayu/metrics/collector.rb +161 -0
  75. data/lib/mayu/metrics/exporter.rb +47 -0
  76. data/lib/mayu/metrics/reporter.rb +187 -0
  77. data/lib/mayu/metrics.rb +82 -0
  78. data/lib/mayu/ref_counter.rb +57 -0
  79. data/lib/mayu/resources/README.md +14 -0
  80. data/lib/mayu/resources/asset.rb +71 -0
  81. data/lib/mayu/resources/assets.rb +76 -0
  82. data/lib/mayu/resources/dependency_graph.rb +306 -0
  83. data/lib/mayu/resources/dot_exporter.rb +167 -0
  84. data/lib/mayu/resources/generators/base.rb +18 -0
  85. data/lib/mayu/resources/generators/copy_file.rb +26 -0
  86. data/lib/mayu/resources/generators/image.rb +106 -0
  87. data/lib/mayu/resources/generators/write_file.rb +39 -0
  88. data/lib/mayu/resources/hot_swap/file_watcher.rb +69 -0
  89. data/lib/mayu/resources/hot_swap.rb +46 -0
  90. data/lib/mayu/resources/mermaid_exporter.rb +210 -0
  91. data/lib/mayu/resources/registry.rb +190 -0
  92. data/lib/mayu/resources/resolver/base.rb +32 -0
  93. data/lib/mayu/resources/resolver/filesystem.rb +94 -0
  94. data/lib/mayu/resources/resolver/static.rb +27 -0
  95. data/lib/mayu/resources/resolver.rb +13 -0
  96. data/lib/mayu/resources/resource.rb +150 -0
  97. data/lib/mayu/resources/transformers/__test__/css/adjacent_selectors.in.css +3 -0
  98. data/lib/mayu/resources/transformers/__test__/css/adjacent_selectors.out.css +6 -0
  99. data/lib/mayu/resources/transformers/__test__/css/attributes.in.css +3 -0
  100. data/lib/mayu/resources/transformers/__test__/css/attributes.out.css +6 -0
  101. data/lib/mayu/resources/transformers/__test__/css/composes.in.css +6 -0
  102. data/lib/mayu/resources/transformers/__test__/css/composes.out.css +10 -0
  103. data/lib/mayu/resources/transformers/__test__/css/element_selectors.in.css +3 -0
  104. data/lib/mayu/resources/transformers/__test__/css/element_selectors.out.css +6 -0
  105. data/lib/mayu/resources/transformers/__test__/css/has.in.css +7 -0
  106. data/lib/mayu/resources/transformers/__test__/css/has.out.css +10 -0
  107. data/lib/mayu/resources/transformers/__test__/css/media_queries.in.css +8 -0
  108. data/lib/mayu/resources/transformers/__test__/css/media_queries.out.css +12 -0
  109. data/lib/mayu/resources/transformers/__test__/css/pseudo_classes.in.css +5 -0
  110. data/lib/mayu/resources/transformers/__test__/css/pseudo_classes.out.css +6 -0
  111. data/lib/mayu/resources/transformers/__test__/haml/README.md +10 -0
  112. data/lib/mayu/resources/transformers/__test__/haml/case.haml +8 -0
  113. data/lib/mayu/resources/transformers/__test__/haml/case.rb +15 -0
  114. data/lib/mayu/resources/transformers/__test__/haml/class_names.haml +13 -0
  115. data/lib/mayu/resources/transformers/__test__/haml/class_names.rb +26 -0
  116. data/lib/mayu/resources/transformers/__test__/haml/comments.haml +5 -0
  117. data/lib/mayu/resources/transformers/__test__/haml/comments.rb +5 -0
  118. data/lib/mayu/resources/transformers/__test__/haml/css.haml +3 -0
  119. data/lib/mayu/resources/transformers/__test__/haml/css.rb +11 -0
  120. data/lib/mayu/resources/transformers/__test__/haml/dashes.haml +3 -0
  121. data/lib/mayu/resources/transformers/__test__/haml/dashes.rb +11 -0
  122. data/lib/mayu/resources/transformers/__test__/haml/early_return.haml +4 -0
  123. data/lib/mayu/resources/transformers/__test__/haml/early_return.rb +9 -0
  124. data/lib/mayu/resources/transformers/__test__/haml/early_return2.haml +3 -0
  125. data/lib/mayu/resources/transformers/__test__/haml/early_return2.rb +6 -0
  126. data/lib/mayu/resources/transformers/__test__/haml/handlers.haml +6 -0
  127. data/lib/mayu/resources/transformers/__test__/haml/handlers.rb +12 -0
  128. data/lib/mayu/resources/transformers/__test__/haml/if_else.haml +6 -0
  129. data/lib/mayu/resources/transformers/__test__/haml/if_else.rb +12 -0
  130. data/lib/mayu/resources/transformers/__test__/haml/interpolation.haml +8 -0
  131. data/lib/mayu/resources/transformers/__test__/haml/interpolation.rb +11 -0
  132. data/lib/mayu/resources/transformers/__test__/haml/object_ref_as_key.haml +1 -0
  133. data/lib/mayu/resources/transformers/__test__/haml/object_ref_as_key.rb +5 -0
  134. data/lib/mayu/resources/transformers/__test__/haml/props.haml +4 -0
  135. data/lib/mayu/resources/transformers/__test__/haml/props.rb +11 -0
  136. data/lib/mayu/resources/transformers/__test__/haml/slots.haml +5 -0
  137. data/lib/mayu/resources/transformers/__test__/haml/slots.rb +9 -0
  138. data/lib/mayu/resources/transformers/__test__/haml/slots_dynamic.haml +3 -0
  139. data/lib/mayu/resources/transformers/__test__/haml/slots_dynamic.rb +9 -0
  140. data/lib/mayu/resources/transformers/__test__/haml/slots_fallback.haml +3 -0
  141. data/lib/mayu/resources/transformers/__test__/haml/slots_fallback.rb +5 -0
  142. data/lib/mayu/resources/transformers/__test__/haml/spacing.haml +5 -0
  143. data/lib/mayu/resources/transformers/__test__/haml/spacing.rb +14 -0
  144. data/lib/mayu/resources/transformers/__test__/haml/spacing2.haml +10 -0
  145. data/lib/mayu/resources/transformers/__test__/haml/spacing2.rb +11 -0
  146. data/lib/mayu/resources/transformers/__test__/haml/spacing3.haml +3 -0
  147. data/lib/mayu/resources/transformers/__test__/haml/spacing3.rb +10 -0
  148. data/lib/mayu/resources/transformers/css/rouge_lexer.rb +841 -0
  149. data/lib/mayu/resources/transformers/css.rb +100 -0
  150. data/lib/mayu/resources/transformers/css.test.rb +87 -0
  151. data/lib/mayu/resources/transformers/haml.rb +984 -0
  152. data/lib/mayu/resources/transformers/haml.test.rb +114 -0
  153. data/lib/mayu/resources/types/README.md +36 -0
  154. data/lib/mayu/resources/types/base.rb +35 -0
  155. data/lib/mayu/resources/types/component.rb +198 -0
  156. data/lib/mayu/resources/types/image.rb +169 -0
  157. data/lib/mayu/resources/types/javascript.rb +50 -0
  158. data/lib/mayu/resources/types/nil.rb +23 -0
  159. data/lib/mayu/resources/types/stylesheet.rb +119 -0
  160. data/lib/mayu/resources/types/svg.rb +69 -0
  161. data/lib/mayu/resources/types.rb +37 -0
  162. data/lib/mayu/routes.rb +170 -0
  163. data/lib/mayu/routing/builder.rb +108 -0
  164. data/lib/mayu/routing/matcher.rb +58 -0
  165. data/lib/mayu/routing/routes.rb +85 -0
  166. data/lib/mayu/routing.rb +17 -0
  167. data/lib/mayu/server/app.rb +494 -0
  168. data/lib/mayu/server/controller.rb +152 -0
  169. data/lib/mayu/server/errors.rb +110 -0
  170. data/lib/mayu/server/file_server.rb +140 -0
  171. data/lib/mayu/server.rb +63 -0
  172. data/lib/mayu/session.rb +358 -0
  173. data/lib/mayu/state/README.md +6 -0
  174. data/lib/mayu/state/action_creator.rb +191 -0
  175. data/lib/mayu/state/action_wrapper.rb +30 -0
  176. data/lib/mayu/state/loader.rb +220 -0
  177. data/lib/mayu/state/store.rb +82 -0
  178. data/lib/mayu/state.rb +8 -0
  179. data/lib/mayu/state.test.rb +97 -0
  180. data/lib/mayu/utils.rb +114 -0
  181. data/lib/mayu/vdom/children.rb +117 -0
  182. data/lib/mayu/vdom/component_marshaler.rb +53 -0
  183. data/lib/mayu/vdom/css_attributes.rb +131 -0
  184. data/lib/mayu/vdom/descriptor.rb +151 -0
  185. data/lib/mayu/vdom/descriptor.test.rb +26 -0
  186. data/lib/mayu/vdom/dom.rb +239 -0
  187. data/lib/mayu/vdom/h.rb +22 -0
  188. data/lib/mayu/vdom/id_generator.rb +55 -0
  189. data/lib/mayu/vdom/interfaces.rb +186 -0
  190. data/lib/mayu/vdom/marshalling.rb +78 -0
  191. data/lib/mayu/vdom/reconciliation.rb +205 -0
  192. data/lib/mayu/vdom/reconciliation.test.rb +56 -0
  193. data/lib/mayu/vdom/special_elements.rb +108 -0
  194. data/lib/mayu/vdom/update_context.rb +180 -0
  195. data/lib/mayu/vdom/vdom.perf.test.rb +146 -0
  196. data/lib/mayu/vdom/vnode.rb +266 -0
  197. data/lib/mayu/vdom/vtree.rb +672 -0
  198. data/lib/mayu/vdom/vtree.test.rb +68 -0
  199. data/lib/mayu/vdom.rb +8 -0
  200. data/lib/mayu/vdom.test.rb +73 -0
  201. data/lib/mayu/version.rb +6 -0
  202. data/lib/mayu.rb +8 -0
  203. data/mayu-live.gemspec +70 -0
  204. metadata +612 -0
data/lib/mayu/utils.rb ADDED
@@ -0,0 +1,114 @@
1
+ # typed: strict
2
+
3
+ module Mayu
4
+ module Utils
5
+ extend T::Sig
6
+
7
+ sig { params(unit: Symbol).returns(Float) }
8
+ def self.monotonic_now(unit = :float_millisecond)
9
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit).to_f
10
+ end
11
+
12
+ sig { params(unit: Symbol, block: T.proc.void).returns(Float) }
13
+ def self.measure_time(unit = :float_millisecond, &block)
14
+ start = monotonic_now
15
+ yield
16
+ monotonic_now - start
17
+ end
18
+
19
+ sig do
20
+ params(
21
+ hash: T::Hash[T.untyped, T.untyped],
22
+ path: T::Array[String]
23
+ ).returns(T::Hash[Symbol, T.untyped])
24
+ end
25
+ def self.flatten_props(hash, path = [])
26
+ hash.reduce({}) do |obj, (k, v)|
27
+ next obj.merge(style: v) if k == :style && path.empty?
28
+
29
+ current_path = [*path, k]
30
+
31
+ obj.merge(
32
+ case v
33
+ when Hash
34
+ flatten_props(v, current_path)
35
+ else
36
+ { current_path.join("_").to_sym => v }
37
+ end
38
+ )
39
+ end
40
+ end
41
+
42
+ class DeepFreezer
43
+ extend T::Sig
44
+ extend T::Generic
45
+ Elem = type_member { { upper: Object } }
46
+
47
+ sig { params(obj: Elem).void }
48
+ def initialize(obj) = @obj = obj
49
+
50
+ sig { returns(Elem) }
51
+ def deep_freeze
52
+ case @obj
53
+ when Hash
54
+ @obj.freeze
55
+ T.cast(
56
+ @obj
57
+ .transform_keys { Utils.deep_freeze(_1) }
58
+ .transform_values { Utils.deep_freeze(_1) },
59
+ Elem
60
+ )
61
+ when Array
62
+ T.cast(@obj.map(&:freeze), Elem)
63
+ else
64
+ @obj.freeze
65
+ end
66
+ end
67
+ end
68
+
69
+ class DeepDuper
70
+ extend T::Sig
71
+ extend T::Generic
72
+ Elem = type_member { { upper: Object } }
73
+
74
+ sig { params(obj: Elem).void }
75
+ def initialize(obj) = @obj = obj
76
+
77
+ sig { returns(Elem) }
78
+ def deep_dup
79
+ case @obj
80
+ when Hash
81
+ @obj.dup
82
+ T.cast(
83
+ @obj
84
+ .transform_keys { Utils.deep_dup(_1) }
85
+ .transform_values { Utils.deep_dup(_1) },
86
+ Elem
87
+ )
88
+ when Array
89
+ T.cast(@obj.map(&:dup), Elem)
90
+ else
91
+ @obj.dup
92
+ end
93
+ end
94
+ end
95
+
96
+ sig do
97
+ type_parameters(:O)
98
+ .params(obj: T.type_parameter(:O))
99
+ .returns(T.type_parameter(:O))
100
+ end
101
+ def self.deep_freeze(obj)
102
+ DeepFreezer.new(obj).deep_freeze
103
+ end
104
+
105
+ sig do
106
+ type_parameters(:O)
107
+ .params(obj: T.type_parameter(:O))
108
+ .returns(T.type_parameter(:O))
109
+ end
110
+ def self.deep_dup(obj)
111
+ DeepDuper.new(obj).deep_dup
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,117 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "children"
5
+ require_relative "../component"
6
+ require_relative "component_marshaler"
7
+
8
+ module Mayu
9
+ module VDOM
10
+ Slots =
11
+ T.type_alias do
12
+ T::Hash[T.nilable(String), T::Array[Interfaces::Descriptor]]
13
+ end
14
+
15
+ class Children
16
+ extend T::Sig
17
+ extend T::Generic
18
+ include Enumerable
19
+
20
+ include Interfaces::Children
21
+
22
+ sig do
23
+ params(
24
+ descriptors: T::Array[Interfaces::Descriptor],
25
+ parent_type: T.untyped
26
+ ).void
27
+ end
28
+ def self.check_duplicate_keys(descriptors, parent_type: "??unknown??")
29
+ keys = descriptors.map(&:key).compact
30
+ duplicates = keys.reject { keys.rindex(_1) == keys.index(_1) }.uniq
31
+ duplicates.each do |key|
32
+ Console.logger.warn(
33
+ self,
34
+ "Duplicate keys detected: #{key.inspect}",
35
+ "This may cause an update error!",
36
+ "Parent type: #{parent_type.inspect}"
37
+ )
38
+ end
39
+ end
40
+
41
+ Elem = type_member { { fixed: Interfaces::Descriptor } }
42
+
43
+ sig do
44
+ params(
45
+ descriptors: T::Array[Interfaces::Descriptor],
46
+ parent_type: T.untyped
47
+ ).void
48
+ end
49
+ def initialize(descriptors, parent_type: nil)
50
+ @descriptors =
51
+ T.let(
52
+ Descriptor::Factory.clean(descriptors, parent_type:),
53
+ T::Array[Interfaces::Descriptor]
54
+ )
55
+ @slots = T.let(nil, T.nilable(Slots))
56
+ end
57
+
58
+ sig { returns(T::Array[Interfaces::Descriptor]) }
59
+ def to_a = @descriptors
60
+
61
+ sig { params(other: T.untyped).returns(T::Boolean) }
62
+ def ==(other)
63
+ case other
64
+ when Children
65
+ @descriptors == other.to_a
66
+ when Array
67
+ @descriptors == other
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ sig { returns(T::Boolean) }
74
+ def empty? = @descriptors.empty?
75
+
76
+ sig do
77
+ override
78
+ .params(
79
+ name: T.nilable(String),
80
+ fallback: T.nilable(T.proc.returns(Interfaces::Descriptor))
81
+ )
82
+ .returns(
83
+ T.nilable(
84
+ T.any(Interfaces::Descriptor, T::Array[Interfaces::Descriptor])
85
+ )
86
+ )
87
+ end
88
+ def slot(name = nil, &fallback)
89
+ case slots.fetch(name, [])
90
+ in []
91
+ yield if block_given?
92
+ in [one]
93
+ one
94
+ in [*many] unless name
95
+ many
96
+ in [*many]
97
+ raise "Got #{many.size} slots one slot with name #{name.inspect}, #{many.map(&:type).inspect}"
98
+ end
99
+ end
100
+
101
+ sig { returns(String) }
102
+ def join = @descriptors.join
103
+
104
+ sig { returns(Slots) }
105
+ def slots = @slots ||= @descriptors.group_by(&:slot)
106
+
107
+ sig do
108
+ override
109
+ .params(block: T.proc.params(arg0: T.untyped).returns(BasicObject))
110
+ .returns(T.untyped)
111
+ end
112
+ def each(&block)
113
+ @descriptors.each(&block)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,53 @@
1
+ # typed: strict
2
+
3
+ require_relative "../component"
4
+
5
+ module Mayu
6
+ module VDOM
7
+ class ComponentMarshaler
8
+ extend T::Sig
9
+
10
+ sig { returns(T.untyped) }
11
+ attr_reader :type
12
+
13
+ sig { params(type: T.untyped).void }
14
+ def initialize(type)
15
+ @type =
16
+ T.let(
17
+ if Component === type
18
+ klass = T.cast(type, T.class_of(Component::Base))
19
+
20
+ # TODO: Would be better to do something like
21
+ # resources.resouce_for_type(klass)
22
+ if resource =
23
+ (
24
+ begin
25
+ klass.__mayu_resource
26
+ rescue StandardError
27
+ nil
28
+ end
29
+ )
30
+ component = resource.path
31
+ { component: }
32
+ else
33
+ { klass: klass }
34
+ end
35
+ else
36
+ type
37
+ end,
38
+ T.untyped
39
+ )
40
+ end
41
+
42
+ sig { returns(T.untyped) }
43
+ def marshal_dump
44
+ @type
45
+ end
46
+
47
+ sig { params(a: T.untyped).void }
48
+ def marshal_load(a)
49
+ @type = a
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,131 @@
1
+ # typed: strict
2
+
3
+ require_relative "update_context"
4
+ require_relative "vnode"
5
+
6
+ module Mayu
7
+ module VDOM
8
+ class CSSAttributes
9
+ extend T::Sig
10
+
11
+ # CSS properties which accept numbers but are not in units of "px".
12
+ # Copied from React:
13
+ # https://github.com/facebook/react/blob/a7c57268fb71163e4abb5e386c0d0e63290baaae/packages/react-dom/src/shared/CSSProperty.js
14
+ UNITLESS_PROPERTIES =
15
+ T.let(
16
+ [
17
+ :animation_iteration_count,
18
+ :aspect_ratio,
19
+ :border_image_outset,
20
+ :border_image_slice,
21
+ :border_image_width,
22
+ :box_flex,
23
+ :box_flex_group,
24
+ :box_ordinal_group,
25
+ :column_count,
26
+ :columns,
27
+ :flex,
28
+ :flex_grow,
29
+ :flex_positive,
30
+ :flex_shrink,
31
+ :flex_negative,
32
+ :flex_order,
33
+ :grid_area,
34
+ :grid_row,
35
+ :grid_row_end,
36
+ :grid_row_span,
37
+ :grid_row_start,
38
+ :grid_column,
39
+ :grid_column_end,
40
+ :grid_column_span,
41
+ :grid_column_start,
42
+ :font_weight,
43
+ :line_clamp,
44
+ :line_height,
45
+ :opacity,
46
+ :order,
47
+ :orphans,
48
+ :tab_size,
49
+ :widows,
50
+ :z_index,
51
+ :zoom,
52
+ # SVG-related properties
53
+ :fill_opacity,
54
+ :flood_opacity,
55
+ :stop_opacity,
56
+ :stroke_dasharray,
57
+ :stroke_dashoffset,
58
+ :stroke_miterlimit,
59
+ :stroke_opacity,
60
+ :stroke_width
61
+ ].freeze,
62
+ T::Array[Symbol]
63
+ )
64
+
65
+ sig { returns(T::Hash[Symbol, T.untyped]) }
66
+ attr_reader :properties
67
+
68
+ sig { params(properties: T.untyped).void }
69
+ def initialize(**properties)
70
+ @properties = properties
71
+ end
72
+
73
+ sig { returns(String) }
74
+ def to_s
75
+ @properties
76
+ .map do |property, value|
77
+ format(
78
+ "%s:%s;",
79
+ transform_property(property),
80
+ transform_value(property, value)
81
+ )
82
+ end
83
+ .join
84
+ end
85
+
86
+ sig do
87
+ params(ctx: UpdateContext, vnode: VNode, other: CSSAttributes).void
88
+ end
89
+ def patch(ctx, vnode, other)
90
+ (properties.keys | other.properties.keys).sort.each do |property|
91
+ old_value = properties[property]
92
+ new_value = other.properties[property]
93
+
94
+ next if old_value == new_value
95
+
96
+ unless new_value
97
+ ctx.css(vnode, transform_property(property))
98
+ next
99
+ end
100
+
101
+ ctx.css(
102
+ vnode,
103
+ transform_property(property),
104
+ transform_value(property, new_value)
105
+ )
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ sig { params(property: Symbol).returns(String) }
112
+ def transform_property(property)
113
+ property.to_s.tr("_", "-")
114
+ end
115
+
116
+ sig { params(property: Symbol, value: T.untyped).returns(String) }
117
+ def transform_value(property, value)
118
+ should_apply_px?(property, value) ? "#{value}px" : value.to_s
119
+ end
120
+
121
+ sig { params(property: Symbol, value: T.untyped).returns(T::Boolean) }
122
+ def should_apply_px?(property, value)
123
+ return false unless Integer === value
124
+ return false if UNITLESS_PROPERTIES.include?(property)
125
+ return false if property.start_with?("__")
126
+ return false if property.start_with?("--")
127
+ true
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,151 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "interfaces"
5
+ require_relative "../component"
6
+ require_relative "component_marshaler"
7
+ require_relative "children"
8
+ require_relative "./special_elements"
9
+
10
+ module Mayu
11
+ module VDOM
12
+ class Descriptor < T::Struct
13
+ class FactoryImpl
14
+ extend T::Sig
15
+ include Interfaces::Descriptor::Factory
16
+
17
+ sig { override.returns(Descriptor) }
18
+ def comment
19
+ Descriptor[Interfaces::Descriptor::COMMENT]
20
+ end
21
+
22
+ sig { override.params(text_content: T.untyped).returns(Descriptor) }
23
+ def text(text_content)
24
+ Descriptor[
25
+ Interfaces::Descriptor::TEXT,
26
+ text_content: text_content.to_s
27
+ ]
28
+ end
29
+
30
+ sig { override.params(obj: T.untyped).returns(Descriptor) }
31
+ def or_text(obj)
32
+ Descriptor === obj ? obj : text(obj.to_s)
33
+ end
34
+
35
+ sig do
36
+ override
37
+ .params(children: Component::Children, parent_type: T.untyped)
38
+ .returns(T::Array[Descriptor])
39
+ end
40
+ def clean(children, parent_type: nil)
41
+ cleaned = Array(children).flatten.select(&:itself) # Remove anything falsy
42
+
43
+ if parent_type == :title
44
+ # <title> can only have text children
45
+ cleaned.map { text(_1) }
46
+ else
47
+ cleaned.map { or_text(_1) }
48
+ end
49
+ end
50
+
51
+ sig do
52
+ override
53
+ .params(descriptors: T::Array[Interfaces::Descriptor])
54
+ .returns(T::Array[Interfaces::Descriptor])
55
+ end
56
+ def add_comments_between_texts(descriptors)
57
+ comment = self.comment
58
+
59
+ [*descriptors, nil].each_cons(2)
60
+ .flat_map do |curr, succ|
61
+ if curr&.text? && succ&.text?
62
+ [curr, comment]
63
+ else
64
+ curr
65
+ end
66
+ end
67
+ .compact
68
+ end
69
+ end
70
+
71
+ Factory = T.let(FactoryImpl.new, FactoryImpl)
72
+
73
+ extend T::Sig
74
+ include Interfaces::Descriptor
75
+
76
+ const :type, Component::ElementType
77
+ const :props, Component::Props
78
+ const :key, T.untyped
79
+ const :slot, T.nilable(String)
80
+
81
+ sig do
82
+ params(
83
+ type: Component::ElementType,
84
+ children: T.untyped,
85
+ props: T.untyped
86
+ ).returns(Descriptor)
87
+ end
88
+ def self.[](type, *children, **props)
89
+ type = T.let(SpecialElements.for_type(type), Component::ElementType)
90
+
91
+ children = Children.new(children, parent_type: type)
92
+ props = props.merge(children:)
93
+ key = props.delete(:key)
94
+ slot = props.delete(:slot)&.to_s
95
+
96
+ new(type:, key:, slot:, props:)
97
+ end
98
+
99
+ sig { returns(T::Array[T.untyped]) }
100
+ def marshal_dump
101
+ [ComponentMarshaler.new(type), Marshalling.dump_props(props), key, slot]
102
+ end
103
+
104
+ sig { params(a: T::Array[T.untyped]).void }
105
+ def marshal_load(a)
106
+ @type, @props, @key, @slot = a
107
+ freeze
108
+ end
109
+
110
+ ##
111
+ # This is used for hash comparisons,
112
+ # https://ruby-doc.org/3.2.0/Hash.html#class-Hash-label-User-Defined+Hash+Keys
113
+ sig { override.params(other: T.untyped).returns(T::Boolean) }
114
+ def eql?(other) = self.class === other && same?(other)
115
+
116
+ sig { override.returns(T::Boolean) }
117
+ def component? = Component.component_class?(@type)
118
+
119
+ sig { override.returns(T.class_of(Component::Base)) }
120
+ def component_class
121
+ if Component.component_class?(@type)
122
+ T.cast(@type, T.class_of(Component::Base))
123
+ else
124
+ raise "#{@type.inspect} is not a component class"
125
+ end
126
+ end
127
+
128
+ sig { returns(String) }
129
+ def to_s
130
+ return text if text?
131
+ return "" if comment?
132
+ "#<Descriptor type=#{type.inspect}>"
133
+ end
134
+
135
+ sig { override.params(other: Interfaces::Descriptor).returns(T::Boolean) }
136
+ def same?(other)
137
+ if key == other.key && type == other.type
138
+ if type == :input
139
+ # Inputs are considered to be different if their type changes.
140
+ # Is this a good behavior? I think maybe it comes from from Preact.
141
+ props[:type] == other.props[:type]
142
+ else
143
+ true
144
+ end
145
+ else
146
+ false
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,26 @@
1
+ # typed: true
2
+
3
+ require "minitest/autorun"
4
+ require "test_helper"
5
+ require_relative "descriptor"
6
+
7
+ class TestDescriptor < Minitest::Test
8
+ class MyComponent < Mayu::Component::Base
9
+ def render
10
+ end
11
+ end
12
+
13
+ def test_element
14
+ descriptor = Mayu::VDOM::Descriptor[:foo, key: "test-key"]
15
+ assert_equal(descriptor.type, :foo)
16
+ assert_equal(descriptor.props, { children: [] })
17
+ assert_equal(descriptor.key, "test-key")
18
+ end
19
+
20
+ def test_component
21
+ descriptor = Mayu::VDOM::Descriptor[MyComponent, key: "test-key"]
22
+ assert_equal(descriptor.type, MyComponent)
23
+ assert_equal(descriptor.props, { children: [] })
24
+ assert_equal(descriptor.key, "test-key")
25
+ end
26
+ end