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.
- checksums.yaml +7 -0
- data/COPYING +661 -0
- data/README.md +598 -0
- data/exe/mayu +33 -0
- data/lib/mayu/app_metrics.rb +93 -0
- data/lib/mayu/banner.rb +12 -0
- data/lib/mayu/client/README.md +17 -0
- data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js +1 -0
- data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js.br +0 -0
- data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js.map +1 -0
- data/lib/mayu/client/dist/DecompressionStreamPolyfill-3ceba43e.js.map.br +0 -0
- data/lib/mayu/client/dist/custom-elements/mayu-alert-cd7ad2a4.js +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-alert-cd7ad2a4.js.map +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-disconnected-9f349f46.js +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-disconnected-9f349f46.js.map +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-exception-63df4e8c.js +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-exception-63df4e8c.js.map +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-ping-c498c2a6.js +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-ping-c498c2a6.js.map +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-progress-bar-eb3e1ac8.js +1 -0
- data/lib/mayu/client/dist/custom-elements/mayu-progress-bar-eb3e1ac8.js.map +1 -0
- data/lib/mayu/client/dist/entries.json +3 -0
- data/lib/mayu/client/dist/main-4b49dbc4.js +1 -0
- data/lib/mayu/client/dist/main-4b49dbc4.js.br +0 -0
- data/lib/mayu/client/dist/main-4b49dbc4.js.map +1 -0
- data/lib/mayu/client/dist/main-4b49dbc4.js.map.br +0 -0
- data/lib/mayu/client/package.json +39 -0
- data/lib/mayu/client/rollup.config.js +81 -0
- data/lib/mayu/client/src/DecompressionStream.ts +15 -0
- data/lib/mayu/client/src/DecompressionStreamPolyfill.ts +43 -0
- data/lib/mayu/client/src/MimeTypes.ts +4 -0
- data/lib/mayu/client/src/NodeTree.ts +445 -0
- data/lib/mayu/client/src/custom-elements/mayu-alert.html +137 -0
- data/lib/mayu/client/src/custom-elements/mayu-alert.ts +62 -0
- data/lib/mayu/client/src/custom-elements/mayu-disconnected.html +134 -0
- data/lib/mayu/client/src/custom-elements/mayu-disconnected.ts +51 -0
- data/lib/mayu/client/src/custom-elements/mayu-exception.html +79 -0
- data/lib/mayu/client/src/custom-elements/mayu-exception.ts +28 -0
- data/lib/mayu/client/src/custom-elements/mayu-log.html +70 -0
- data/lib/mayu/client/src/custom-elements/mayu-log.ts +42 -0
- data/lib/mayu/client/src/custom-elements/mayu-ping.html +36 -0
- data/lib/mayu/client/src/custom-elements/mayu-ping.ts +53 -0
- data/lib/mayu/client/src/custom-elements/mayu-progress-bar.html +44 -0
- data/lib/mayu/client/src/custom-elements/mayu-progress-bar.ts +40 -0
- data/lib/mayu/client/src/custom-elements/types.d.ts +4 -0
- data/lib/mayu/client/src/global.d.ts +26 -0
- data/lib/mayu/client/src/h.ts +27 -0
- data/lib/mayu/client/src/logger.ts +56 -0
- data/lib/mayu/client/src/main.ts +271 -0
- data/lib/mayu/client/src/serializeEvent.ts +90 -0
- data/lib/mayu/client/src/stream.ts +175 -0
- data/lib/mayu/client/src/types.ts +1 -0
- data/lib/mayu/client/src/utils.ts +71 -0
- data/lib/mayu/client/tsconfig.json +18 -0
- data/lib/mayu/colors.rb +34 -0
- data/lib/mayu/commands/base.rb +22 -0
- data/lib/mayu/commands/build.rb +82 -0
- data/lib/mayu/commands.rb +53 -0
- data/lib/mayu/component/base.rb +177 -0
- data/lib/mayu/component/handler_ref.rb +99 -0
- data/lib/mayu/component/helpers.rb +93 -0
- data/lib/mayu/component/interface.rb +18 -0
- data/lib/mayu/component/wrapper.rb +165 -0
- data/lib/mayu/component.rb +54 -0
- data/lib/mayu/configuration.rb +195 -0
- data/lib/mayu/disable_sorbet.rb +23 -0
- data/lib/mayu/environment.rb +151 -0
- data/lib/mayu/event_stream.rb +158 -0
- data/lib/mayu/fetch.rb +88 -0
- data/lib/mayu/html.rb +53 -0
- data/lib/mayu/html.yaml +767 -0
- data/lib/mayu/message_cipher.rb +172 -0
- data/lib/mayu/message_cipher.test.rb +16 -0
- data/lib/mayu/metrics/collector.rb +161 -0
- data/lib/mayu/metrics/exporter.rb +47 -0
- data/lib/mayu/metrics/reporter.rb +187 -0
- data/lib/mayu/metrics.rb +82 -0
- data/lib/mayu/ref_counter.rb +57 -0
- data/lib/mayu/resources/README.md +14 -0
- data/lib/mayu/resources/asset.rb +71 -0
- data/lib/mayu/resources/assets.rb +76 -0
- data/lib/mayu/resources/dependency_graph.rb +306 -0
- data/lib/mayu/resources/dot_exporter.rb +167 -0
- data/lib/mayu/resources/generators/base.rb +18 -0
- data/lib/mayu/resources/generators/copy_file.rb +26 -0
- data/lib/mayu/resources/generators/image.rb +106 -0
- data/lib/mayu/resources/generators/write_file.rb +39 -0
- data/lib/mayu/resources/hot_swap/file_watcher.rb +69 -0
- data/lib/mayu/resources/hot_swap.rb +46 -0
- data/lib/mayu/resources/mermaid_exporter.rb +210 -0
- data/lib/mayu/resources/registry.rb +190 -0
- data/lib/mayu/resources/resolver/base.rb +32 -0
- data/lib/mayu/resources/resolver/filesystem.rb +94 -0
- data/lib/mayu/resources/resolver/static.rb +27 -0
- data/lib/mayu/resources/resolver.rb +13 -0
- data/lib/mayu/resources/resource.rb +150 -0
- data/lib/mayu/resources/transformers/__test__/css/adjacent_selectors.in.css +3 -0
- data/lib/mayu/resources/transformers/__test__/css/adjacent_selectors.out.css +6 -0
- data/lib/mayu/resources/transformers/__test__/css/attributes.in.css +3 -0
- data/lib/mayu/resources/transformers/__test__/css/attributes.out.css +6 -0
- data/lib/mayu/resources/transformers/__test__/css/composes.in.css +6 -0
- data/lib/mayu/resources/transformers/__test__/css/composes.out.css +10 -0
- data/lib/mayu/resources/transformers/__test__/css/element_selectors.in.css +3 -0
- data/lib/mayu/resources/transformers/__test__/css/element_selectors.out.css +6 -0
- data/lib/mayu/resources/transformers/__test__/css/has.in.css +7 -0
- data/lib/mayu/resources/transformers/__test__/css/has.out.css +10 -0
- data/lib/mayu/resources/transformers/__test__/css/media_queries.in.css +8 -0
- data/lib/mayu/resources/transformers/__test__/css/media_queries.out.css +12 -0
- data/lib/mayu/resources/transformers/__test__/css/pseudo_classes.in.css +5 -0
- data/lib/mayu/resources/transformers/__test__/css/pseudo_classes.out.css +6 -0
- data/lib/mayu/resources/transformers/__test__/haml/README.md +10 -0
- data/lib/mayu/resources/transformers/__test__/haml/case.haml +8 -0
- data/lib/mayu/resources/transformers/__test__/haml/case.rb +15 -0
- data/lib/mayu/resources/transformers/__test__/haml/class_names.haml +13 -0
- data/lib/mayu/resources/transformers/__test__/haml/class_names.rb +26 -0
- data/lib/mayu/resources/transformers/__test__/haml/comments.haml +5 -0
- data/lib/mayu/resources/transformers/__test__/haml/comments.rb +5 -0
- data/lib/mayu/resources/transformers/__test__/haml/css.haml +3 -0
- data/lib/mayu/resources/transformers/__test__/haml/css.rb +11 -0
- data/lib/mayu/resources/transformers/__test__/haml/dashes.haml +3 -0
- data/lib/mayu/resources/transformers/__test__/haml/dashes.rb +11 -0
- data/lib/mayu/resources/transformers/__test__/haml/early_return.haml +4 -0
- data/lib/mayu/resources/transformers/__test__/haml/early_return.rb +9 -0
- data/lib/mayu/resources/transformers/__test__/haml/early_return2.haml +3 -0
- data/lib/mayu/resources/transformers/__test__/haml/early_return2.rb +6 -0
- data/lib/mayu/resources/transformers/__test__/haml/handlers.haml +6 -0
- data/lib/mayu/resources/transformers/__test__/haml/handlers.rb +12 -0
- data/lib/mayu/resources/transformers/__test__/haml/if_else.haml +6 -0
- data/lib/mayu/resources/transformers/__test__/haml/if_else.rb +12 -0
- data/lib/mayu/resources/transformers/__test__/haml/interpolation.haml +8 -0
- data/lib/mayu/resources/transformers/__test__/haml/interpolation.rb +11 -0
- data/lib/mayu/resources/transformers/__test__/haml/object_ref_as_key.haml +1 -0
- data/lib/mayu/resources/transformers/__test__/haml/object_ref_as_key.rb +5 -0
- data/lib/mayu/resources/transformers/__test__/haml/props.haml +4 -0
- data/lib/mayu/resources/transformers/__test__/haml/props.rb +11 -0
- data/lib/mayu/resources/transformers/__test__/haml/slots.haml +5 -0
- data/lib/mayu/resources/transformers/__test__/haml/slots.rb +9 -0
- data/lib/mayu/resources/transformers/__test__/haml/slots_dynamic.haml +3 -0
- data/lib/mayu/resources/transformers/__test__/haml/slots_dynamic.rb +9 -0
- data/lib/mayu/resources/transformers/__test__/haml/slots_fallback.haml +3 -0
- data/lib/mayu/resources/transformers/__test__/haml/slots_fallback.rb +5 -0
- data/lib/mayu/resources/transformers/__test__/haml/spacing.haml +5 -0
- data/lib/mayu/resources/transformers/__test__/haml/spacing.rb +14 -0
- data/lib/mayu/resources/transformers/__test__/haml/spacing2.haml +10 -0
- data/lib/mayu/resources/transformers/__test__/haml/spacing2.rb +11 -0
- data/lib/mayu/resources/transformers/__test__/haml/spacing3.haml +3 -0
- data/lib/mayu/resources/transformers/__test__/haml/spacing3.rb +10 -0
- data/lib/mayu/resources/transformers/css/rouge_lexer.rb +841 -0
- data/lib/mayu/resources/transformers/css.rb +100 -0
- data/lib/mayu/resources/transformers/css.test.rb +87 -0
- data/lib/mayu/resources/transformers/haml.rb +984 -0
- data/lib/mayu/resources/transformers/haml.test.rb +114 -0
- data/lib/mayu/resources/types/README.md +36 -0
- data/lib/mayu/resources/types/base.rb +35 -0
- data/lib/mayu/resources/types/component.rb +198 -0
- data/lib/mayu/resources/types/image.rb +169 -0
- data/lib/mayu/resources/types/javascript.rb +50 -0
- data/lib/mayu/resources/types/nil.rb +23 -0
- data/lib/mayu/resources/types/stylesheet.rb +119 -0
- data/lib/mayu/resources/types/svg.rb +69 -0
- data/lib/mayu/resources/types.rb +37 -0
- data/lib/mayu/routes.rb +170 -0
- data/lib/mayu/routing/builder.rb +108 -0
- data/lib/mayu/routing/matcher.rb +58 -0
- data/lib/mayu/routing/routes.rb +85 -0
- data/lib/mayu/routing.rb +17 -0
- data/lib/mayu/server/app.rb +494 -0
- data/lib/mayu/server/controller.rb +152 -0
- data/lib/mayu/server/errors.rb +110 -0
- data/lib/mayu/server/file_server.rb +140 -0
- data/lib/mayu/server.rb +63 -0
- data/lib/mayu/session.rb +358 -0
- data/lib/mayu/state/README.md +6 -0
- data/lib/mayu/state/action_creator.rb +191 -0
- data/lib/mayu/state/action_wrapper.rb +30 -0
- data/lib/mayu/state/loader.rb +220 -0
- data/lib/mayu/state/store.rb +82 -0
- data/lib/mayu/state.rb +8 -0
- data/lib/mayu/state.test.rb +97 -0
- data/lib/mayu/utils.rb +114 -0
- data/lib/mayu/vdom/children.rb +117 -0
- data/lib/mayu/vdom/component_marshaler.rb +53 -0
- data/lib/mayu/vdom/css_attributes.rb +131 -0
- data/lib/mayu/vdom/descriptor.rb +151 -0
- data/lib/mayu/vdom/descriptor.test.rb +26 -0
- data/lib/mayu/vdom/dom.rb +239 -0
- data/lib/mayu/vdom/h.rb +22 -0
- data/lib/mayu/vdom/id_generator.rb +55 -0
- data/lib/mayu/vdom/interfaces.rb +186 -0
- data/lib/mayu/vdom/marshalling.rb +78 -0
- data/lib/mayu/vdom/reconciliation.rb +205 -0
- data/lib/mayu/vdom/reconciliation.test.rb +56 -0
- data/lib/mayu/vdom/special_elements.rb +108 -0
- data/lib/mayu/vdom/update_context.rb +180 -0
- data/lib/mayu/vdom/vdom.perf.test.rb +146 -0
- data/lib/mayu/vdom/vnode.rb +266 -0
- data/lib/mayu/vdom/vtree.rb +672 -0
- data/lib/mayu/vdom/vtree.test.rb +68 -0
- data/lib/mayu/vdom.rb +8 -0
- data/lib/mayu/vdom.test.rb +73 -0
- data/lib/mayu/version.rb +6 -0
- data/lib/mayu.rb +8 -0
- data/mayu-live.gemspec +70 -0
- 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
|