papercraft 1.2 → 1.4
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +2 -0
- data/lib/papercraft/compiler.rb +205 -370
- data/lib/papercraft/compiler_old.rb +701 -0
- data/lib/papercraft/html.rb +9 -9
- data/lib/papercraft/tags.rb +32 -38
- data/lib/papercraft/template.rb +3 -3
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +5 -5
- metadata +25 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78c8ccb1c86c1ca23bf5db29174c938015c1137fcd4018f0afd09b298b648e94
|
4
|
+
data.tar.gz: 196e06b72fef6f750789b64883110a075aef402e34b8616dcc8114c425d1de17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0599e7b5340e1dc5afe72f76c930ed0587f1bafdc9b7bac03cb75597efdd8e450faf81012cf16666fe3588ea39aa53ffec6ae14aa3f0002e79036144d3681131'
|
7
|
+
data.tar.gz: e8b88f6cd76c8a5a10df7bb84e174e7e0d7ff06aac6c30e1d763982ac7b63261a9203e887e0df647fc5ef55248b8239fa75851e77eeb9ef74bc7ce4be9e8a1d0
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
data/lib/papercraft/compiler.rb
CHANGED
@@ -1,428 +1,263 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@output_mode = false
|
18
|
-
end
|
19
|
-
|
20
|
-
def emit_code_line_break
|
21
|
-
return if @code_buffer.empty?
|
22
|
-
|
23
|
-
@code_buffer << "\n" if @code_buffer[-1] != "\n"
|
24
|
-
@line_break = nil
|
25
|
-
end
|
26
|
-
|
27
|
-
def emit_literal(lit)
|
28
|
-
if @output_mode
|
29
|
-
emit_code_line_break if @line_break
|
30
|
-
@emit_buffer ||= String.new(capacity: DEFAULT_EMIT_BUFFER_CAPACITY)
|
31
|
-
@emit_buffer << lit
|
32
|
-
else
|
33
|
-
emit_code(lit)
|
3
|
+
require 'cgi'
|
4
|
+
require 'escape_utils'
|
5
|
+
require 'sirop'
|
6
|
+
|
7
|
+
class Papercraft::Compiler < Sirop::Sourcifier
|
8
|
+
module AuxMethods
|
9
|
+
def format_html_attr(tag)
|
10
|
+
tag.to_s.tr('_', '-')
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_html_attrs(attrs)
|
14
|
+
attrs.reduce(+'') do |html, (k, v)|
|
15
|
+
html << ' ' if !html.empty?
|
16
|
+
html << "#{format_html_attr(k)}=\"#{v}\""
|
34
17
|
end
|
35
18
|
end
|
36
19
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
when :html
|
46
|
-
__html_encode__(str)
|
47
|
-
when :uri
|
48
|
-
__uri_encode__(str)
|
20
|
+
def render_emit_call(o, *a, **b, &block)
|
21
|
+
case o
|
22
|
+
when nil
|
23
|
+
# do nothing
|
24
|
+
when Papercraft::Template
|
25
|
+
o.render(*a, **b, &block)
|
26
|
+
when ::Proc
|
27
|
+
Papercraft.html(&o).render(*a, **b, &block)
|
49
28
|
else
|
50
|
-
|
29
|
+
o.to_s
|
51
30
|
end
|
52
31
|
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Papercraft.extend(AuxMethods)
|
53
35
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
emit_literal(')}')
|
59
|
-
else
|
60
|
-
yield
|
61
|
-
end
|
62
|
-
end
|
36
|
+
def initialize
|
37
|
+
super
|
38
|
+
@html_buffer = +''
|
39
|
+
end
|
63
40
|
|
64
|
-
|
65
|
-
|
41
|
+
def compile(node)
|
42
|
+
@root_node = node
|
43
|
+
inject_buffer_parameter(node)
|
66
44
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
45
|
+
@buffer.clear
|
46
|
+
@html_buffer.clear
|
47
|
+
visit(node)
|
48
|
+
@buffer
|
49
|
+
end
|
71
50
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@code_buffer << "#{' ' * @level}#{code}"
|
76
|
-
else
|
77
|
-
if @code_buffer.empty? || (@code_buffer[-1] == "\n")
|
78
|
-
@code_buffer << "#{' ' * @level}#{code}"
|
79
|
-
else
|
80
|
-
@code_buffer << "#{code}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
51
|
+
def inject_buffer_parameter(node)
|
52
|
+
node.inject_parameters('__buffer__')
|
53
|
+
end
|
84
54
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
55
|
+
def embed_visit(node, pre = '', post = '')
|
56
|
+
tmp_last_loc_start = @last_loc_start
|
57
|
+
tmp_last_loc_end = @last_loc_end
|
58
|
+
@last_loc_start = loc_start(node.location)
|
59
|
+
@last_loc_end = loc_end(node.location)
|
60
|
+
|
61
|
+
@embed_mode = true
|
62
|
+
tmp_buffer = @buffer
|
63
|
+
@buffer = +''
|
64
|
+
visit(node)
|
65
|
+
@embed_mode = false
|
66
|
+
@html_buffer << "#{pre}#{@buffer}#{post}"
|
67
|
+
@buffer = tmp_buffer
|
68
|
+
|
69
|
+
@last_loc_start = tmp_last_loc_start
|
70
|
+
@last_loc_end = tmp_last_loc_end
|
71
|
+
end
|
93
72
|
|
94
|
-
|
73
|
+
def html_embed_visit(node)
|
74
|
+
embed_visit(node, '#{CGI.escapeHTML((', ').to_s)}')
|
75
|
+
end
|
95
76
|
|
96
|
-
|
97
|
-
|
77
|
+
def tag_attr_embed_visit(node, key)
|
78
|
+
if key
|
79
|
+
embed_visit(node, '#{Papercraft.format_html_attr(', ')}')
|
80
|
+
else
|
81
|
+
embed_visit(node, '#{', '}')
|
98
82
|
end
|
83
|
+
end
|
99
84
|
|
100
|
-
|
101
|
-
|
102
|
-
|
85
|
+
def emit_code(loc, semicolon: false)
|
86
|
+
flush_html_buffer if !@embed_mode
|
87
|
+
super
|
88
|
+
end
|
103
89
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
# puts "- parse(#{node.type}) (break: #{@line_break.inspect})"
|
108
|
-
send(:"parse_#{node.type.downcase}", node)
|
109
|
-
end
|
90
|
+
def emit_html(str)
|
91
|
+
@html_buffer << str
|
92
|
+
end
|
110
93
|
|
111
|
-
|
112
|
-
|
113
|
-
end
|
94
|
+
def flush_html_buffer
|
95
|
+
return if @html_buffer.empty?
|
114
96
|
|
115
|
-
|
116
|
-
|
117
|
-
if call.type == :FCALL
|
118
|
-
parse_fcall(call, scope)
|
119
|
-
else
|
120
|
-
parse(call)
|
121
|
-
emit_code(" do")
|
122
|
-
args = scope.children[0]
|
123
|
-
emit_code(" |#{args.join(', ')}|") if args
|
124
|
-
emit_code("\n")
|
125
|
-
@level += 1
|
126
|
-
parse(scope)
|
127
|
-
flush_emit_buffer
|
128
|
-
@level -= 1
|
129
|
-
emit_code("end\n")
|
130
|
-
end
|
97
|
+
if @last_loc_start
|
98
|
+
adjust_whitespace(@html_location_start) if @html_location_start
|
131
99
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
100
|
+
if @defer_proc_mode
|
101
|
+
@buffer << "__b__ << \"#{@html_buffer}\""
|
102
|
+
elsif @defer_mode
|
103
|
+
@buffer << "__parts__ << \"#{@html_buffer}\""
|
104
|
+
else
|
105
|
+
@buffer << "__buffer__ << \"#{@html_buffer}\""
|
136
106
|
end
|
107
|
+
@html_buffer.clear
|
108
|
+
@last_loc_end = loc_end(@html_location_end) if @html_location_end
|
137
109
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
text = fcall_inner_text_from_args(args)
|
142
|
-
atts = fcall_attributes_from_args(args)
|
143
|
-
if block
|
144
|
-
emit_tag(tag, atts) { parse(block) }
|
145
|
-
elsif text
|
146
|
-
emit_tag(tag, atts) do
|
147
|
-
emit_output do
|
148
|
-
if text.is_a?(String)
|
149
|
-
emit_text(text)
|
150
|
-
else
|
151
|
-
emit_expression { parse(text) }
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
else
|
156
|
-
emit_tag(tag, atts)
|
157
|
-
end
|
158
|
-
end
|
110
|
+
@html_location_start = nil
|
111
|
+
@html_location_end = nil
|
112
|
+
end
|
159
113
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
first = args.first
|
164
|
-
case first.type
|
165
|
-
when :STR
|
166
|
-
first.children.first
|
167
|
-
when :LIT
|
168
|
-
first.children.first.to_s
|
169
|
-
when :HASH
|
170
|
-
nil
|
171
|
-
else
|
172
|
-
first
|
173
|
-
end
|
174
|
-
end
|
114
|
+
def visit_call_node(node)
|
115
|
+
return super if node.receiver || @embed_mode
|
175
116
|
|
176
|
-
|
177
|
-
return nil if !args
|
117
|
+
@html_location_start ||= node.location
|
178
118
|
|
179
|
-
|
180
|
-
|
119
|
+
case node.name
|
120
|
+
when :text
|
121
|
+
emit_html_text(node)
|
122
|
+
when :emit
|
123
|
+
emit_html_emit(node)
|
124
|
+
when :emit_yield
|
125
|
+
raise NotImplementedError, "emit_yield is not yet supported in compiled templates"
|
126
|
+
when :defer
|
127
|
+
emit_html_deferred(node)
|
128
|
+
else
|
129
|
+
emit_html_tag(node)
|
181
130
|
end
|
182
131
|
|
183
|
-
|
184
|
-
|
185
|
-
if atts
|
186
|
-
emit_literal("<#{tag}")
|
187
|
-
emit_tag_attributes(atts)
|
188
|
-
emit_literal(block ? '>' : '/>')
|
189
|
-
else
|
190
|
-
emit_literal(block ? "<#{tag}>" : "<#{tag}/>")
|
191
|
-
end
|
192
|
-
end
|
193
|
-
if block
|
194
|
-
block.call
|
195
|
-
emit_output { emit_literal("</#{tag}>") }
|
196
|
-
end
|
197
|
-
end
|
132
|
+
@html_location_end = node.location
|
133
|
+
end
|
198
134
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
key = list.shift
|
203
|
-
break unless key
|
204
|
-
|
205
|
-
value = list.shift
|
206
|
-
value_type = value.type
|
207
|
-
case value_type
|
208
|
-
when :FALSE, :NIL
|
209
|
-
next
|
210
|
-
end
|
211
|
-
|
212
|
-
emit_literal(' ')
|
213
|
-
emit_tag_attribute_key(key)
|
214
|
-
next if value_type == :TRUE
|
215
|
-
|
216
|
-
emit_literal('=\"')
|
217
|
-
emit_tag_attribute_value(value, key)
|
218
|
-
emit_literal('\"')
|
219
|
-
end
|
220
|
-
end
|
135
|
+
def tag_args(node)
|
136
|
+
args = node.arguments&.arguments
|
137
|
+
return nil if !args
|
221
138
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
when :NIL
|
229
|
-
emit_literal('nil')
|
230
|
-
else
|
231
|
-
emit_expression { parse(key) }
|
232
|
-
end
|
139
|
+
if args[0]&.is_a?(Prism::KeywordHashNode)
|
140
|
+
[nil, args[0]]
|
141
|
+
elsif args[1]&.is_a?(Prism::KeywordHashNode)
|
142
|
+
args
|
143
|
+
else
|
144
|
+
[args && args[0], nil]
|
233
145
|
end
|
146
|
+
end
|
234
147
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
when :LIT
|
241
|
-
emit_text(value.children.first.to_s)
|
242
|
-
else
|
243
|
-
parse(value)
|
244
|
-
end
|
245
|
-
end
|
148
|
+
def emit_tag_open(node, attrs)
|
149
|
+
emit_html("<#{node.name}")
|
150
|
+
emit_tag_attributes(node, attrs) if attrs
|
151
|
+
emit_html(">")
|
152
|
+
end
|
246
153
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
emit_literal('__context__')
|
251
|
-
else
|
252
|
-
parse(receiver)
|
253
|
-
end
|
254
|
-
if method == :[]
|
255
|
-
emit_literal('[')
|
256
|
-
args = args.children.compact
|
257
|
-
while true
|
258
|
-
arg = args.shift
|
259
|
-
break unless arg
|
260
|
-
|
261
|
-
parse(arg)
|
262
|
-
emit_literal(', ') if !args.empty?
|
263
|
-
end
|
264
|
-
emit_literal(']')
|
265
|
-
else
|
266
|
-
emit_literal('.')
|
267
|
-
emit_literal(method.to_s)
|
268
|
-
if args
|
269
|
-
emit_literal('(')
|
270
|
-
args = args.children.compact
|
271
|
-
while true
|
272
|
-
arg = args.shift
|
273
|
-
break unless arg
|
274
|
-
|
275
|
-
parse(arg)
|
276
|
-
emit_literal(', ') if !args.empty?
|
277
|
-
end
|
278
|
-
emit_literal(')')
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|
154
|
+
def emit_tag_close(node)
|
155
|
+
emit_html("</#{node.name}>")
|
156
|
+
end
|
282
157
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
158
|
+
def emit_tag_open_close(node, attrs)
|
159
|
+
emit_html("<#{node.name}")
|
160
|
+
emit_tag_attributes(node, attrs) if attrs
|
161
|
+
emit_html("/>")
|
162
|
+
end
|
287
163
|
|
288
|
-
|
289
|
-
|
290
|
-
|
164
|
+
def emit_tag_inner_text(node)
|
165
|
+
case node
|
166
|
+
when Prism::StringNode, Prism::SymbolNode
|
167
|
+
@html_buffer << CGI.escapeHTML(node.unescaped)
|
168
|
+
else
|
169
|
+
html_embed_visit(node)
|
291
170
|
end
|
171
|
+
end
|
292
172
|
|
293
|
-
|
294
|
-
|
295
|
-
|
173
|
+
def emit_tag_attributes(node, attrs)
|
174
|
+
attrs.elements.each do |e|
|
175
|
+
emit_html(" ")
|
296
176
|
|
297
|
-
|
298
|
-
|
177
|
+
if e.is_a?(Prism::AssocSplatNode)
|
178
|
+
embed_visit(e.value, '#{Papercraft.format_html_attrs(', ')}')
|
179
|
+
else
|
180
|
+
emit_tag_attribute_node(e.key, true)
|
181
|
+
emit_html('=\"')
|
182
|
+
emit_tag_attribute_node(e.value)
|
183
|
+
emit_html('\"')
|
184
|
+
end
|
299
185
|
end
|
186
|
+
end
|
300
187
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
emit_literal(', ') if !items.empty?
|
310
|
-
end
|
311
|
-
emit_literal(']')
|
188
|
+
def emit_tag_attribute_node(node, key = false)
|
189
|
+
case node
|
190
|
+
when Prism::StringNode, Prism::SymbolNode
|
191
|
+
value = node.unescaped
|
192
|
+
value = Papercraft.format_html_attr(value) if key
|
193
|
+
@html_buffer << value
|
194
|
+
else
|
195
|
+
tag_attr_embed_visit(node, key)
|
312
196
|
end
|
197
|
+
end
|
313
198
|
|
314
|
-
|
315
|
-
|
316
|
-
|
199
|
+
def emit_html_tag(node)
|
200
|
+
inner_text, attrs = tag_args(node)
|
201
|
+
block = node.block
|
202
|
+
|
203
|
+
if inner_text
|
204
|
+
emit_tag_open(node, attrs)
|
205
|
+
emit_tag_inner_text(inner_text)
|
206
|
+
emit_tag_close(node)
|
207
|
+
elsif block
|
208
|
+
emit_tag_open(node, attrs)
|
209
|
+
visit(block.body)
|
210
|
+
@html_location_start ||= node.block.closing_loc
|
211
|
+
emit_tag_close(node)
|
212
|
+
else
|
213
|
+
emit_tag_open_close(node, attrs)
|
317
214
|
end
|
215
|
+
end
|
318
216
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
emit_literal(" #{op} ")
|
323
|
-
right.children.compact.each { |c| parse(c) }
|
324
|
-
end
|
217
|
+
def emit_html_text(node)
|
218
|
+
args = node.arguments&.arguments
|
219
|
+
return nil if !args
|
325
220
|
|
326
|
-
|
327
|
-
|
328
|
-
end
|
221
|
+
emit_tag_inner_text(args[0])
|
222
|
+
end
|
329
223
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
emit_if_output(cond, then_branch, else_branch)
|
334
|
-
else
|
335
|
-
emit_if_code(cond, then_branch, else_branch)
|
336
|
-
end
|
337
|
-
end
|
224
|
+
def emit_html_emit(node)
|
225
|
+
args = node.arguments&.arguments
|
226
|
+
return nil if !args
|
338
227
|
|
339
|
-
|
340
|
-
|
341
|
-
if @output_mode
|
342
|
-
emit_unless_output(cond, then_branch, else_branch)
|
343
|
-
else
|
344
|
-
emit_unless_code(cond, then_branch, else_branch)
|
345
|
-
end
|
346
|
-
end
|
228
|
+
embed_visit(node.arguments, '#{Papercraft.render_emit_call(', ')}')
|
229
|
+
end
|
347
230
|
|
348
|
-
|
349
|
-
|
350
|
-
emit_literal(" ? ")
|
351
|
-
parse(then_branch)
|
352
|
-
emit_literal(" : ")
|
353
|
-
if else_branch
|
354
|
-
parse(else_branch)
|
355
|
-
else
|
356
|
-
emit_literal(nil)
|
357
|
-
end
|
358
|
-
end
|
231
|
+
def emit_html_deferred(node)
|
232
|
+
raise NotImplementedError, "#defer in embed mode is not supported in compiled templates" if @embed_mode
|
359
233
|
|
360
|
-
|
361
|
-
|
362
|
-
emit_literal(" ? ")
|
363
|
-
if else_branch
|
364
|
-
parse(else_branch)
|
365
|
-
else
|
366
|
-
emit_literal(nil)
|
367
|
-
end
|
368
|
-
emit_literal(" : ")
|
369
|
-
parse(then_branch)
|
370
|
-
end
|
234
|
+
block = node.block
|
235
|
+
return if not block
|
371
236
|
|
372
|
-
|
373
|
-
emit_code('if ')
|
374
|
-
parse(cond)
|
375
|
-
emit_code("\n")
|
376
|
-
@level += 1
|
377
|
-
parse(then_branch)
|
378
|
-
flush_emit_buffer
|
379
|
-
@level -= 1
|
380
|
-
if else_branch
|
381
|
-
emit_code("else\n")
|
382
|
-
@level += 1
|
383
|
-
parse(else_branch)
|
384
|
-
flush_emit_buffer
|
385
|
-
@level -= 1
|
386
|
-
end
|
387
|
-
emit_code("end\n")
|
388
|
-
end
|
237
|
+
setup_defer_mode if !@defer_mode
|
389
238
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
flush_emit_buffer
|
397
|
-
@level -= 1
|
398
|
-
if else_branch
|
399
|
-
emit_code("else\n")
|
400
|
-
@level += 1
|
401
|
-
parse(else_branch)
|
402
|
-
flush_emit_buffer
|
403
|
-
@level -= 1
|
404
|
-
end
|
405
|
-
emit_code("end\n")
|
406
|
-
end
|
239
|
+
flush_html_buffer
|
240
|
+
@buffer << ';__parts__ << ->(__b__) '
|
241
|
+
@defer_proc_mode = true
|
242
|
+
visit(node.block)
|
243
|
+
@defer_proc_mode = nil
|
244
|
+
end
|
407
245
|
|
408
|
-
|
246
|
+
DEFER_PREFIX_EMPTY = "; __parts__ = []"
|
247
|
+
DEFER_PREFIX_NOT_EMPTY = "; __parts__ = [__buffer__.dup]; __buffer__.clear"
|
248
|
+
DEFER_POSTFIX = ";__parts__.each { |p| p.is_a?(Proc) ? p.(__buffer__) : (__buffer__ << p) }"
|
409
249
|
|
410
|
-
|
250
|
+
def setup_defer_mode
|
251
|
+
@defer_mode = true
|
252
|
+
if @html_buffer && !@html_buffer.empty?
|
253
|
+
@buffer << DEFER_PREFIX_NOT_EMPTY
|
254
|
+
else
|
255
|
+
@buffer << DEFER_PREFIX_EMPTY
|
411
256
|
end
|
412
257
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
puts "#{' ' * level}#{node.type.inspect}"
|
417
|
-
node.children.each { |c| pp_ast(c, level + 1) }
|
418
|
-
when Array
|
419
|
-
puts "#{' ' * level}["
|
420
|
-
node.each { |c| pp_ast(c, level + 1) }
|
421
|
-
puts "#{' ' * level}]"
|
422
|
-
else
|
423
|
-
puts "#{' ' * level}#{node.inspect}"
|
424
|
-
return
|
425
|
-
end
|
258
|
+
@root_node.after_body do
|
259
|
+
flush_html_buffer
|
260
|
+
@buffer << DEFER_POSTFIX
|
426
261
|
end
|
427
262
|
end
|
428
263
|
end
|