proscenium-phlex 0.1.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.
@@ -0,0 +1,361 @@
1
+ PATH
2
+ remote: ../../proscenium
3
+ specs:
4
+ proscenium (0.19.0.beta19)
5
+ ffi (~> 1.17.0)
6
+ rails (>= 7.1.0, < 9.0)
7
+
8
+ PATH
9
+ remote: ..
10
+ specs:
11
+ proscenium-phlex (0.1.0)
12
+ phlex-rails (~> 1.2.2)
13
+ prism
14
+ proscenium (~> 0.19.0.beta19)
15
+ rails (>= 7.1.0, < 9.0)
16
+ require-hooks (~> 0.2)
17
+
18
+ GEM
19
+ remote: https://rubygems.org/
20
+ specs:
21
+ actioncable (8.0.2)
22
+ actionpack (= 8.0.2)
23
+ activesupport (= 8.0.2)
24
+ nio4r (~> 2.0)
25
+ websocket-driver (>= 0.6.1)
26
+ zeitwerk (~> 2.6)
27
+ actionmailbox (8.0.2)
28
+ actionpack (= 8.0.2)
29
+ activejob (= 8.0.2)
30
+ activerecord (= 8.0.2)
31
+ activestorage (= 8.0.2)
32
+ activesupport (= 8.0.2)
33
+ mail (>= 2.8.0)
34
+ actionmailer (8.0.2)
35
+ actionpack (= 8.0.2)
36
+ actionview (= 8.0.2)
37
+ activejob (= 8.0.2)
38
+ activesupport (= 8.0.2)
39
+ mail (>= 2.8.0)
40
+ rails-dom-testing (~> 2.2)
41
+ actionpack (8.0.2)
42
+ actionview (= 8.0.2)
43
+ activesupport (= 8.0.2)
44
+ nokogiri (>= 1.8.5)
45
+ rack (>= 2.2.4)
46
+ rack-session (>= 1.0.1)
47
+ rack-test (>= 0.6.3)
48
+ rails-dom-testing (~> 2.2)
49
+ rails-html-sanitizer (~> 1.6)
50
+ useragent (~> 0.16)
51
+ actiontext (8.0.2)
52
+ actionpack (= 8.0.2)
53
+ activerecord (= 8.0.2)
54
+ activestorage (= 8.0.2)
55
+ activesupport (= 8.0.2)
56
+ globalid (>= 0.6.0)
57
+ nokogiri (>= 1.8.5)
58
+ actionview (8.0.2)
59
+ activesupport (= 8.0.2)
60
+ builder (~> 3.1)
61
+ erubi (~> 1.11)
62
+ rails-dom-testing (~> 2.2)
63
+ rails-html-sanitizer (~> 1.6)
64
+ activejob (8.0.2)
65
+ activesupport (= 8.0.2)
66
+ globalid (>= 0.3.6)
67
+ activemodel (8.0.2)
68
+ activesupport (= 8.0.2)
69
+ activerecord (8.0.2)
70
+ activemodel (= 8.0.2)
71
+ activesupport (= 8.0.2)
72
+ timeout (>= 0.4.0)
73
+ activestorage (8.0.2)
74
+ actionpack (= 8.0.2)
75
+ activejob (= 8.0.2)
76
+ activerecord (= 8.0.2)
77
+ activesupport (= 8.0.2)
78
+ marcel (~> 1.0)
79
+ activesupport (8.0.2)
80
+ base64
81
+ benchmark (>= 0.3)
82
+ bigdecimal
83
+ concurrent-ruby (~> 1.0, >= 1.3.1)
84
+ connection_pool (>= 2.2.5)
85
+ drb
86
+ i18n (>= 1.6, < 2)
87
+ logger (>= 1.4.2)
88
+ minitest (>= 5.1)
89
+ securerandom (>= 0.3)
90
+ tzinfo (~> 2.0, >= 2.0.5)
91
+ uri (>= 0.13.1)
92
+ addressable (2.8.7)
93
+ public_suffix (>= 2.0.2, < 7.0)
94
+ amazing_print (1.8.1)
95
+ appraisal (2.5.0)
96
+ bundler
97
+ rake
98
+ thor (>= 0.14.0)
99
+ ast (2.4.3)
100
+ base64 (0.3.0)
101
+ benchmark (0.4.1)
102
+ bigdecimal (3.2.2)
103
+ bindex (0.8.1)
104
+ builder (3.3.0)
105
+ capybara (3.40.0)
106
+ addressable
107
+ matrix
108
+ mini_mime (>= 0.1.3)
109
+ nokogiri (~> 1.11)
110
+ rack (>= 1.6.0)
111
+ rack-test (>= 0.6.3)
112
+ regexp_parser (>= 1.5, < 3.0)
113
+ xpath (~> 3.2)
114
+ concurrent-ruby (1.3.5)
115
+ connection_pool (2.5.3)
116
+ crass (1.0.6)
117
+ date (3.4.1)
118
+ debug (1.11.0)
119
+ irb (~> 1.10)
120
+ reline (>= 0.3.8)
121
+ drb (2.2.3)
122
+ erb (5.0.2)
123
+ erubi (1.13.1)
124
+ ffi (1.17.2-aarch64-linux-gnu)
125
+ ffi (1.17.2-aarch64-linux-musl)
126
+ ffi (1.17.2-arm-linux-gnu)
127
+ ffi (1.17.2-arm-linux-musl)
128
+ ffi (1.17.2-arm64-darwin)
129
+ ffi (1.17.2-x86_64-darwin)
130
+ ffi (1.17.2-x86_64-linux-gnu)
131
+ ffi (1.17.2-x86_64-linux-musl)
132
+ globalid (1.2.1)
133
+ activesupport (>= 6.1)
134
+ i18n (1.14.7)
135
+ concurrent-ruby (~> 1.0)
136
+ io-console (0.8.1)
137
+ irb (1.15.2)
138
+ pp (>= 0.6.0)
139
+ rdoc (>= 4.0.0)
140
+ reline (>= 0.4.2)
141
+ json (2.13.0)
142
+ language_server-protocol (3.17.0.5)
143
+ lint_roller (1.1.0)
144
+ logger (1.7.0)
145
+ loofah (2.24.1)
146
+ crass (~> 1.0.2)
147
+ nokogiri (>= 1.12.0)
148
+ mail (2.8.1)
149
+ mini_mime (>= 0.1.1)
150
+ net-imap
151
+ net-pop
152
+ net-smtp
153
+ marcel (1.0.4)
154
+ matrix (0.4.3)
155
+ maxitest (6.0.0)
156
+ minitest (>= 5.20.0, < 5.26.0)
157
+ mini_mime (1.1.5)
158
+ minitest (5.25.5)
159
+ minitest-focus (1.4.0)
160
+ minitest (>= 4, < 6)
161
+ minitest-spec-rails (7.4.1)
162
+ minitest (>= 5.0)
163
+ railties (>= 4.1)
164
+ net-imap (0.5.9)
165
+ date
166
+ net-protocol
167
+ net-pop (0.1.2)
168
+ net-protocol
169
+ net-protocol (0.2.2)
170
+ timeout
171
+ net-smtp (0.5.1)
172
+ net-protocol
173
+ nio4r (2.7.4)
174
+ nokogiri (1.18.8-aarch64-linux-gnu)
175
+ racc (~> 1.4)
176
+ nokogiri (1.18.8-aarch64-linux-musl)
177
+ racc (~> 1.4)
178
+ nokogiri (1.18.8-arm-linux-gnu)
179
+ racc (~> 1.4)
180
+ nokogiri (1.18.8-arm-linux-musl)
181
+ racc (~> 1.4)
182
+ nokogiri (1.18.8-arm64-darwin)
183
+ racc (~> 1.4)
184
+ nokogiri (1.18.8-x86_64-darwin)
185
+ racc (~> 1.4)
186
+ nokogiri (1.18.8-x86_64-linux-gnu)
187
+ racc (~> 1.4)
188
+ nokogiri (1.18.8-x86_64-linux-musl)
189
+ racc (~> 1.4)
190
+ parallel (1.27.0)
191
+ parser (3.3.8.0)
192
+ ast (~> 2.4.1)
193
+ racc
194
+ phlex (1.11.0)
195
+ phlex-rails (1.2.2)
196
+ phlex (>= 1.10, < 2)
197
+ railties (>= 6.1, < 9)
198
+ pp (0.6.2)
199
+ prettyprint
200
+ prettyprint (0.2.0)
201
+ prism (1.4.0)
202
+ psych (5.2.6)
203
+ date
204
+ stringio
205
+ public_suffix (6.0.2)
206
+ puma (6.6.0)
207
+ nio4r (~> 2.0)
208
+ racc (1.8.1)
209
+ rack (3.1.16)
210
+ rack-session (2.1.1)
211
+ base64 (>= 0.1.0)
212
+ rack (>= 3.0.0)
213
+ rack-test (2.2.0)
214
+ rack (>= 1.3)
215
+ rackup (2.2.1)
216
+ rack (>= 3)
217
+ rails (8.0.2)
218
+ actioncable (= 8.0.2)
219
+ actionmailbox (= 8.0.2)
220
+ actionmailer (= 8.0.2)
221
+ actionpack (= 8.0.2)
222
+ actiontext (= 8.0.2)
223
+ actionview (= 8.0.2)
224
+ activejob (= 8.0.2)
225
+ activemodel (= 8.0.2)
226
+ activerecord (= 8.0.2)
227
+ activestorage (= 8.0.2)
228
+ activesupport (= 8.0.2)
229
+ bundler (>= 1.15.0)
230
+ railties (= 8.0.2)
231
+ rails-dom-testing (2.3.0)
232
+ activesupport (>= 5.0.0)
233
+ minitest
234
+ nokogiri (>= 1.6)
235
+ rails-html-sanitizer (1.6.2)
236
+ loofah (~> 2.21)
237
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
238
+ railties (8.0.2)
239
+ actionpack (= 8.0.2)
240
+ activesupport (= 8.0.2)
241
+ irb (~> 1.13)
242
+ rackup (>= 1.0.0)
243
+ rake (>= 12.2)
244
+ thor (~> 1.0, >= 1.2.2)
245
+ zeitwerk (~> 2.6)
246
+ rainbow (3.1.1)
247
+ rake (13.3.0)
248
+ rdoc (6.14.2)
249
+ erb
250
+ psych (>= 4.0.0)
251
+ regexp_parser (2.10.0)
252
+ reline (0.6.1)
253
+ io-console (~> 0.5)
254
+ require-hooks (0.2.2)
255
+ rubocop (1.78.0)
256
+ json (~> 2.3)
257
+ language_server-protocol (~> 3.17.0.2)
258
+ lint_roller (~> 1.1.0)
259
+ parallel (~> 1.10)
260
+ parser (>= 3.3.0.2)
261
+ rainbow (>= 2.2.2, < 4.0)
262
+ regexp_parser (>= 2.9.3, < 3.0)
263
+ rubocop-ast (>= 1.45.1, < 2.0)
264
+ ruby-progressbar (~> 1.7)
265
+ unicode-display_width (>= 2.4.0, < 4.0)
266
+ rubocop-ast (1.46.0)
267
+ parser (>= 3.3.7.2)
268
+ prism (~> 1.4)
269
+ rubocop-capybara (2.22.1)
270
+ lint_roller (~> 1.1)
271
+ rubocop (~> 1.72, >= 1.72.1)
272
+ rubocop-disable_syntax (0.2.0)
273
+ lint_roller
274
+ rubocop (>= 1.72.0)
275
+ rubocop-minitest (0.38.1)
276
+ lint_roller (~> 1.1)
277
+ rubocop (>= 1.75.0, < 2.0)
278
+ rubocop-ast (>= 1.38.0, < 2.0)
279
+ rubocop-packaging (0.6.0)
280
+ lint_roller (~> 1.1.0)
281
+ rubocop (>= 1.72.1, < 2.0)
282
+ rubocop-performance (1.25.0)
283
+ lint_roller (~> 1.1)
284
+ rubocop (>= 1.75.0, < 2.0)
285
+ rubocop-ast (>= 1.38.0, < 2.0)
286
+ rubocop-rails (2.32.0)
287
+ activesupport (>= 4.2.0)
288
+ lint_roller (~> 1.1)
289
+ rack (>= 1.1)
290
+ rubocop (>= 1.75.0, < 2.0)
291
+ rubocop-ast (>= 1.44.0, < 2.0)
292
+ rubocop-rake (0.7.1)
293
+ lint_roller (~> 1.1)
294
+ rubocop (>= 1.72.1)
295
+ ruby-progressbar (1.13.0)
296
+ securerandom (0.4.1)
297
+ sqlite3 (2.7.3-aarch64-linux-gnu)
298
+ sqlite3 (2.7.3-aarch64-linux-musl)
299
+ sqlite3 (2.7.3-arm-linux-gnu)
300
+ sqlite3 (2.7.3-arm-linux-musl)
301
+ sqlite3 (2.7.3-arm64-darwin)
302
+ sqlite3 (2.7.3-x86_64-darwin)
303
+ sqlite3 (2.7.3-x86_64-linux-gnu)
304
+ sqlite3 (2.7.3-x86_64-linux-musl)
305
+ stringio (3.1.7)
306
+ thor (1.4.0)
307
+ timeout (0.4.3)
308
+ tzinfo (2.0.6)
309
+ concurrent-ruby (~> 1.0)
310
+ unicode-display_width (3.1.4)
311
+ unicode-emoji (~> 4.0, >= 4.0.4)
312
+ unicode-emoji (4.0.4)
313
+ uri (1.0.3)
314
+ useragent (0.16.11)
315
+ web-console (4.2.1)
316
+ actionview (>= 6.0.0)
317
+ activemodel (>= 6.0.0)
318
+ bindex (>= 0.4.0)
319
+ railties (>= 6.0.0)
320
+ websocket-driver (0.8.0)
321
+ base64
322
+ websocket-extensions (>= 0.1.0)
323
+ websocket-extensions (0.1.5)
324
+ xpath (3.2.0)
325
+ nokogiri (~> 1.8)
326
+ zeitwerk (2.7.3)
327
+
328
+ PLATFORMS
329
+ aarch64-linux-gnu
330
+ aarch64-linux-musl
331
+ arm-linux-gnu
332
+ arm-linux-musl
333
+ arm64-darwin
334
+ x86_64-darwin
335
+ x86_64-linux-gnu
336
+ x86_64-linux-musl
337
+
338
+ DEPENDENCIES
339
+ amazing_print
340
+ appraisal
341
+ capybara
342
+ debug
343
+ maxitest
344
+ minitest-focus
345
+ minitest-spec-rails
346
+ proscenium!
347
+ proscenium-phlex!
348
+ puma
349
+ rails (~> 8.0.1)
350
+ rubocop-capybara
351
+ rubocop-disable_syntax
352
+ rubocop-minitest
353
+ rubocop-packaging
354
+ rubocop-performance
355
+ rubocop-rails
356
+ rubocop-rake
357
+ sqlite3
358
+ web-console
359
+
360
+ BUNDLED WITH
361
+ 2.6.8
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::Phlex
4
+ module AbstractClass
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ attr_accessor :abstract_class
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'prism'
4
+ require 'require-hooks/setup'
5
+
6
+ module Proscenium::Phlex
7
+ class CssModuleRewriter
8
+ def self.init(include: [], exclude: [])
9
+ RequireHooks.source_transform(
10
+ patterns: include,
11
+ exclude_patterns: exclude
12
+ ) do |path, source|
13
+ source ||= File.read(path)
14
+ Processor.call(source)
15
+ end
16
+ end
17
+
18
+ class Processor < Prism::Visitor
19
+ def self.call(source)
20
+ visitor = new
21
+ visitor.visit(Prism.parse(source).value)
22
+
23
+ buffer = source.dup
24
+ annotations = visitor.annotations
25
+ annotations.sort_by!(&:first)
26
+
27
+ annotations.reverse_each do |offset, action|
28
+ case action
29
+ when :start
30
+ buffer.insert(offset, 'class_names(*')
31
+ when :end
32
+ buffer.insert(offset, ')')
33
+ else
34
+ raise 'Invalid annotation'
35
+ end
36
+ end
37
+
38
+ buffer
39
+ end
40
+
41
+ def initialize
42
+ @annotations = []
43
+ end
44
+
45
+ PREFIX = '@'
46
+
47
+ attr_reader :annotations
48
+
49
+ def visit_assoc_node(node)
50
+ # Skip if the key is not a symbol or string
51
+ return if %i[symbol_node string_node].exclude?(node.key.type)
52
+
53
+ return if node.key.type == :symbol_node && node.key.value != 'class'
54
+ return if node.key.type == :string_node && node.key.content != 'class'
55
+
56
+ value = node.value
57
+ type = value.type
58
+
59
+ if (type == :symbol_node && value.value.start_with?(PREFIX)) ||
60
+ (type == :array_node && value.elements.any? { |it| it.value.start_with?(PREFIX) })
61
+ build_annotation value
62
+ end
63
+ end
64
+
65
+ def build_annotation(value)
66
+ location = value.location
67
+
68
+ @annotations <<
69
+ [location.start_character_offset, :start] <<
70
+ [location.end_character_offset, :end]
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::Phlex
4
+ module CssModules
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include Proscenium::CssModule
9
+ include Proscenium::SourcePath
10
+ extend Proscenium::CssModule::Path
11
+ end
12
+
13
+ class_methods do
14
+ # Set of CSS module paths that have been resolved after being transformed from 'class' HTML
15
+ # attributes. See #process_attributes. This is here because Phlex caches attributes. Which
16
+ # means while the CSS class names will be transformed, any resolved paths will be lost in
17
+ # subsequent requests.
18
+ attr_accessor :resolved_css_module_paths
19
+ end
20
+
21
+ def before_template
22
+ self.class.resolved_css_module_paths ||= Concurrent::Set.new
23
+ super
24
+ end
25
+
26
+ def after_template
27
+ self.class.resolved_css_module_paths ||= Concurrent::Set.new
28
+ self.class.resolved_css_module_paths.each do |path|
29
+ Proscenium::Importer.import path, sideloaded: true
30
+ end
31
+
32
+ super
33
+ end
34
+
35
+ # Resolve and side load any CSS modules in the "class" attributes, where a CSS module is a class
36
+ # name beginning with a `@`. The class name is resolved to a CSS module name based on the file
37
+ # system path of the Phlex class, and any CSS file is side loaded.
38
+ #
39
+ # For example, the following will side load the CSS module file at
40
+ # app/components/user/component.module.css, and add the CSS Module name `user_name` to the
41
+ # <div>.
42
+ #
43
+ # # app/components/user/component.rb
44
+ # class User::Component < Proscenium::Phlex
45
+ # def view_template
46
+ # div class: :@user_name do
47
+ # 'Joel Moss'
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # Additionally, any class name containing a `/` is resolved as a CSS module path. Allowing you
53
+ # to use the same syntax as a CSS module, but without the need to manually import the CSS file.
54
+ #
55
+ # For example, the following will side load the CSS module file at /lib/users.module.css, and
56
+ # add the CSS Module name `name` to the <div>.
57
+ #
58
+ # class User::Component < Proscenium::Phlex
59
+ # def view_template
60
+ # div class: '/lib/users@name' do
61
+ # 'Joel Moss'
62
+ # end
63
+ # end
64
+ # end
65
+ #
66
+ # @raise [Proscenium::CssModule::Resolver::NotFound] If a CSS module file is not found for the
67
+ # Phlex class file path.
68
+ def process_attributes(**attributes)
69
+ if attributes.key?(:class) && (attributes[:class] = tokens(attributes[:class])).include?('@')
70
+ names = attributes[:class].is_a?(Array) ? attributes[:class] : attributes[:class].split
71
+
72
+ self.class.resolved_css_module_paths ||= Concurrent::Set.new
73
+
74
+ attributes[:class] = cssm.class_names(*names).map do |name, path|
75
+ self.class.resolved_css_module_paths << path if path
76
+ name
77
+ end
78
+ end
79
+
80
+ attributes
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/engine'
4
+
5
+ module Proscenium
6
+ module Phlex
7
+ # @api private
8
+ class Engine < ::Rails::Engine
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::Phlex
4
+ module IncludeAssets
5
+ extend ActiveSupport::Concern
6
+ include AbstractClass
7
+
8
+ def include_stylesheets
9
+ comment { '[PROSCENIUM_STYLESHEETS]' }
10
+ end
11
+
12
+ def include_javascripts
13
+ comment { '[PROSCENIUM_JAVASCRIPTS]' }
14
+ end
15
+
16
+ def include_assets
17
+ include_stylesheets
18
+ include_javascripts
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::Phlex
4
+ # Renders a <div> for use with React components, with data attributes specifying the component
5
+ # path and props.
6
+ #
7
+ # If a block is given, it will be yielded within the div, allowing for a custom "loading" UI. If
8
+ # no block is given, then a "loading..." text will be rendered. It is intended that the component
9
+ # is mounted to this div, and the loading UI will then be replaced with the component's rendered
10
+ # output.
11
+ #
12
+ # You can pass props to the component in the `:props` keyword argument.
13
+ module React
14
+ extend ActiveSupport::Concern
15
+ include Sideload
16
+ include Proscenium::ReactComponentable
17
+
18
+ # Override this to provide your own loading UI.
19
+ #
20
+ # @example
21
+ # def view_template(**attributes, &block)
22
+ # super do
23
+ # 'Look at me! I am loading now...'
24
+ # end
25
+ # end
26
+ #
27
+ # @yield the given block to a `div` within the top level component div.
28
+ def view_template(**attributes, &)
29
+ send(root_tag, **{ data: data_attributes }.deep_merge(attributes), &)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::Phlex
4
+ module Sideload
5
+ extend ActiveSupport::Concern
6
+ include AbstractClass
7
+
8
+ included do
9
+ include Proscenium::SourcePath
10
+ class_attribute :sideload_assets_options
11
+ end
12
+
13
+ class_methods do
14
+ def sideload_assets(value)
15
+ self.sideload_assets_options = value
16
+ end
17
+ end
18
+
19
+ def before_template
20
+ Proscenium::SideLoad.sideload_inheritance_chain self,
21
+ helpers.controller.sideload_assets_options
22
+
23
+ super
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module Phlex
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proscenium'
4
+ require 'proscenium/phlex/engine'
5
+ require 'phlex-rails'
6
+
7
+ module Proscenium
8
+ module Phlex
9
+ class Error < StandardError; end
10
+
11
+ autoload :AbstractClass, 'proscenium/phlex/abstract_class'
12
+ autoload :IncludeAssets, 'proscenium/phlex/include_assets'
13
+ autoload :Sideload, 'proscenium/phlex/sideload'
14
+ autoload :CssModules, 'proscenium/phlex/css_modules'
15
+ autoload :CssModuleRewriter, 'proscenium/phlex/css_module_rewriter'
16
+ autoload :React, 'proscenium/phlex/react'
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'proscenium/phlex'