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
@@ -0,0 +1,114 @@
1
+ # typed: true
2
+
3
+ require "minitest/autorun"
4
+ require "test_helper"
5
+
6
+ require_relative "haml"
7
+ require "rouge"
8
+
9
+ class TestHaml < Minitest::Test
10
+ EXAMPLES_ROOT = File.join(__dir__, "__test__", "haml")
11
+
12
+ Dir[File.join(EXAMPLES_ROOT, "*.haml")].each do |input_path|
13
+ basename = File.basename(input_path, ".*")
14
+
15
+ if ENV["MATCH"] in String => match
16
+ next unless basename.include?(match)
17
+ end
18
+
19
+ skip_path = File.join(EXAMPLES_ROOT, "#{basename}.skip")
20
+ output_path = File.join(EXAMPLES_ROOT, "#{basename}.rb")
21
+
22
+ input = File.read(input_path)
23
+ expected = File.read(output_path)
24
+
25
+ define_method(:"test_#{basename}") do
26
+ T.bind(self, TestHaml)
27
+ skip File.read(skip_path) if File.exist?(skip_path)
28
+ actual = transform_and_format(input, path: input_path)
29
+ # File.write(output_path, actual)
30
+ assert_equal(expected, actual)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def transform_and_format_file(root:, path:)
37
+ transform_and_format(File.read(File.join(root, path)), path:)
38
+ end
39
+
40
+ def transform_and_format(
41
+ haml,
42
+ transform_elements_to_classes: false,
43
+ path: nil
44
+ )
45
+ transformed =
46
+ Mayu::Resources::Transformers::Haml.transform(
47
+ Mayu::Resources::Transformers::Haml::TransformOptions.new(
48
+ source: haml,
49
+ source_path: "app/components/MyComponent.haml",
50
+ source_line: 1,
51
+ content_hash: "abc123",
52
+ transform_elements_to_classes:
53
+ )
54
+ ).output
55
+
56
+ puts "\e[1mInput:\e[0;2m #{path}\e[0m"
57
+ puts prepend_line_numbers(
58
+ colorize_source(haml, Rouge::Lexers::Haml.new).each_line
59
+ )
60
+ handle_parse_error(transformed) do
61
+ formatted = SyntaxTree.format(transformed)
62
+ puts "\e[1mOutput:\e[0m"
63
+ puts prepend_line_numbers(
64
+ colorize_source(formatted, Rouge::Lexers::Ruby).each_line
65
+ )
66
+ puts
67
+ formatted
68
+ end
69
+ end
70
+
71
+ def handle_parse_error(source)
72
+ yield
73
+ rescue SyntaxTree::Parser::ParseError => e
74
+ start_line = [0, 0].max
75
+ formatted_source =
76
+ prepend_line_numbers(
77
+ extract_lines(source.to_s, start_line, -1),
78
+ start_line: start_line + 1,
79
+ error_line: e.lineno
80
+ ).join
81
+
82
+ Console.logger.error(self, <<~ERROR)
83
+ #{e.message} on line #{e.lineno} col #{e.column}
84
+ #{formatted_source}
85
+ ERROR
86
+
87
+ raise
88
+ end
89
+
90
+ def extract_lines(str, from, to)
91
+ str.each_line.to_a[from..to] || []
92
+ end
93
+
94
+ def colorize_source(source, lexer)
95
+ theme = Rouge::Themes::Monokai.new
96
+ formatter = Rouge::Formatters::Terminal256.new(theme:)
97
+ formatter.format(lexer.lex(source.chomp))
98
+ end
99
+
100
+ def prepend_line_numbers(lines, start_line: 1, error_line: nil)
101
+ number_format = "\e[38;5;250;48;5;236m%3d \e[0m"
102
+ error_format = "\e[41m%s\e[0m"
103
+
104
+ lines
105
+ .map
106
+ .with_index(start_line) do |line, i|
107
+ if error_line == i
108
+ format(error_format, line.chomp) + "\n"
109
+ else
110
+ line
111
+ end.prepend(format(number_format, i))
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,36 @@
1
+ # Resources::Types
2
+
3
+ These classes implement behaviors for different types of resources.
4
+
5
+ Some resources can generate static files during build time.
6
+ These static files should have a predictable filename based
7
+ on the content hash + some options.
8
+ Compressable types should be brotlied.
9
+ The generated files could be named like this:
10
+
11
+ 47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU=.css
12
+ 47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU=.css.br
13
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=.png
14
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=640w.png
15
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=768w.png
16
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=960w.png
17
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=1024w.png
18
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=1366w.png
19
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=1600w.png
20
+ M4CmsIPAxdPZRaERcnCA8mL7whpfFfArIziflCS0iUY=1920w.png
21
+ eN3Sv_BzhBLUw72oUgA5sCa8YF74CJm0Z8Z97eQgofk=.css
22
+ eN3Sv_BzhBLUw72oUgA5sCa8YF74CJm0Z8Z97eQgofk=.css.br
23
+
24
+ ## Resources::Types::Component
25
+
26
+ Loads a `.rb`-file or `.haml`-file and transpiles the latter,
27
+ then evaluates the code in the scope of a new class that inherits
28
+ `Mayu::Component::Base`.
29
+
30
+ ## Resources::Types::Image
31
+
32
+ Loads an image, stores its size and information about which
33
+ versions to generate for `srcset`.
34
+
35
+ During build time it will generate smaller versions of the image,
36
+ and store them in the configured directory for static files.
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require_relative "../resource"
5
+ require_relative "../asset"
6
+
7
+ module Mayu
8
+ module Resources
9
+ module Types
10
+ class Base
11
+ extend T::Sig
12
+
13
+ sig { params(resource: Resource).void }
14
+ def initialize(resource)
15
+ @resource = resource
16
+ end
17
+
18
+ sig { returns(T::Array[Asset]) }
19
+ def assets
20
+ []
21
+ end
22
+
23
+ sig { returns(String) }
24
+ def name
25
+ self.class.name.to_s.sub(/.*::/, "")
26
+ end
27
+
28
+ sig { params(assets_dir: String).returns(T::Array[Asset]) }
29
+ def generate_assets(assets_dir)
30
+ []
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require_relative "base"
5
+ require_relative "../../component/base"
6
+ require_relative "../transformers/haml"
7
+
8
+ module Mayu
9
+ module Resources
10
+ module Types
11
+ class Component < Base
12
+ module LoaderUtils
13
+ extend T::Sig
14
+
15
+ sig { params(mod: Module, resource: Resources::Resource).void }
16
+ def self.define_import(mod, resource)
17
+ mod.instance_exec(resource) do |resource|
18
+ define_singleton_method(:__resource) { resource }
19
+
20
+ sig do
21
+ params(path: String).returns(T.class_of(Mayu::Component::Base))
22
+ end
23
+ def self.import(path)
24
+ __resource.import(path) => Component => impl
25
+ impl.component
26
+ end
27
+
28
+ sig { params(path: String).returns(Image) }
29
+ def self.image(path)
30
+ __resource.import(path) => Image => impl
31
+ impl
32
+ end
33
+
34
+ sig { params(path: String).returns(SVG) }
35
+ def self.svg(path)
36
+ __resource.import(path) => SVG => impl
37
+ impl
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ extend T::Sig
44
+
45
+ ComponentBase = Mayu::Component::Base
46
+
47
+ sig { params(resource: Resource).void }
48
+ def initialize(resource)
49
+ @resource = resource
50
+
51
+ original_source = T.let(resource.read(encoding: "utf-8"), String)
52
+
53
+ source =
54
+ case File.extname(resource.path)
55
+ when ".haml"
56
+ transform_result =
57
+ Transformers::Haml.transform(
58
+ Transformers::Haml::TransformOptions.new(
59
+ source: original_source,
60
+ source_path: resource.path,
61
+ source_line: 1
62
+ )
63
+ )
64
+ source = transform_result.output
65
+
66
+ @inline_css =
67
+ T.let(
68
+ transform_result.css,
69
+ T.nilable(Transformers::CSS::TransformResult)
70
+ )
71
+
72
+ source
73
+ else
74
+ original_source
75
+ end
76
+
77
+ @source = T.let(source, String)
78
+ @component = T.let(nil, T.nilable(T.class_of(ComponentBase)))
79
+ end
80
+
81
+ sig { returns(T::Array[Asset]) }
82
+ def assets
83
+ return [] unless @inline_css
84
+
85
+ source_map_link =
86
+ "\n/*# sourceMappingURL=#{@inline_css.filename}.map */\n"
87
+
88
+ [
89
+ Asset.new(
90
+ @inline_css.filename,
91
+ Generators::WriteFile.new(
92
+ contents: @inline_css.output + source_map_link,
93
+ compress: true
94
+ )
95
+ ),
96
+ Asset.new(
97
+ @inline_css.filename + ".map",
98
+ Generators::WriteFile.new(
99
+ contents: JSON.generate(@inline_css.source_map),
100
+ compress: true
101
+ )
102
+ )
103
+ ]
104
+ end
105
+
106
+ sig { returns(T.class_of(ComponentBase)) }
107
+ def component
108
+ @component ||= setup_component
109
+ end
110
+
111
+ sig { returns(T.class_of(Mayu::Component::Base)) }
112
+ def setup_component
113
+ impl = Class.new(Mayu::Component::Base)
114
+
115
+ LoaderUtils.define_import(impl, @resource)
116
+
117
+ impl.__mayu_resource = @resource
118
+
119
+ impl.const_set(:INLINE_CSS_ASSETS, assets)
120
+ impl.const_set(:H, Mayu::VDOM::H)
121
+
122
+ begin
123
+ # $stderr.puts "\e[33m#{@source}\e[0m"
124
+ impl.class_eval(@source, @resource.path, 1)
125
+ rescue SyntaxTree::Parser::ParseError => e
126
+ $stderr.puts "\e[31mError parsing #{@resource.path}:#{e.lineno} #{e.message}\e[0m"
127
+
128
+ puts "Error on line #{e.lineno}"
129
+ @source
130
+ .each_line
131
+ .with_index(1) do |line, lineno|
132
+ if lineno == e.lineno
133
+ puts "\e[31m#{line.chomp}\e[0m"
134
+ else
135
+ puts "\e[33m#{line.chomp}\e[0m"
136
+ end
137
+ end
138
+ rescue => e
139
+ backtrace =
140
+ [*e.backtrace].reject { _1.include?("/gems/sorbet-runtime-") }
141
+ .join("\n")
142
+ $stderr.puts "\e[31mError loading #{@resource.path}: #{e.class.name}: #{e.message}\n\e[33m#{backtrace}\e[0m"
143
+ $stderr.puts "\e[33m#{@source}\e[0m"
144
+ raise "Error parsing #{@resource.absolute_path}"
145
+ end
146
+
147
+ styles =
148
+ @resource.registry.add_resource(
149
+ @resource.path.sub(/\.\w+\z/, ".css")
150
+ )
151
+
152
+ @resource.registry.dependency_graph.add_dependency(
153
+ @resource.path,
154
+ styles.path
155
+ )
156
+
157
+ classes = T.let(Hash.new, T::Hash[Symbol, String])
158
+
159
+ if styles.type.is_a?(Types::Stylesheet)
160
+ classes.merge!(styles.type.classes)
161
+
162
+ impl.instance_exec(styles) do |styles|
163
+ define_singleton_method(:stylesheet) { styles.type }
164
+ end
165
+ end
166
+
167
+ classes.merge!(@inline_css.classes) if @inline_css
168
+
169
+ unless classes.empty?
170
+ impl.instance_exec(
171
+ Resources::Types::Stylesheet::ClassNames.new(classes)
172
+ ) do |classnames|
173
+ define_singleton_method(:styles) { classnames }
174
+ define_method(:styles) { classnames }
175
+ end
176
+ end
177
+
178
+ impl
179
+ end
180
+
181
+ MarshalFormat =
182
+ T.type_alias do
183
+ [String, T.nilable(Transformers::CSS::TransformResult)]
184
+ end
185
+
186
+ sig { returns(MarshalFormat) }
187
+ def marshal_dump
188
+ [@source, @inline_css]
189
+ end
190
+
191
+ sig { params(args: MarshalFormat).void }
192
+ def marshal_load(args)
193
+ @source, @inline_css = args
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require "image_size"
5
+ require "base64"
6
+ require_relative "base"
7
+
8
+ module Mayu
9
+ module Resources
10
+ module Types
11
+ class Image < Base
12
+ extend T::Sig
13
+
14
+ FORMATS = T.let([:webp], T::Array[Symbol])
15
+
16
+ BREAKPOINTS =
17
+ T.let(
18
+ [120, 240, 320, 640, 768, 960, 1024, 1366, 1600, 1920, 3840].freeze,
19
+ T::Array[Integer]
20
+ )
21
+
22
+ class ImageDescriptor < T::Struct
23
+ const :format, Symbol
24
+ const :width, Integer
25
+ const :height, Integer
26
+ const :filename, String
27
+ end
28
+
29
+ sig { returns(ImageDescriptor) }
30
+ attr_reader :original
31
+ sig { returns(T::Array[ImageDescriptor]) }
32
+ attr_reader :versions
33
+ sig { returns(String) }
34
+ attr_reader :blur
35
+
36
+ sig { params(resource: Resource).void }
37
+ def initialize(resource)
38
+ @resource = resource
39
+
40
+ content_hash = Base64.urlsafe_encode64(resource.content_hash)
41
+ image_size = ImageSize.path(resource.absolute_path)
42
+
43
+ extname = File.extname(resource.path)
44
+ filename = "#{content_hash}.#{image_size.format}"
45
+
46
+ @original =
47
+ T.let(
48
+ ImageDescriptor.new(
49
+ format: image_size.format,
50
+ width: image_size.width,
51
+ height: image_size.height,
52
+ filename: filename
53
+ ),
54
+ ImageDescriptor
55
+ )
56
+
57
+ breakpoints =
58
+ BREAKPOINTS.select { _1 < image_size.width }.sort.reverse
59
+ aspect_ratio = image_size.height / image_size.width.to_f
60
+
61
+ formats = [image_size.format, *FORMATS].uniq
62
+
63
+ @blur =
64
+ T.let(
65
+ [
66
+ "convert",
67
+ resource.absolute_path,
68
+ "-resize",
69
+ "16x16>",
70
+ "-strip",
71
+ "png:-"
72
+ ].then { Shellwords.shelljoin(_1) }
73
+ .then { `#{_1}` }
74
+ .then { Base64.strict_encode64(_1) }
75
+ .prepend("data:image/png;base64,"),
76
+ String
77
+ )
78
+
79
+ @versions =
80
+ T.let(
81
+ formats
82
+ .map do |format|
83
+ breakpoints.map do |width|
84
+ ImageDescriptor.new(
85
+ format: format,
86
+ width:,
87
+ height: (width * aspect_ratio).to_i,
88
+ filename: "#{content_hash}#{width}w.#{format}"
89
+ )
90
+ end
91
+ end
92
+ .flatten,
93
+ T::Array[ImageDescriptor]
94
+ )
95
+ end
96
+
97
+ sig { returns(T::Array[Asset]) }
98
+ def assets
99
+ [
100
+ Asset.new(
101
+ @original.filename,
102
+ Generators::CopyFile.new(@resource.absolute_path)
103
+ ),
104
+ *@versions.map do |version|
105
+ Asset.new(
106
+ version.filename,
107
+ Generators::Image.new(@resource.absolute_path, version)
108
+ )
109
+ end
110
+ ]
111
+ end
112
+
113
+ sig { params(asset_dir: String).returns(T::Array[Asset]) }
114
+ def generate_assets(asset_dir)
115
+ assets.each { |asset| asset.process(asset_dir) }
116
+ end
117
+
118
+ sig { returns(String) }
119
+ def src
120
+ "/__mayu/static/#{@original.filename}"
121
+ end
122
+
123
+ sig { returns(String) }
124
+ def srcset
125
+ [@original, *@versions.filter { _1.format == :webp }].map do |version|
126
+ "/__mayu/static/#{version.filename} #{version.width}w"
127
+ end
128
+ .reverse
129
+ .join(",")
130
+ end
131
+
132
+ sig { returns(String) }
133
+ def sizes
134
+ [
135
+ "100vw",
136
+ *@versions
137
+ .filter { _1.format == :webp }
138
+ .map do |version|
139
+ "(max-width: #{version.width}px) #{version.width}px"
140
+ end
141
+ ].reverse.join(", ")
142
+ end
143
+
144
+ sig { returns(Float) }
145
+ def aspect_ratio
146
+ original.width / original.height.to_f
147
+ end
148
+
149
+ MarshalFormat =
150
+ T.type_alias { [ImageDescriptor, T::Array[ImageDescriptor], String] }
151
+
152
+ sig { returns(MarshalFormat) }
153
+ def marshal_dump
154
+ [@original, @versions, @blur]
155
+ end
156
+
157
+ sig { params(args: MarshalFormat).void }
158
+ def marshal_load(args)
159
+ @original, @versions, @blur = args
160
+ end
161
+
162
+ sig { returns(String) }
163
+ def inspect
164
+ "#<Image src=#{src.inspect}>"
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "brotli"
5
+
6
+ module Mayu
7
+ module Resources
8
+ module Types
9
+ class JavaScript < Base
10
+ extend T::Sig
11
+
12
+ sig { returns(String) }
13
+ attr_reader :filename
14
+
15
+ sig { params(resource: Resource).void }
16
+ def initialize(resource)
17
+ super
18
+ @filename =
19
+ T.let(
20
+ Base64.urlsafe_encode64(@resource.content_hash).+(".js").freeze,
21
+ String
22
+ )
23
+ @source = T.let(resource.read(encoding: "utf-8").freeze, String)
24
+ end
25
+
26
+ sig { returns(T::Array[Asset]) }
27
+ def assets
28
+ [
29
+ Asset.new(
30
+ filename,
31
+ Generators::WriteFile.new(contents: @source, compress: true)
32
+ )
33
+ ]
34
+ end
35
+
36
+ MarshalFormat = T.type_alias { [String, String] }
37
+
38
+ sig { returns(MarshalFormat) }
39
+ def marshal_dump
40
+ [@filename, @source]
41
+ end
42
+
43
+ sig { params(args: MarshalFormat).void }
44
+ def marshal_load(args)
45
+ @filename, @source = args
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require_relative "base"
5
+
6
+ module Mayu
7
+ module Resources
8
+ module Types
9
+ class Nil < Base
10
+ extend T::Sig
11
+
12
+ sig { returns(NilClass) }
13
+ def marshal_dump
14
+ nil
15
+ end
16
+
17
+ sig { params(args: NilClass).void }
18
+ def marshal_load(args)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end