papercraft 1.4 → 2.13
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 +89 -0
- data/README.md +246 -617
- data/lib/papercraft/compiler/nodes.rb +223 -0
- data/lib/papercraft/compiler/tag_translator.rb +93 -0
- data/lib/papercraft/compiler.rb +657 -201
- data/lib/papercraft/proc_ext.rb +118 -0
- data/lib/papercraft/template.rb +16 -195
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +111 -87
- metadata +11 -60
- data/lib/papercraft/compiler_old.rb +0 -701
- data/lib/papercraft/extension_proxy.rb +0 -41
- data/lib/papercraft/extensions/soap.rb +0 -42
- data/lib/papercraft/html.rb +0 -173
- data/lib/papercraft/json.rb +0 -128
- data/lib/papercraft/renderer.rb +0 -190
- data/lib/papercraft/tags.rb +0 -408
- data/lib/papercraft/xml.rb +0 -47
- data/lib/tilt/papercraft.rb +0 -25
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Prism::InspectVisitor
|
4
|
+
def visit_tag_node(node)
|
5
|
+
commands << [inspect_node("TagNode", node), indent]
|
6
|
+
# flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact
|
7
|
+
# commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent]
|
8
|
+
# commands << ["├── left:\n", indent]
|
9
|
+
# commands << [node.left, "#{indent}│ "]
|
10
|
+
# commands << ["├── right:\n", indent]
|
11
|
+
# commands << [node.right, "#{indent}│ "]
|
12
|
+
# commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Papercraft
|
17
|
+
# Represents a tag call
|
18
|
+
class TagNode < Prism::Node
|
19
|
+
attr_reader :call_node, :location, :tag, :tag_location, :inner_text, :attributes, :block
|
20
|
+
|
21
|
+
def initialize(call_node, translator)
|
22
|
+
@call_node = call_node
|
23
|
+
@location = call_node.location
|
24
|
+
@tag = call_node.name
|
25
|
+
prepare_block(translator)
|
26
|
+
|
27
|
+
args = call_node.arguments&.arguments
|
28
|
+
return if !args
|
29
|
+
|
30
|
+
if @tag == :tag
|
31
|
+
@tag = args[0]
|
32
|
+
args = args[1..]
|
33
|
+
end
|
34
|
+
|
35
|
+
if args.size == 1 && args.first.is_a?(Prism::KeywordHashNode)
|
36
|
+
@inner_text = nil
|
37
|
+
@attributes = args.first
|
38
|
+
else
|
39
|
+
@inner_text = args.first
|
40
|
+
@attributes = args[1].is_a?(Prism::KeywordHashNode) ? args[1] : nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def accept(visitor)
|
45
|
+
visitor.visit_tag_node(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def prepare_block(translator)
|
49
|
+
@block = call_node.block
|
50
|
+
if @block.is_a?(Prism::BlockNode)
|
51
|
+
@block = translator.visit(@block)
|
52
|
+
offset = @location.start_offset
|
53
|
+
length = @block.opening_loc.start_offset - offset
|
54
|
+
@tag_location = @location.copy(start_offset: offset, length: length)
|
55
|
+
else
|
56
|
+
@tag_location = @location
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Represents a render call
|
62
|
+
class RenderNode
|
63
|
+
attr_reader :call_node, :location, :block
|
64
|
+
|
65
|
+
include Prism::DSL
|
66
|
+
|
67
|
+
def initialize(call_node, translator)
|
68
|
+
@call_node = call_node
|
69
|
+
@location = call_node.location
|
70
|
+
@translator = translator
|
71
|
+
@block = call_node.block && translator.visit(call_node.block)
|
72
|
+
|
73
|
+
lambda = call_node.arguments && call_node.arguments.arguments[0]
|
74
|
+
end
|
75
|
+
|
76
|
+
def ad_hoc_string_location(str)
|
77
|
+
src = source(str)
|
78
|
+
Prism::DSL.location(source: src, start_offset: 0, length: str.bytesize)
|
79
|
+
end
|
80
|
+
|
81
|
+
def transform(node)
|
82
|
+
node && @translator.visit(node)
|
83
|
+
end
|
84
|
+
|
85
|
+
def transform_array(array)
|
86
|
+
array ? array.map { @translator.visit(it) } : []
|
87
|
+
end
|
88
|
+
|
89
|
+
def accept(visitor)
|
90
|
+
visitor.visit_render_node(self)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ConstTagNode
|
95
|
+
attr_reader :call_node, :location
|
96
|
+
|
97
|
+
def initialize(call_node, translator)
|
98
|
+
@call_node = call_node
|
99
|
+
@location = call_node.location
|
100
|
+
end
|
101
|
+
|
102
|
+
def accept(visitor)
|
103
|
+
visitor.visit_const_tag_node(self)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Represents a text call
|
108
|
+
class TextNode
|
109
|
+
attr_reader :call_node, :location
|
110
|
+
|
111
|
+
def initialize(call_node, _translator)
|
112
|
+
@call_node = call_node
|
113
|
+
@location = call_node.location
|
114
|
+
end
|
115
|
+
|
116
|
+
def accept(visitor)
|
117
|
+
visitor.visit_text_node(self)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Represents a raw call
|
122
|
+
class RawNode
|
123
|
+
attr_reader :call_node, :location
|
124
|
+
|
125
|
+
def initialize(call_node, _translator)
|
126
|
+
@call_node = call_node
|
127
|
+
@location = call_node.location
|
128
|
+
end
|
129
|
+
|
130
|
+
def accept(visitor)
|
131
|
+
visitor.visit_raw_node(self)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Represents a defer call
|
136
|
+
class DeferNode
|
137
|
+
attr_reader :call_node, :location, :block
|
138
|
+
|
139
|
+
def initialize(call_node, translator)
|
140
|
+
@call_node = call_node
|
141
|
+
@location = call_node.location
|
142
|
+
@block = call_node.block && translator.visit(call_node.block)
|
143
|
+
end
|
144
|
+
|
145
|
+
def accept(visitor)
|
146
|
+
visitor.visit_defer_node(self)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Represents a builtin call
|
151
|
+
class BuiltinNode
|
152
|
+
attr_reader :tag, :call_node, :location, :block
|
153
|
+
|
154
|
+
def initialize(call_node, translator)
|
155
|
+
@call_node = call_node
|
156
|
+
@tag = call_node.name
|
157
|
+
@location = call_node.location
|
158
|
+
@block = call_node.block && translator.visit(call_node.block)
|
159
|
+
end
|
160
|
+
|
161
|
+
def accept(visitor)
|
162
|
+
visitor.visit_builtin_node(self)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class ExtensionTagNode
|
167
|
+
attr_reader :tag, :call_node, :location, :block
|
168
|
+
|
169
|
+
def initialize(call_node, translator)
|
170
|
+
@call_node = call_node
|
171
|
+
@tag = call_node.name
|
172
|
+
@location = call_node.location
|
173
|
+
@block = call_node.block && translator.visit(call_node.block)
|
174
|
+
end
|
175
|
+
|
176
|
+
def accept(visitor)
|
177
|
+
visitor.visit_extension_tag_node(self)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class BlockInvocationNode
|
182
|
+
attr_reader :call_node, :location, :block
|
183
|
+
|
184
|
+
def initialize(call_node, translator)
|
185
|
+
@call_node = call_node
|
186
|
+
@tag = call_node.name
|
187
|
+
@location = call_node.location
|
188
|
+
@block = call_node.block && translator.visit(call_node.block)
|
189
|
+
end
|
190
|
+
|
191
|
+
def accept(visitor)
|
192
|
+
visitor.visit_block_invocation_node(self)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class RenderYieldNode
|
198
|
+
attr_reader :call_node, :location
|
199
|
+
|
200
|
+
def initialize(call_node, translator)
|
201
|
+
@call_node = call_node
|
202
|
+
@tag = call_node.name
|
203
|
+
@location = call_node.location
|
204
|
+
end
|
205
|
+
|
206
|
+
def accept(visitor)
|
207
|
+
visitor.visit_render_yield_node(self)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class RenderChildrenNode
|
212
|
+
attr_reader :call_node, :location
|
213
|
+
|
214
|
+
def initialize(call_node, translator)
|
215
|
+
@call_node = call_node
|
216
|
+
@tag = call_node.name
|
217
|
+
@location = call_node.location
|
218
|
+
end
|
219
|
+
|
220
|
+
def accept(visitor)
|
221
|
+
visitor.visit_render_children_node(self)
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prism'
|
4
|
+
require_relative './nodes'
|
5
|
+
|
6
|
+
module Papercraft
|
7
|
+
# Translates a normal proc AST into an AST containing custom nodes used for
|
8
|
+
# generating HTML. This translation is the first step in compiling templates
|
9
|
+
# into procs that generate HTML.
|
10
|
+
class TagTranslator < Prism::MutationCompiler
|
11
|
+
include Prism::DSL
|
12
|
+
|
13
|
+
def initialize(root)
|
14
|
+
@root = root
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.transform(ast, root)
|
19
|
+
ast.accept(new(root))
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit_call_node(node, dont_translate: false)
|
23
|
+
return super(node) if dont_translate
|
24
|
+
|
25
|
+
match_builtin(node) ||
|
26
|
+
match_extension(node) ||
|
27
|
+
match_const_tag(node) ||
|
28
|
+
match_block_call(node) ||
|
29
|
+
match_tag(node) ||
|
30
|
+
super(node)
|
31
|
+
end
|
32
|
+
|
33
|
+
def match_builtin(node)
|
34
|
+
return if node.receiver
|
35
|
+
|
36
|
+
case node.name
|
37
|
+
when :render_yield
|
38
|
+
RenderYieldNode.new(node, self)
|
39
|
+
when :render_children
|
40
|
+
RenderChildrenNode.new(node, self)
|
41
|
+
when :raise
|
42
|
+
visit_call_node(node, dont_translate: true)
|
43
|
+
when :render
|
44
|
+
RenderNode.new(node, self)
|
45
|
+
when :raw
|
46
|
+
RawNode.new(node, self)
|
47
|
+
when :text
|
48
|
+
TextNode.new(node, self)
|
49
|
+
when :defer
|
50
|
+
DeferNode.new(node, self)
|
51
|
+
when :html5, :markdown
|
52
|
+
BuiltinNode.new(node, self)
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def match_extension(node)
|
59
|
+
return if node.receiver
|
60
|
+
return if !Papercraft::Extensions[node.name]
|
61
|
+
|
62
|
+
ExtensionTagNode.new(node, self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def match_const_tag(node)
|
66
|
+
return if node.receiver
|
67
|
+
return if node.name !~ /^[A-Z]/
|
68
|
+
|
69
|
+
ConstTagNode.new(node, self)
|
70
|
+
end
|
71
|
+
|
72
|
+
def match_block_call(node)
|
73
|
+
return if !node.receiver
|
74
|
+
return if node.name != :call
|
75
|
+
|
76
|
+
receiver = node.receiver
|
77
|
+
return if !receiver.is_a?(Prism::LocalVariableReadNode)
|
78
|
+
return if @root.parameters&.parameters.block&.name != receiver.name
|
79
|
+
|
80
|
+
if node.block
|
81
|
+
raise Papercraft::Error, 'No support for proc invocation with block'
|
82
|
+
end
|
83
|
+
|
84
|
+
BlockInvocationNode.new(node, self)
|
85
|
+
end
|
86
|
+
|
87
|
+
def match_tag(node)
|
88
|
+
return if node.receiver
|
89
|
+
|
90
|
+
TagNode.new(node, self)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|