rsx-rb 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +34 -0
- data/LICENSE +21 -0
- data/README.md +1022 -0
- data/examples/components/button.rsx +22 -0
- data/examples/components/card.rsx +21 -0
- data/examples/components/sidebar.rsx +29 -0
- data/examples/components/theme.rsx +29 -0
- data/examples/components/user_table.rsx +64 -0
- data/examples/user_profile.rsx +51 -0
- data/examples/views/dashboard.html.rsx +26 -0
- data/exe/rsx +10 -0
- data/lib/rsx/attributes.rb +313 -0
- data/lib/rsx/cache.rb +102 -0
- data/lib/rsx/children.rb +78 -0
- data/lib/rsx/cli.rb +96 -0
- data/lib/rsx/codegen.rb +320 -0
- data/lib/rsx/compile_cache.rb +84 -0
- data/lib/rsx/component.rb +212 -0
- data/lib/rsx/context.rb +67 -0
- data/lib/rsx/errors.rb +27 -0
- data/lib/rsx/escape.rb +59 -0
- data/lib/rsx/helpers.rb +29 -0
- data/lib/rsx/loader.rb +194 -0
- data/lib/rsx/nodes.rb +26 -0
- data/lib/rsx/railtie.rb +81 -0
- data/lib/rsx/safe_string.rb +42 -0
- data/lib/rsx/tasks.rake +26 -0
- data/lib/rsx/template.rb +63 -0
- data/lib/rsx/template_handler.rb +35 -0
- data/lib/rsx/transformer.rb +1034 -0
- data/lib/rsx/version.rb +8 -0
- data/lib/rsx-rb.rb +4 -0
- data/lib/rsx.rb +430 -0
- metadata +86 -0
data/lib/rsx/version.rb
ADDED
data/lib/rsx-rb.rb
ADDED
data/lib/rsx.rb
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
require_relative "rsx/version"
|
|
6
|
+
require_relative "rsx/errors"
|
|
7
|
+
require_relative "rsx/safe_string"
|
|
8
|
+
require_relative "rsx/escape"
|
|
9
|
+
require_relative "rsx/attributes"
|
|
10
|
+
require_relative "rsx/children"
|
|
11
|
+
require_relative "rsx/nodes"
|
|
12
|
+
require_relative "rsx/codegen"
|
|
13
|
+
require_relative "rsx/transformer"
|
|
14
|
+
require_relative "rsx/cache"
|
|
15
|
+
require_relative "rsx/compile_cache"
|
|
16
|
+
require_relative "rsx/component"
|
|
17
|
+
require_relative "rsx/context"
|
|
18
|
+
require_relative "rsx/loader"
|
|
19
|
+
require_relative "rsx/template"
|
|
20
|
+
|
|
21
|
+
# RSX brings JSX authoring to Ruby.
|
|
22
|
+
#
|
|
23
|
+
# component Greeting do |name:|
|
|
24
|
+
# return <p className="greeting">Hello, {name}!</p>
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# Markup is compiled ahead of time into Ruby string building, so rendering is
|
|
28
|
+
# just concatenation and escaping. See README.md for the full language guide.
|
|
29
|
+
module RSX
|
|
30
|
+
EMPTY_SAFE = SafeString.new("").freeze
|
|
31
|
+
|
|
32
|
+
# Slots for markup that never changes, populated on first render by compiled
|
|
33
|
+
# templates. Keys are generated at compile time, one per call site.
|
|
34
|
+
STATICS = {}
|
|
35
|
+
|
|
36
|
+
# Configuration is intentionally small: where to find components, where to put
|
|
37
|
+
# compiled output, and which cache to use.
|
|
38
|
+
class Configuration
|
|
39
|
+
# Directories searched for .rsx files and imports.
|
|
40
|
+
attr_accessor :paths
|
|
41
|
+
|
|
42
|
+
# Where compiled Ruby is cached. Set to nil to compile in memory only.
|
|
43
|
+
attr_reader :cache_dir
|
|
44
|
+
|
|
45
|
+
# Any object responding to fetch(key, expires_in:) { } and clear.
|
|
46
|
+
attr_writer :cache_store
|
|
47
|
+
|
|
48
|
+
# Namespace that `component Name` constants are defined under.
|
|
49
|
+
attr_accessor :component_namespace
|
|
50
|
+
|
|
51
|
+
# Reload changed .rsx files automatically (development).
|
|
52
|
+
attr_accessor :reload
|
|
53
|
+
|
|
54
|
+
def initialize
|
|
55
|
+
@paths = []
|
|
56
|
+
@component_namespace = Object
|
|
57
|
+
@reload = false
|
|
58
|
+
self.cache_dir = default_cache_dir
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def cache_dir=(directory)
|
|
62
|
+
@cache_dir = directory
|
|
63
|
+
@compile_cache = CompileCache.new(directory)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def compile_cache
|
|
67
|
+
@compile_cache ||= CompileCache.new(@cache_dir)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def cache_store
|
|
71
|
+
@cache_store ||= Cache::Memory.new
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def default_cache_dir
|
|
77
|
+
if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
|
|
78
|
+
::Rails.root.join("tmp/cache/rsx").to_s
|
|
79
|
+
else
|
|
80
|
+
File.join(Dir.pwd, "tmp", "cache", "rsx")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class << self
|
|
86
|
+
# ------------------------------------------------------------------
|
|
87
|
+
# Configuration
|
|
88
|
+
# ------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
def config
|
|
91
|
+
@config ||= Configuration.new
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def configure
|
|
95
|
+
yield config
|
|
96
|
+
config
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def loader
|
|
100
|
+
@loader ||= Loader.new(config)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def cache
|
|
104
|
+
config.cache_store
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def reset!
|
|
108
|
+
@config = nil
|
|
109
|
+
@loader = nil
|
|
110
|
+
@templates = nil
|
|
111
|
+
STATICS.clear
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# ------------------------------------------------------------------
|
|
115
|
+
# Compiling and loading
|
|
116
|
+
# ------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
# Transforms .rsx source into plain Ruby. Useful for debugging: `rsx compile`.
|
|
119
|
+
def compile(source, path: nil)
|
|
120
|
+
Transformer.transform(source, path: path)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def load(path)
|
|
124
|
+
loader.load(path)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def load_all(paths = config.paths)
|
|
128
|
+
loader.load_all(paths)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def reload!
|
|
132
|
+
loader.reload!
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Compiles every .rsx file under the configured paths and warms the on-disk
|
|
136
|
+
# cache. Run at boot (or from `rake rsx:precompile`) so requests never
|
|
137
|
+
# compile anything.
|
|
138
|
+
def precompile!(paths = config.paths)
|
|
139
|
+
loader.files(paths).each do |file|
|
|
140
|
+
source = File.read(file)
|
|
141
|
+
config.compile_cache.fetch(file, source) { Transformer.transform(source, path: file) }
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The compiled Template for a markup .rsx file.
|
|
146
|
+
def template(path)
|
|
147
|
+
entry = loader.load(loader.resolve!(path))
|
|
148
|
+
entry.template || raise(Error, "#{entry.path} defines components; render one of them instead")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# ------------------------------------------------------------------
|
|
152
|
+
# Rendering
|
|
153
|
+
# ------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
# Renders a component, a .rsx file path, or a lambda component.
|
|
156
|
+
#
|
|
157
|
+
# RSX.render(UserProfile, user: user)
|
|
158
|
+
# RSX.render("app/components/user_profile.rsx", user: user)
|
|
159
|
+
def render(target, context: nil, **props)
|
|
160
|
+
if target.is_a?(String) || target.respond_to?(:to_path)
|
|
161
|
+
render_file(target.to_s, context: context, **props)
|
|
162
|
+
else
|
|
163
|
+
safe(render_component(target, props.empty? ? nil : props, nil, context))
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def render_file(path, context: nil, **props)
|
|
168
|
+
entry = loader.load(loader.resolve!(path))
|
|
169
|
+
props = props.empty? ? nil : props
|
|
170
|
+
component = entry.renderable
|
|
171
|
+
|
|
172
|
+
if component
|
|
173
|
+
safe(render_component(component, props, nil, context))
|
|
174
|
+
else
|
|
175
|
+
entry.template.render(props, context: context)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Renders .rsx source directly. Handy in tests and scripts.
|
|
180
|
+
def render_source(source, path: nil, context: nil, **props)
|
|
181
|
+
Template.new(source, path: path).render(props.empty? ? nil : props, context: context)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Internal: wraps the value of a compiled ActionView template.
|
|
185
|
+
def template_result(value)
|
|
186
|
+
safe(child(value))
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Internal: emitted by compiled markup for every <Component /> tag.
|
|
190
|
+
def render_component(target, props = nil, children = nil, parent = nil)
|
|
191
|
+
if children
|
|
192
|
+
props = props ? props : {}
|
|
193
|
+
props[:children] = children
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
case target
|
|
197
|
+
when Proc
|
|
198
|
+
child(target.arity.zero? ? target.call : target.call(props || Component::EMPTY_PROPS))
|
|
199
|
+
when String, Symbol
|
|
200
|
+
render_component(lookup_component(target), props, nil, parent)
|
|
201
|
+
else
|
|
202
|
+
unless target.respond_to?(:rsx_call)
|
|
203
|
+
raise UnknownComponentError,
|
|
204
|
+
"#{target.inspect} is not renderable. Components are defined with " \
|
|
205
|
+
"`component Name do ... end` in a .rsx file."
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
target.rsx_call(props, parent)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def lookup_component(name)
|
|
213
|
+
name = name.to_s
|
|
214
|
+
namespace = config.component_namespace
|
|
215
|
+
return namespace.const_get(name) if constant_defined?(name)
|
|
216
|
+
|
|
217
|
+
resolved = loader.resolve(name)
|
|
218
|
+
return loader.default_export(resolved) if resolved
|
|
219
|
+
|
|
220
|
+
raise UnknownComponentError, "no component named #{name}"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# ------------------------------------------------------------------
|
|
224
|
+
# Runtime emitted by compiled markup
|
|
225
|
+
# ------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
# Coerces any value into markup, escaping it unless it is already safe.
|
|
228
|
+
# Mirrors React: nil, true and false render nothing, arrays are concatenated.
|
|
229
|
+
def child(value)
|
|
230
|
+
case value
|
|
231
|
+
when SafeString then value
|
|
232
|
+
when String then Escape.safe?(value) ? value : Escape.html(value)
|
|
233
|
+
when nil, true, false then EMPTY_SAFE
|
|
234
|
+
when Children then value.render
|
|
235
|
+
when Integer, Float, Symbol then value.to_s
|
|
236
|
+
when Array
|
|
237
|
+
out = +""
|
|
238
|
+
value.each { |item| out << child(item) }
|
|
239
|
+
out
|
|
240
|
+
when Component then value.rsx_perform
|
|
241
|
+
when Hash
|
|
242
|
+
raise Error, "cannot render a Hash. Did you mean to interpolate one of its values?"
|
|
243
|
+
else
|
|
244
|
+
if value.respond_to?(:html_safe?) && value.html_safe?
|
|
245
|
+
value.to_s
|
|
246
|
+
elsif value.respond_to?(:to_rsx)
|
|
247
|
+
child(value.to_rsx)
|
|
248
|
+
elsif value.is_a?(Class) && value < Component
|
|
249
|
+
raise Error, "cannot render the component class #{value.name} as a value. " \
|
|
250
|
+
"Write <#{value.name} /> to render it."
|
|
251
|
+
else
|
|
252
|
+
Escape.html(value.to_s)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Wraps markup that the compiler proved static. Called once per call site.
|
|
258
|
+
def static(literal)
|
|
259
|
+
SafeString.new(literal).freeze
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Wraps an already-escaped or trusted string without copying when possible.
|
|
263
|
+
def safe(value)
|
|
264
|
+
value.is_a?(SafeString) ? value : SafeString.new(value.to_s)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Marks a string as trusted HTML. The equivalent of Rails' `html_safe`.
|
|
268
|
+
def raw(value)
|
|
269
|
+
return EMPTY_SAFE if value.nil?
|
|
270
|
+
|
|
271
|
+
value.is_a?(SafeString) ? value : SafeString.new(value.to_s)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def escape(value)
|
|
275
|
+
SafeString.new(Escape.html(value.to_s))
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# dangerouslySetInnerHTML={{ __html: markup }}
|
|
279
|
+
def raw_html(value)
|
|
280
|
+
return EMPTY_SAFE if value.nil?
|
|
281
|
+
|
|
282
|
+
html = value.respond_to?(:[]) && !value.is_a?(String) ? (value[:__html] || value["__html"]) : value
|
|
283
|
+
html.nil? ? EMPTY_SAFE : html.to_s
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def json(object)
|
|
287
|
+
require "json"
|
|
288
|
+
JSON.generate(object)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# ------------------------------------------------------------------
|
|
292
|
+
# Defining components
|
|
293
|
+
# ------------------------------------------------------------------
|
|
294
|
+
|
|
295
|
+
# Emitted by `component Name do |props| ... end`.
|
|
296
|
+
def define_component(name, cache: nil, static: false, &body)
|
|
297
|
+
component = Class.new(Component)
|
|
298
|
+
assign_constant(name, component)
|
|
299
|
+
component.cache(cache) if cache
|
|
300
|
+
component.rsx_static! if static
|
|
301
|
+
loader.track(component)
|
|
302
|
+
component.class_eval(&body)
|
|
303
|
+
component
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def create_context(default = nil, name: nil)
|
|
307
|
+
Context.new(default, name: name)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Emitted by `import Name from "path"`.
|
|
311
|
+
def import(spec, as: nil, from: nil)
|
|
312
|
+
from = nil if from.to_s.empty?
|
|
313
|
+
loader.import(spec, as: as, from: from)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Emitted by `export default Name`.
|
|
317
|
+
def export_default(component, from: nil)
|
|
318
|
+
entry = loader.current_entry || (from && loader.entries.find { |candidate| candidate.path == from })
|
|
319
|
+
entry.default = component if entry
|
|
320
|
+
component
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Emitted by `export Name`.
|
|
324
|
+
def export(component, from: nil)
|
|
325
|
+
entry = loader.current_entry || (from && loader.entries.find { |candidate| candidate.path == from })
|
|
326
|
+
if entry && !entry.components.include?(component)
|
|
327
|
+
entry.components << component
|
|
328
|
+
end
|
|
329
|
+
component
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# ------------------------------------------------------------------
|
|
333
|
+
# Constants
|
|
334
|
+
# ------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
def assign_constant(path, value)
|
|
337
|
+
parts = path.to_s.split("::")
|
|
338
|
+
target = config.component_namespace
|
|
339
|
+
|
|
340
|
+
parts[0..-2].each do |part|
|
|
341
|
+
target = if target.const_defined?(part, false)
|
|
342
|
+
target.const_get(part, false)
|
|
343
|
+
else
|
|
344
|
+
target.const_set(part, Module.new)
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
last = parts.last
|
|
349
|
+
target.send(:remove_const, last) if target.const_defined?(last, false)
|
|
350
|
+
target.const_set(last, value)
|
|
351
|
+
value
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def constant_defined?(path)
|
|
355
|
+
parts = path.to_s.split("::")
|
|
356
|
+
return false unless parts.all? { |part| part.match?(/\A[A-Z][A-Za-z0-9_]*\z/) }
|
|
357
|
+
|
|
358
|
+
target = config.component_namespace
|
|
359
|
+
parts.each do |part|
|
|
360
|
+
return false unless target.is_a?(Module) && target.const_defined?(part, false)
|
|
361
|
+
|
|
362
|
+
target = target.const_get(part, false)
|
|
363
|
+
end
|
|
364
|
+
true
|
|
365
|
+
end
|
|
366
|
+
alias const_defined_at? constant_defined?
|
|
367
|
+
|
|
368
|
+
def remove_constant(component)
|
|
369
|
+
name = component.name
|
|
370
|
+
return if name.nil?
|
|
371
|
+
|
|
372
|
+
parts = name.split("::")
|
|
373
|
+
target = Object
|
|
374
|
+
parts[0..-2].each do |part|
|
|
375
|
+
return unless target.const_defined?(part, false)
|
|
376
|
+
|
|
377
|
+
target = target.const_get(part, false)
|
|
378
|
+
end
|
|
379
|
+
last = parts.last
|
|
380
|
+
return unless target.const_defined?(last, false)
|
|
381
|
+
return unless target.const_get(last, false).equal?(component)
|
|
382
|
+
|
|
383
|
+
target.send(:remove_const, last)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# ------------------------------------------------------------------
|
|
387
|
+
# Cache helpers
|
|
388
|
+
# ------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
def normalize_cache_options(options)
|
|
391
|
+
case options
|
|
392
|
+
when true then {}
|
|
393
|
+
when false, nil then nil
|
|
394
|
+
when Hash then options
|
|
395
|
+
when Numeric then { expires_in: options }
|
|
396
|
+
when Proc then { key: options }
|
|
397
|
+
else raise ArgumentError, "unsupported cache option #{options.inspect}"
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Builds a short, stable cache key fragment from arbitrary Ruby values.
|
|
402
|
+
def stable_key(object)
|
|
403
|
+
fragment = key_fragment(object)
|
|
404
|
+
fragment.length > 128 ? Digest::SHA256.hexdigest(fragment)[0, 32] : fragment
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def key_fragment(object)
|
|
408
|
+
case object
|
|
409
|
+
when nil then "~"
|
|
410
|
+
when String then object
|
|
411
|
+
when Symbol, Numeric, true, false then object.to_s
|
|
412
|
+
when Array then object.map { |item| key_fragment(item) }.join(",")
|
|
413
|
+
when Hash then object.map { |key, value| "#{key}=#{key_fragment(value)}" }.join("&")
|
|
414
|
+
when Time then object.to_f.to_s
|
|
415
|
+
else
|
|
416
|
+
if object.respond_to?(:cache_key_with_version)
|
|
417
|
+
object.cache_key_with_version.to_s
|
|
418
|
+
elsif object.respond_to?(:cache_key)
|
|
419
|
+
object.cache_key.to_s
|
|
420
|
+
elsif object.respond_to?(:id) && object.respond_to?(:updated_at)
|
|
421
|
+
"#{object.class}/#{object.id}-#{object.updated_at.to_i}"
|
|
422
|
+
else
|
|
423
|
+
object.to_s
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
require_relative "rsx/railtie" if defined?(::Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rsx-rb
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- RSX contributors
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |
|
|
14
|
+
RSX is a template language that brings React's JSX authoring model to Ruby.
|
|
15
|
+
Write .rsx files where Ruby replaces JavaScript and markup is embedded directly
|
|
16
|
+
in expression position, including fragments (<>), expression containers ({}),
|
|
17
|
+
spread attributes and composable components. Templates compile ahead of time to
|
|
18
|
+
plain Ruby string building, so rendering is fast and cacheable. Zero runtime
|
|
19
|
+
dependencies; Rails integration is optional and loads automatically when present.
|
|
20
|
+
email:
|
|
21
|
+
executables:
|
|
22
|
+
- rsx
|
|
23
|
+
extensions: []
|
|
24
|
+
extra_rdoc_files: []
|
|
25
|
+
files:
|
|
26
|
+
- CHANGELOG.md
|
|
27
|
+
- LICENSE
|
|
28
|
+
- README.md
|
|
29
|
+
- examples/components/button.rsx
|
|
30
|
+
- examples/components/card.rsx
|
|
31
|
+
- examples/components/sidebar.rsx
|
|
32
|
+
- examples/components/theme.rsx
|
|
33
|
+
- examples/components/user_table.rsx
|
|
34
|
+
- examples/user_profile.rsx
|
|
35
|
+
- examples/views/dashboard.html.rsx
|
|
36
|
+
- exe/rsx
|
|
37
|
+
- lib/rsx-rb.rb
|
|
38
|
+
- lib/rsx.rb
|
|
39
|
+
- lib/rsx/attributes.rb
|
|
40
|
+
- lib/rsx/cache.rb
|
|
41
|
+
- lib/rsx/children.rb
|
|
42
|
+
- lib/rsx/cli.rb
|
|
43
|
+
- lib/rsx/codegen.rb
|
|
44
|
+
- lib/rsx/compile_cache.rb
|
|
45
|
+
- lib/rsx/component.rb
|
|
46
|
+
- lib/rsx/context.rb
|
|
47
|
+
- lib/rsx/errors.rb
|
|
48
|
+
- lib/rsx/escape.rb
|
|
49
|
+
- lib/rsx/helpers.rb
|
|
50
|
+
- lib/rsx/loader.rb
|
|
51
|
+
- lib/rsx/nodes.rb
|
|
52
|
+
- lib/rsx/railtie.rb
|
|
53
|
+
- lib/rsx/safe_string.rb
|
|
54
|
+
- lib/rsx/tasks.rake
|
|
55
|
+
- lib/rsx/template.rb
|
|
56
|
+
- lib/rsx/template_handler.rb
|
|
57
|
+
- lib/rsx/transformer.rb
|
|
58
|
+
- lib/rsx/version.rb
|
|
59
|
+
homepage: https://github.com/rsx-rb/rsx
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata:
|
|
63
|
+
homepage_uri: https://github.com/rsx-rb/rsx
|
|
64
|
+
source_code_uri: https://github.com/rsx-rb/rsx
|
|
65
|
+
changelog_uri: https://github.com/rsx-rb/rsx/blob/main/CHANGELOG.md
|
|
66
|
+
rubygems_mfa_required: 'true'
|
|
67
|
+
post_install_message:
|
|
68
|
+
rdoc_options: []
|
|
69
|
+
require_paths:
|
|
70
|
+
- lib
|
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 3.0.0
|
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '0'
|
|
81
|
+
requirements: []
|
|
82
|
+
rubygems_version: 3.5.3
|
|
83
|
+
signing_key:
|
|
84
|
+
specification_version: 4
|
|
85
|
+
summary: JSX-style templates for Ruby and Rails.
|
|
86
|
+
test_files: []
|