@rhinostone/swig 2.0.0-alpha.7 → 2.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.
- package/.changes/v1.0.0-pre1.md +2 -2
- package/.changes/v1.0.0.md +1 -1
- package/.changes/v2.0.0-alpha.8.md +4 -0
- package/.changes/v2.0.0.md +6 -0
- package/.playwright-mcp/console-2026-04-22T15-34-20-480Z.log +2 -0
- package/.playwright-mcp/console-2026-04-22T15-35-04-265Z.log +11 -0
- package/.playwright-mcp/console-2026-04-22T15-37-26-953Z.log +1 -0
- package/.playwright-mcp/console-2026-04-22T15-51-15-160Z.log +148 -0
- package/.playwright-mcp/console-2026-04-22T15-51-33-405Z.log +74 -0
- package/.playwright-mcp/console-2026-04-22T15-51-53-922Z.log +74 -0
- package/.playwright-mcp/console-2026-04-22T15-53-10-736Z.log +74 -0
- package/.playwright-mcp/console-2026-04-22T15-53-40-091Z.log +883 -0
- package/.playwright-mcp/console-2026-04-22T16-12-02-541Z.log +74 -0
- package/.playwright-mcp/console-2026-04-22T16-33-44-982Z.log +13973 -0
- package/.playwright-mcp/page-2026-04-22T15-34-24-524Z.yml +1 -0
- package/.playwright-mcp/page-2026-04-22T15-35-04-346Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T15-37-27-039Z.yml +5 -0
- package/.playwright-mcp/page-2026-04-22T15-51-15-600Z.yml +76 -0
- package/.playwright-mcp/page-2026-04-22T15-51-33-605Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T15-51-54-206Z.yml +676 -0
- package/.playwright-mcp/page-2026-04-22T15-53-11-277Z.yml +632 -0
- package/.playwright-mcp/page-2026-04-22T15-53-40-297Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T16-12-02-855Z.yml +0 -0
- package/.playwright-mcp/page-2026-04-22T16-33-45-281Z.yml +0 -0
- package/HISTORY.md +15 -3
- package/README.md +32 -6
- package/ROADMAP.md +29 -6
- package/dist/swig.js +3 -3
- package/dist/swig.min.js +3 -3
- package/dist/swig.min.js.map +1 -1
- package/lib/swig.js +2 -2
- package/package.json +2 -2
- package/rhinostone-swig-2.0.0-alpha.7.tgz +0 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
- generic [ref=e2]:
|
|
2
|
+
- region "Skip to main content":
|
|
3
|
+
- link "Skip to main content" [ref=e3] [cursor=pointer]:
|
|
4
|
+
- /url: "#__docusaurus_skipToContent_fallback"
|
|
5
|
+
- navigation "Main" [ref=e4]:
|
|
6
|
+
- generic [ref=e5]:
|
|
7
|
+
- generic [ref=e6]:
|
|
8
|
+
- link "Gina logo Gina" [ref=e7] [cursor=pointer]:
|
|
9
|
+
- /url: /docs/
|
|
10
|
+
- img "Gina logo" [ref=e9]
|
|
11
|
+
- generic [ref=e10]: Gina
|
|
12
|
+
- link "Docs" [ref=e11] [cursor=pointer]:
|
|
13
|
+
- /url: /docs/intro
|
|
14
|
+
- link "Tutorials" [ref=e12] [cursor=pointer]:
|
|
15
|
+
- /url: /docs/tutorials
|
|
16
|
+
- link "Swig" [ref=e13] [cursor=pointer]:
|
|
17
|
+
- /url: /docs/swig/
|
|
18
|
+
- link "Roadmap" [ref=e14] [cursor=pointer]:
|
|
19
|
+
- /url: /docs/roadmap
|
|
20
|
+
- generic [ref=e15]:
|
|
21
|
+
- link "v0.3.6(opens in new tab)" [ref=e16] [cursor=pointer]:
|
|
22
|
+
- /url: https://github.com/gina-io/gina/releases
|
|
23
|
+
- text: v0.3.6
|
|
24
|
+
- img "(opens in new tab)" [ref=e17]
|
|
25
|
+
- link "GitHub(opens in new tab)" [ref=e19] [cursor=pointer]:
|
|
26
|
+
- /url: https://github.com/gina-io/gina
|
|
27
|
+
- text: GitHub
|
|
28
|
+
- img "(opens in new tab)" [ref=e20]
|
|
29
|
+
- button "Switch between dark and light mode (currently system mode)" [disabled] [ref=e23]:
|
|
30
|
+
- img [ref=e24]
|
|
31
|
+
- textbox "Search" [ref=e28]
|
|
32
|
+
- generic [ref=e31]:
|
|
33
|
+
- complementary [ref=e32]:
|
|
34
|
+
- generic [ref=e33]:
|
|
35
|
+
- generic [ref=e34]:
|
|
36
|
+
- link "@rhinostone/swig v1.6.0" [ref=e35] [cursor=pointer]:
|
|
37
|
+
- /url: https://www.npmjs.com/package/@rhinostone/swig
|
|
38
|
+
- generic [ref=e36]: "@rhinostone/swig"
|
|
39
|
+
- generic [ref=e37]: v1.6.0
|
|
40
|
+
- link "gina-io/swig on GitHub" [ref=e38] [cursor=pointer]:
|
|
41
|
+
- /url: https://github.com/gina-io/swig
|
|
42
|
+
- img [ref=e39]
|
|
43
|
+
- navigation "Docs sidebar" [ref=e42]:
|
|
44
|
+
- list [ref=e43]:
|
|
45
|
+
- listitem [ref=e44]:
|
|
46
|
+
- link "Overview" [ref=e45] [cursor=pointer]:
|
|
47
|
+
- /url: /docs/swig/
|
|
48
|
+
- img [ref=e47]
|
|
49
|
+
- generic "Overview" [ref=e50]
|
|
50
|
+
- listitem [ref=e51]:
|
|
51
|
+
- link "Getting Started" [ref=e52] [cursor=pointer]:
|
|
52
|
+
- /url: /docs/swig/getting-started
|
|
53
|
+
- img [ref=e54]
|
|
54
|
+
- generic "Getting Started" [ref=e59]
|
|
55
|
+
- listitem [ref=e60]:
|
|
56
|
+
- link "Template Syntax" [ref=e61] [cursor=pointer]:
|
|
57
|
+
- /url: /docs/swig/syntax
|
|
58
|
+
- img [ref=e63]
|
|
59
|
+
- generic "Template Syntax" [ref=e66]
|
|
60
|
+
- listitem [ref=e67]:
|
|
61
|
+
- link "Tags" [ref=e68] [cursor=pointer]:
|
|
62
|
+
- /url: /docs/swig/tags
|
|
63
|
+
- img [ref=e70]
|
|
64
|
+
- generic "Tags" [ref=e72]
|
|
65
|
+
- listitem [ref=e73]:
|
|
66
|
+
- link "Filters" [ref=e74] [cursor=pointer]:
|
|
67
|
+
- /url: /docs/swig/filters
|
|
68
|
+
- img [ref=e76]
|
|
69
|
+
- generic "Filters" [ref=e78]
|
|
70
|
+
- listitem [ref=e79]:
|
|
71
|
+
- link "API" [ref=e80] [cursor=pointer]:
|
|
72
|
+
- /url: /docs/swig/api
|
|
73
|
+
- img [ref=e82]
|
|
74
|
+
- generic "API" [ref=e85]
|
|
75
|
+
- listitem [ref=e86]:
|
|
76
|
+
- link "Loaders" [ref=e87] [cursor=pointer]:
|
|
77
|
+
- /url: /docs/swig/loaders
|
|
78
|
+
- img [ref=e89]
|
|
79
|
+
- generic "Loaders" [ref=e93]
|
|
80
|
+
- listitem [ref=e94]:
|
|
81
|
+
- link "Extending Swig" [ref=e95] [cursor=pointer]:
|
|
82
|
+
- /url: /docs/swig/extending
|
|
83
|
+
- img [ref=e97]
|
|
84
|
+
- generic "Extending Swig" [ref=e99]
|
|
85
|
+
- listitem [ref=e100]:
|
|
86
|
+
- link "CLI" [ref=e101] [cursor=pointer]:
|
|
87
|
+
- /url: /docs/swig/cli
|
|
88
|
+
- img [ref=e103]
|
|
89
|
+
- generic "CLI" [ref=e105]
|
|
90
|
+
- listitem [ref=e106]:
|
|
91
|
+
- link "Browser Usage" [ref=e107] [cursor=pointer]:
|
|
92
|
+
- /url: /docs/swig/browser
|
|
93
|
+
- img [ref=e109]
|
|
94
|
+
- generic "Browser Usage" [ref=e111]
|
|
95
|
+
- listitem [ref=e112]:
|
|
96
|
+
- link "Security" [ref=e113] [cursor=pointer]:
|
|
97
|
+
- /url: /docs/swig/security
|
|
98
|
+
- img [ref=e115]
|
|
99
|
+
- generic "Security" [ref=e117]
|
|
100
|
+
- listitem [ref=e118]:
|
|
101
|
+
- generic [ref=e119]:
|
|
102
|
+
- link "Twig Frontend" [ref=e120] [cursor=pointer]:
|
|
103
|
+
- /url: /docs/swig/twig/
|
|
104
|
+
- generic "Twig Frontend" [ref=e121]
|
|
105
|
+
- button "Collapse sidebar category 'Twig Frontend'" [expanded] [ref=e122] [cursor=pointer]
|
|
106
|
+
- list [ref=e123]:
|
|
107
|
+
- listitem [ref=e124]:
|
|
108
|
+
- link "Parity" [ref=e125] [cursor=pointer]:
|
|
109
|
+
- /url: /docs/swig/twig/parity
|
|
110
|
+
- generic "Parity" [ref=e126]
|
|
111
|
+
- listitem [ref=e127]:
|
|
112
|
+
- link "Non-Goals" [ref=e128] [cursor=pointer]:
|
|
113
|
+
- /url: /docs/swig/twig/non-goals
|
|
114
|
+
- generic "Non-Goals" [ref=e129]
|
|
115
|
+
- listitem [ref=e130]:
|
|
116
|
+
- link "From upstream Twig" [ref=e131] [cursor=pointer]:
|
|
117
|
+
- /url: /docs/swig/twig/migration
|
|
118
|
+
- generic "From upstream Twig" [ref=e132]
|
|
119
|
+
- listitem [ref=e133]:
|
|
120
|
+
- link "Browser Usage" [ref=e134] [cursor=pointer]:
|
|
121
|
+
- /url: /docs/swig/twig/browser
|
|
122
|
+
- img [ref=e136]
|
|
123
|
+
- generic "Browser Usage" [ref=e138]
|
|
124
|
+
- listitem [ref=e139]:
|
|
125
|
+
- link "Migration Guide" [ref=e140] [cursor=pointer]:
|
|
126
|
+
- /url: /docs/swig/migration
|
|
127
|
+
- img [ref=e142]
|
|
128
|
+
- generic "Migration Guide" [ref=e144]
|
|
129
|
+
- main [ref=e145]:
|
|
130
|
+
- generic [ref=e147]:
|
|
131
|
+
- generic [ref=e149]:
|
|
132
|
+
- article [ref=e150]:
|
|
133
|
+
- navigation "Breadcrumbs" [ref=e151]:
|
|
134
|
+
- list [ref=e152]:
|
|
135
|
+
- listitem [ref=e153]:
|
|
136
|
+
- link "Home page" [ref=e154] [cursor=pointer]:
|
|
137
|
+
- /url: /docs/
|
|
138
|
+
- img [ref=e155]
|
|
139
|
+
- listitem [ref=e157]:
|
|
140
|
+
- link "Twig Frontend" [ref=e158] [cursor=pointer]:
|
|
141
|
+
- /url: /docs/swig/twig/
|
|
142
|
+
- listitem [ref=e159]:
|
|
143
|
+
- generic [ref=e160]: Non-Goals
|
|
144
|
+
- generic [ref=e161]:
|
|
145
|
+
- heading "Twig Non-Goals" [level=1] [ref=e163]
|
|
146
|
+
- generic [ref=e164]:
|
|
147
|
+
- generic [ref=e165]:
|
|
148
|
+
- generic [ref=e166]:
|
|
149
|
+
- img [ref=e167]
|
|
150
|
+
- text: 6 min read
|
|
151
|
+
- generic [ref=e170]: Intermediate
|
|
152
|
+
- generic [ref=e171]:
|
|
153
|
+
- generic [ref=e172]: Prerequisites
|
|
154
|
+
- list [ref=e173]:
|
|
155
|
+
- listitem [ref=e174]:
|
|
156
|
+
- link "Overview" [ref=e175] [cursor=pointer]:
|
|
157
|
+
- /url: /docs/swig/twig/
|
|
158
|
+
- listitem [ref=e176]:
|
|
159
|
+
- link "Parity" [ref=e177] [cursor=pointer]:
|
|
160
|
+
- /url: /docs/swig/twig/parity
|
|
161
|
+
- listitem [ref=e178]:
|
|
162
|
+
- link "Security — the parser is the boundary" [ref=e179] [cursor=pointer]:
|
|
163
|
+
- /url: /docs/swig/security#the-parser-is-the-boundary
|
|
164
|
+
- paragraph [ref=e180]:
|
|
165
|
+
- code [ref=e181]: "@rhinostone/swig-twig"
|
|
166
|
+
- text: is a
|
|
167
|
+
- strong [ref=e182]: strict subset
|
|
168
|
+
- text: of upstream PHP Twig. Syntax outside the subset throws at parse time rather than silently producing a template that behaves differently from upstream. This page enumerates each rejection, the error message produced, and the rationale.
|
|
169
|
+
- paragraph [ref=e183]: "The rejections fall into three buckets:"
|
|
170
|
+
- list [ref=e184]:
|
|
171
|
+
- listitem [ref=e185]:
|
|
172
|
+
- strong [ref=e186]: Sandbox / extension tags
|
|
173
|
+
- text: that rely on runtime features swig-core does not expose.
|
|
174
|
+
- listitem [ref=e187]:
|
|
175
|
+
- strong [ref=e188]: Deferred features
|
|
176
|
+
- text: that will likely ship later but need dedicated IR design work.
|
|
177
|
+
- listitem [ref=e189]:
|
|
178
|
+
- strong [ref=e190]: Intentional security rejections
|
|
179
|
+
- text: where silent acceptance would weaken the CVE-2023-25345 guard perimeter.
|
|
180
|
+
- heading "Unsupported tagsDirect link to Unsupported tags" [level=2] [ref=e191]:
|
|
181
|
+
- text: Unsupported tags
|
|
182
|
+
- link "Direct link to Unsupported tags" [ref=e192] [cursor=pointer]:
|
|
183
|
+
- /url: "#unsupported-tags"
|
|
184
|
+
- text: "#"
|
|
185
|
+
- paragraph [ref=e193]:
|
|
186
|
+
- text: All of the following throw a generic
|
|
187
|
+
- code [ref=e194]: Unexpected tag "<name>"
|
|
188
|
+
- text: "error at parse time. They split into two categories:"
|
|
189
|
+
- list [ref=e195]:
|
|
190
|
+
- listitem [ref=e196]:
|
|
191
|
+
- strong [ref=e197]: Won't support (security).
|
|
192
|
+
- text: Threat-model calls; unlikely to change.
|
|
193
|
+
- listitem [ref=e198]:
|
|
194
|
+
- strong [ref=e199]: Deferred.
|
|
195
|
+
- text: Design work awaiting demand —
|
|
196
|
+
- link "open an issue" [ref=e200] [cursor=pointer]:
|
|
197
|
+
- /url: https://github.com/gina-io/swig/issues
|
|
198
|
+
- text: if you hit one.
|
|
199
|
+
- heading "Won't support (security)Direct link to Won't support (security)" [level=3] [ref=e201]:
|
|
200
|
+
- text: Won't support (security)
|
|
201
|
+
- link "Direct link to Won't support (security)" [ref=e202] [cursor=pointer]:
|
|
202
|
+
- /url: "#wont-support-security"
|
|
203
|
+
- text: "#"
|
|
204
|
+
- table [ref=e203]:
|
|
205
|
+
- rowgroup [ref=e204]:
|
|
206
|
+
- row "Tag Upstream purpose Why rejected" [ref=e205]:
|
|
207
|
+
- columnheader "Tag" [ref=e206]
|
|
208
|
+
- columnheader "Upstream purpose" [ref=e207]
|
|
209
|
+
- columnheader "Why rejected" [ref=e208]
|
|
210
|
+
- rowgroup [ref=e209]:
|
|
211
|
+
- 'row "{% sandbox %} Restrict the template runtime (whitelist tags, filters, functions). swig-twig''s threat model declares template source trusted (see Security — the parser is the boundary). Inverting that requires closing runtime bracket access (foo[varname] where varname resolves to \"__proto__\" — a known unclosable gap), re-auditing every .safe=true filter as attacker-reachable, and accepting that new Function(...) compiles the attacker-controlled source. A partial sandbox that users trust is strictly worse than none." [ref=e210]':
|
|
212
|
+
- 'cell "{% sandbox %}" [ref=e211]':
|
|
213
|
+
- code [ref=e212]: "{% sandbox %}"
|
|
214
|
+
- cell "Restrict the template runtime (whitelist tags, filters, functions)." [ref=e213]
|
|
215
|
+
- cell "swig-twig's threat model declares template source trusted (see Security — the parser is the boundary). Inverting that requires closing runtime bracket access (foo[varname] where varname resolves to \"__proto__\" — a known unclosable gap), re-auditing every .safe=true filter as attacker-reachable, and accepting that new Function(...) compiles the attacker-controlled source. A partial sandbox that users trust is strictly worse than none." [ref=e214]:
|
|
216
|
+
- text: swig-twig's threat model declares template source trusted (see
|
|
217
|
+
- link "Security — the parser is the boundary" [ref=e215] [cursor=pointer]:
|
|
218
|
+
- /url: /docs/swig/security#the-parser-is-the-boundary
|
|
219
|
+
- text: ). Inverting that requires closing runtime bracket access (
|
|
220
|
+
- code [ref=e216]: foo[varname]
|
|
221
|
+
- text: where varname resolves to
|
|
222
|
+
- code [ref=e217]: "\"__proto__\""
|
|
223
|
+
- text: — a
|
|
224
|
+
- link "known unclosable gap" [ref=e218] [cursor=pointer]:
|
|
225
|
+
- /url: /docs/swig/security#what-the-guards-do-not-protect-against
|
|
226
|
+
- text: ), re-auditing every
|
|
227
|
+
- code [ref=e219]: .safe=true
|
|
228
|
+
- text: filter as attacker-reachable, and accepting that
|
|
229
|
+
- code [ref=e220]: new Function(...)
|
|
230
|
+
- text: compiles the attacker-controlled source. A partial sandbox that users trust is strictly worse than none.
|
|
231
|
+
- 'row "{% do %} Evaluate an expression for its side effects. Every token-swallowing tag is a new _dangerousProps audit site, duplicated across frontend and backend (see CVE-2023-25345 Phase 2). Small surface widening, for pure sugar over {{ foo.method() }} — autoescape already discards the return value." [ref=e221]':
|
|
232
|
+
- 'cell "{% do %}" [ref=e222]':
|
|
233
|
+
- code [ref=e223]: "{% do %}"
|
|
234
|
+
- cell "Evaluate an expression for its side effects." [ref=e224]
|
|
235
|
+
- 'cell "Every token-swallowing tag is a new _dangerousProps audit site, duplicated across frontend and backend (see CVE-2023-25345 Phase 2). Small surface widening, for pure sugar over {{ foo.method() }} — autoescape already discards the return value." [ref=e225]':
|
|
236
|
+
- text: Every token-swallowing tag is a new
|
|
237
|
+
- code [ref=e226]: _dangerousProps
|
|
238
|
+
- text: audit site, duplicated across frontend and backend (see
|
|
239
|
+
- link "CVE-2023-25345 Phase 2" [ref=e227] [cursor=pointer]:
|
|
240
|
+
- /url: /docs/swig/security#cve-2023-25345--arbitrary-code-execution-via-__proto__
|
|
241
|
+
- text: ). Small surface widening, for pure sugar over
|
|
242
|
+
- code [ref=e228]: "{{ foo.method() }}"
|
|
243
|
+
- text: — autoescape already discards the return value.
|
|
244
|
+
- heading "DeferredDirect link to Deferred" [level=3] [ref=e229]:
|
|
245
|
+
- text: Deferred
|
|
246
|
+
- link "Direct link to Deferred" [ref=e230] [cursor=pointer]:
|
|
247
|
+
- /url: "#deferred"
|
|
248
|
+
- text: "#"
|
|
249
|
+
- table [ref=e231]:
|
|
250
|
+
- rowgroup [ref=e232]:
|
|
251
|
+
- row "Tag Upstream purpose Why not yet" [ref=e233]:
|
|
252
|
+
- columnheader "Tag" [ref=e234]
|
|
253
|
+
- columnheader "Upstream purpose" [ref=e235]
|
|
254
|
+
- columnheader "Why not yet" [ref=e236]
|
|
255
|
+
- rowgroup [ref=e237]:
|
|
256
|
+
- 'row "{% embed %} Include with block-override. Compound of include + block remapping. Most commonly requested of the three — likely to ship in a later minor if demand shows up." [ref=e238]':
|
|
257
|
+
- 'cell "{% embed %}" [ref=e239]':
|
|
258
|
+
- code [ref=e240]: "{% embed %}"
|
|
259
|
+
- cell "Include with block-override." [ref=e241]
|
|
260
|
+
- cell "Compound of include + block remapping. Most commonly requested of the three — likely to ship in a later minor if demand shows up." [ref=e242]:
|
|
261
|
+
- text: Compound of
|
|
262
|
+
- code [ref=e243]: include
|
|
263
|
+
- text: + block remapping. Most commonly requested of the three — likely to ship in a later minor if demand shows up.
|
|
264
|
+
- 'row "{% use %} Pull blocks in from a \"trait\" template without inheriting it. Block composition is expressible via extends + include today. Needs a dedicated IR node." [ref=e244]':
|
|
265
|
+
- 'cell "{% use %}" [ref=e245]':
|
|
266
|
+
- code [ref=e246]: "{% use %}"
|
|
267
|
+
- cell "Pull blocks in from a \"trait\" template without inheriting it." [ref=e247]
|
|
268
|
+
- cell "Block composition is expressible via extends + include today. Needs a dedicated IR node." [ref=e248]:
|
|
269
|
+
- text: Block composition is expressible via
|
|
270
|
+
- code [ref=e249]: extends
|
|
271
|
+
- text: +
|
|
272
|
+
- code [ref=e250]: include
|
|
273
|
+
- text: today. Needs a dedicated IR node.
|
|
274
|
+
- 'row "{% deprecated %} Emit a deprecation warning from within a template. Trivial to build; low demand in swig-twig''s likely audience." [ref=e251]':
|
|
275
|
+
- 'cell "{% deprecated %}" [ref=e252]':
|
|
276
|
+
- code [ref=e253]: "{% deprecated %}"
|
|
277
|
+
- cell "Emit a deprecation warning from within a template." [ref=e254]
|
|
278
|
+
- cell "Trivial to build; low demand in swig-twig's likely audience." [ref=e255]
|
|
279
|
+
- paragraph [ref=e256]: "If you need one of these, either:"
|
|
280
|
+
- list [ref=e257]:
|
|
281
|
+
- listitem [ref=e258]:
|
|
282
|
+
- text: Express the same intent with supported tags (e.g.
|
|
283
|
+
- code [ref=e259]: extends
|
|
284
|
+
- text: +
|
|
285
|
+
- code [ref=e260]: block
|
|
286
|
+
- text: for trait-style composition).
|
|
287
|
+
- listitem [ref=e261]:
|
|
288
|
+
- text: Register a
|
|
289
|
+
- link "custom tag" [ref=e262] [cursor=pointer]:
|
|
290
|
+
- /url: /docs/swig/extending#custom-tags
|
|
291
|
+
- text: scoped to your application.
|
|
292
|
+
- listitem [ref=e263]:
|
|
293
|
+
- link "Open an issue" [ref=e264] [cursor=pointer]:
|
|
294
|
+
- /url: https://github.com/gina-io/swig/issues
|
|
295
|
+
- text: with a concrete use case — especially for the "Deferred" rows.
|
|
296
|
+
- heading "Macro kwargsDirect link to Macro kwargs" [level=2] [ref=e265]:
|
|
297
|
+
- text: Macro kwargs
|
|
298
|
+
- link "Direct link to Macro kwargs" [ref=e266] [cursor=pointer]:
|
|
299
|
+
- /url: "#macro-kwargs"
|
|
300
|
+
- text: "#"
|
|
301
|
+
- paragraph [ref=e267]: "Upstream Twig supports positional and keyword macro calls:"
|
|
302
|
+
- code [ref=e271]:
|
|
303
|
+
- generic [ref=e272]: "{% macro field(name, value=null, class=\"\") %}…{% endmacro %}"
|
|
304
|
+
- generic [ref=e273]: "{{ field(\"email\", value=user.email, class=\"required\") }}"
|
|
305
|
+
- paragraph [ref=e274]:
|
|
306
|
+
- text: swig-twig accepts the positional form —
|
|
307
|
+
- code [ref=e275]: "{{ field(\"email\", user.email, \"required\") }}"
|
|
308
|
+
- text: — but
|
|
309
|
+
- strong [ref=e276]: rejects the keyword-argument form
|
|
310
|
+
- text: . Lowering kwargs needs a per-macro parameter map in the IR, plus a runtime dispatcher in
|
|
311
|
+
- code [ref=e277]: swig-core
|
|
312
|
+
- text: . That landed on the Phase 4 target list but slipped past alpha.5.
|
|
313
|
+
- paragraph [ref=e278]: "Workarounds:"
|
|
314
|
+
- list [ref=e279]:
|
|
315
|
+
- listitem [ref=e280]:
|
|
316
|
+
- text: "Pass an object literal and destructure inside the macro:"
|
|
317
|
+
- code [ref=e284]:
|
|
318
|
+
- generic [ref=e285]: "{% macro field(args) %}{{ args.name }} — {{ args.class ?? \"\" }}{% endmacro %}"
|
|
319
|
+
- generic [ref=e286]: "{{ field({ name: \"email\", class: \"required\" }) }}"
|
|
320
|
+
- listitem [ref=e287]: Order positional parameters from most-commonly-passed to least-commonly-passed so defaults "fall off the right."
|
|
321
|
+
- 'heading "Dynamic {% extends %} and {% from %}Direct link to dynamic--extends--and--from-" [level=2] [ref=e288]':
|
|
322
|
+
- text: Dynamic
|
|
323
|
+
- code [ref=e289]: "{% extends %}"
|
|
324
|
+
- text: and
|
|
325
|
+
- code [ref=e290]: "{% from %}"
|
|
326
|
+
- link "Direct link to dynamic--extends--and--from-" [ref=e291] [cursor=pointer]:
|
|
327
|
+
- /url: "#dynamic--extends--and--from-"
|
|
328
|
+
- text: "#"
|
|
329
|
+
- paragraph [ref=e292]:
|
|
330
|
+
- text: Both tags require the path to be a
|
|
331
|
+
- strong [ref=e293]: string literal
|
|
332
|
+
- text: ". Expressions throw:"
|
|
333
|
+
- table [ref=e294]:
|
|
334
|
+
- rowgroup [ref=e295]:
|
|
335
|
+
- row "Shape Accepted? Error" [ref=e296]:
|
|
336
|
+
- columnheader "Shape" [ref=e297]
|
|
337
|
+
- columnheader "Accepted?" [ref=e298]
|
|
338
|
+
- columnheader "Error" [ref=e299]
|
|
339
|
+
- rowgroup [ref=e300]:
|
|
340
|
+
- 'row "{% extends \"base.twig\" %} Yes —" [ref=e301]':
|
|
341
|
+
- 'cell "{% extends \"base.twig\" %}" [ref=e302]':
|
|
342
|
+
- code [ref=e303]: "{% extends \"base.twig\" %}"
|
|
343
|
+
- cell "Yes" [ref=e304]
|
|
344
|
+
- cell "—" [ref=e305]
|
|
345
|
+
- 'row "{% extends parent_template %} No Dynamic \"extends\" is not supported — parent path must be a string literal" [ref=e306]':
|
|
346
|
+
- 'cell "{% extends parent_template %}" [ref=e307]':
|
|
347
|
+
- code [ref=e308]: "{% extends parent_template %}"
|
|
348
|
+
- cell "No" [ref=e309]
|
|
349
|
+
- cell "Dynamic \"extends\" is not supported — parent path must be a string literal" [ref=e310]:
|
|
350
|
+
- code [ref=e311]: Dynamic "extends" is not supported — parent path must be a string literal
|
|
351
|
+
- 'row "{% extends cond ? \"a.twig\" : \"b.twig\" %} No Same as above." [ref=e312]':
|
|
352
|
+
- 'cell "{% extends cond ? \"a.twig\" : \"b.twig\" %}" [ref=e313]':
|
|
353
|
+
- code [ref=e314]: "{% extends cond ? \"a.twig\" : \"b.twig\" %}"
|
|
354
|
+
- cell "No" [ref=e315]
|
|
355
|
+
- cell "Same as above." [ref=e316]
|
|
356
|
+
- 'row "{% from \"macros.twig\" import foo %} Yes —" [ref=e317]':
|
|
357
|
+
- 'cell "{% from \"macros.twig\" import foo %}" [ref=e318]':
|
|
358
|
+
- code [ref=e319]: "{% from \"macros.twig\" import foo %}"
|
|
359
|
+
- cell "Yes" [ref=e320]
|
|
360
|
+
- cell "—" [ref=e321]
|
|
361
|
+
- 'row "{% from dynamic_path import foo %} No Dynamic \"from\" is not supported — path must be a string literal" [ref=e322]':
|
|
362
|
+
- 'cell "{% from dynamic_path import foo %}" [ref=e323]':
|
|
363
|
+
- code [ref=e324]: "{% from dynamic_path import foo %}"
|
|
364
|
+
- cell "No" [ref=e325]
|
|
365
|
+
- cell "Dynamic \"from\" is not supported — path must be a string literal" [ref=e326]:
|
|
366
|
+
- code [ref=e327]: Dynamic "from" is not supported — path must be a string literal
|
|
367
|
+
- paragraph [ref=e328]:
|
|
368
|
+
- text: "Rationale: swig's"
|
|
369
|
+
- code [ref=e329]: getParents()
|
|
370
|
+
- text: walks the inheritance chain at parse time so it can cache compiled templates by resolved filename. A dynamic parent would either break that caching (recompile on every render) or require a runtime loader path that none of the built-in loaders support today. The string-literal requirement keeps compiled output deterministic and lets the shared cache work.
|
|
371
|
+
- paragraph [ref=e330]:
|
|
372
|
+
- text: "Workaround:"
|
|
373
|
+
- strong [ref=e331]: dispatch in the caller
|
|
374
|
+
- text: and render the selected template directly.
|
|
375
|
+
- code [ref=e335]:
|
|
376
|
+
- generic [ref=e336]: "var template = (flavor === 'admin') ? 'page_admin.twig' : 'page_public.twig';"
|
|
377
|
+
- generic [ref=e337]: "twig.renderFile(template, { /* locals */ });"
|
|
378
|
+
- paragraph [ref=e338]:
|
|
379
|
+
- text: Wrapping
|
|
380
|
+
- code [ref=e339]: "{% extends %}"
|
|
381
|
+
- text: in
|
|
382
|
+
- code [ref=e340]: "{% if %}"
|
|
383
|
+
- text: blocks does not work — the parent link is resolved at parse time, so whichever
|
|
384
|
+
- code [ref=e341]: extends
|
|
385
|
+
- text: the parser sees last wins regardless of the runtime condition. This applies whether you use the (currently
|
|
386
|
+
- link "deferred" [ref=e342] [cursor=pointer]:
|
|
387
|
+
- /url: "#conditional-branches-deferred"
|
|
388
|
+
- text: )
|
|
389
|
+
- code [ref=e343]: "{% if %}…{% else %}…{% endif %}"
|
|
390
|
+
- text: form or paired
|
|
391
|
+
- code [ref=e344]: "{% if %}"
|
|
392
|
+
- text: blocks. Dispatch in the caller is the only working path.
|
|
393
|
+
- paragraph [ref=e345]:
|
|
394
|
+
- code [ref=e346]: include
|
|
395
|
+
- text: is not affected — its path can be any expression.
|
|
396
|
+
- heading "Bracket-notation setDirect link to bracket-notation-set" [level=2] [ref=e347]:
|
|
397
|
+
- text: Bracket-notation
|
|
398
|
+
- code [ref=e348]: set
|
|
399
|
+
- link "Direct link to bracket-notation-set" [ref=e349] [cursor=pointer]:
|
|
400
|
+
- /url: "#bracket-notation-set"
|
|
401
|
+
- text: "#"
|
|
402
|
+
- paragraph [ref=e350]: "Dot-path LHS is supported; bracket-notation LHS throws:"
|
|
403
|
+
- table [ref=e351]:
|
|
404
|
+
- rowgroup [ref=e352]:
|
|
405
|
+
- row "Shape Accepted? Error" [ref=e353]:
|
|
406
|
+
- columnheader "Shape" [ref=e354]
|
|
407
|
+
- columnheader "Accepted?" [ref=e355]
|
|
408
|
+
- columnheader "Error" [ref=e356]
|
|
409
|
+
- rowgroup [ref=e357]:
|
|
410
|
+
- 'row "{% set foo = 1 %} Yes —" [ref=e358]':
|
|
411
|
+
- 'cell "{% set foo = 1 %}" [ref=e359]':
|
|
412
|
+
- code [ref=e360]: "{% set foo = 1 %}"
|
|
413
|
+
- cell "Yes" [ref=e361]
|
|
414
|
+
- cell "—" [ref=e362]
|
|
415
|
+
- 'row "{% set foo.bar = 1 %} Yes —" [ref=e363]':
|
|
416
|
+
- 'cell "{% set foo.bar = 1 %}" [ref=e364]':
|
|
417
|
+
- code [ref=e365]: "{% set foo.bar = 1 %}"
|
|
418
|
+
- cell "Yes" [ref=e366]
|
|
419
|
+
- cell "—" [ref=e367]
|
|
420
|
+
- 'row "{% set foo[\"bar\"] = 1 %} No Bracket-notation assignment is not supported in \"set\" (use dot-path notation)" [ref=e368]':
|
|
421
|
+
- 'cell "{% set foo[\"bar\"] = 1 %}" [ref=e369]':
|
|
422
|
+
- code [ref=e370]: "{% set foo[\"bar\"] = 1 %}"
|
|
423
|
+
- cell "No" [ref=e371]
|
|
424
|
+
- cell "Bracket-notation assignment is not supported in \"set\" (use dot-path notation)" [ref=e372]:
|
|
425
|
+
- code [ref=e373]: Bracket-notation assignment is not supported in "set" (use dot-path notation)
|
|
426
|
+
- 'row "{% set foo[dynamic_key] = 1 %} No Same as above." [ref=e374]':
|
|
427
|
+
- 'cell "{% set foo[dynamic_key] = 1 %}" [ref=e375]':
|
|
428
|
+
- code [ref=e376]: "{% set foo[dynamic_key] = 1 %}"
|
|
429
|
+
- cell "No" [ref=e377]
|
|
430
|
+
- cell "Same as above." [ref=e378]
|
|
431
|
+
- paragraph [ref=e379]:
|
|
432
|
+
- text: "Rationale: bracket-notation writes at runtime can reach through the prototype chain ("
|
|
433
|
+
- code [ref=e380]: foo["__proto__"]["x"] = …
|
|
434
|
+
- text: ) in ways that the parse-time
|
|
435
|
+
- code [ref=e381]: _dangerousProps
|
|
436
|
+
- text: check cannot fully enclose. Dot-path writes are statically analysable — each segment is a lexer-validated identifier — and the native swig frontend already went through this audit during
|
|
437
|
+
- link "CVE-2023-25345 Phase 2" [ref=e382] [cursor=pointer]:
|
|
438
|
+
- /url: /docs/swig/security#cve-2023-25345--arbitrary-code-execution-via-__proto__
|
|
439
|
+
- text: . swig-twig inherits the same perimeter.
|
|
440
|
+
- heading "Conditional branches (deferred)Direct link to Conditional branches (deferred)" [level=2] [ref=e383]:
|
|
441
|
+
- text: Conditional branches (deferred)
|
|
442
|
+
- link "Direct link to Conditional branches (deferred)" [ref=e384] [cursor=pointer]:
|
|
443
|
+
- /url: "#conditional-branches-deferred"
|
|
444
|
+
- text: "#"
|
|
445
|
+
- paragraph [ref=e385]:
|
|
446
|
+
- text: Upstream Twig supports multi-branch conditionals —
|
|
447
|
+
- code [ref=e386]: "{% if %} … {% elseif %} … {% else %} … {% endif %}"
|
|
448
|
+
- text: — and an empty-fallback form on
|
|
449
|
+
- code [ref=e387]: "{% for %}"
|
|
450
|
+
- text: —
|
|
451
|
+
- code [ref=e388]: "{% for %} … {% else %} … {% endfor %}"
|
|
452
|
+
- text: (runs when the iterable is empty). swig-twig alpha.8 ships only the single-branch form.
|
|
453
|
+
- table [ref=e389]:
|
|
454
|
+
- rowgroup [ref=e390]:
|
|
455
|
+
- row "Shape Accepted? Error" [ref=e391]:
|
|
456
|
+
- columnheader "Shape" [ref=e392]
|
|
457
|
+
- columnheader "Accepted?" [ref=e393]
|
|
458
|
+
- columnheader "Error" [ref=e394]
|
|
459
|
+
- rowgroup [ref=e395]:
|
|
460
|
+
- 'row "{% if x %}…{% endif %} Yes —" [ref=e396]':
|
|
461
|
+
- 'cell "{% if x %}…{% endif %}" [ref=e397]':
|
|
462
|
+
- code [ref=e398]: "{% if x %}…{% endif %}"
|
|
463
|
+
- cell "Yes" [ref=e399]
|
|
464
|
+
- cell "—" [ref=e400]
|
|
465
|
+
- 'row "{% if x %}…{% else %}…{% endif %} No Unexpected tag \"else\"" [ref=e401]':
|
|
466
|
+
- 'cell "{% if x %}…{% else %}…{% endif %}" [ref=e402]':
|
|
467
|
+
- code [ref=e403]: "{% if x %}…{% else %}…{% endif %}"
|
|
468
|
+
- cell "No" [ref=e404]
|
|
469
|
+
- cell "Unexpected tag \"else\"" [ref=e405]:
|
|
470
|
+
- code [ref=e406]: Unexpected tag "else"
|
|
471
|
+
- 'row "{% if x %}…{% elseif y %}…{% endif %} No Unexpected tag \"elseif\"" [ref=e407]':
|
|
472
|
+
- 'cell "{% if x %}…{% elseif y %}…{% endif %}" [ref=e408]':
|
|
473
|
+
- code [ref=e409]: "{% if x %}…{% elseif y %}…{% endif %}"
|
|
474
|
+
- cell "No" [ref=e410]
|
|
475
|
+
- cell "Unexpected tag \"elseif\"" [ref=e411]:
|
|
476
|
+
- code [ref=e412]: Unexpected tag "elseif"
|
|
477
|
+
- 'row "{% for x in items %}…{% endfor %} Yes —" [ref=e413]':
|
|
478
|
+
- 'cell "{% for x in items %}…{% endfor %}" [ref=e414]':
|
|
479
|
+
- code [ref=e415]: "{% for x in items %}…{% endfor %}"
|
|
480
|
+
- cell "Yes" [ref=e416]
|
|
481
|
+
- cell "—" [ref=e417]
|
|
482
|
+
- 'row "{% for x in items %}…{% else %}…{% endfor %} No Unexpected tag \"else\"" [ref=e418]':
|
|
483
|
+
- 'cell "{% for x in items %}…{% else %}…{% endfor %}" [ref=e419]':
|
|
484
|
+
- code [ref=e420]: "{% for x in items %}…{% else %}…{% endfor %}"
|
|
485
|
+
- cell "No" [ref=e421]
|
|
486
|
+
- cell "Unexpected tag \"else\"" [ref=e422]:
|
|
487
|
+
- code [ref=e423]: Unexpected tag "else"
|
|
488
|
+
- paragraph [ref=e424]:
|
|
489
|
+
- text: "Rationale: the single-branch shape shipped first to unblock the rest of the Twig parser surface. Multi-branch lowering needs a dedicated branch-chain IR node plus the frontend handler that consumes"
|
|
490
|
+
- code [ref=e425]: else
|
|
491
|
+
- text: /
|
|
492
|
+
- code [ref=e426]: elseif
|
|
493
|
+
- text: tokens without pushing them on the open-tag stack. Not blocking
|
|
494
|
+
- code [ref=e427]: 2.0.0
|
|
495
|
+
- text: stable.
|
|
496
|
+
- link "Open an issue" [ref=e428] [cursor=pointer]:
|
|
497
|
+
- /url: https://github.com/gina-io/swig/issues
|
|
498
|
+
- text: with a concrete use case to prioritise.
|
|
499
|
+
- paragraph [ref=e429]: "Workarounds that parse under alpha.8:"
|
|
500
|
+
- list [ref=e430]:
|
|
501
|
+
- listitem [ref=e431]:
|
|
502
|
+
- strong [ref=e432]:
|
|
503
|
+
- code [ref=e433]: "{% if %} / {% else %}"
|
|
504
|
+
- text: → two
|
|
505
|
+
- code [ref=e434]: if
|
|
506
|
+
- text: blocks with
|
|
507
|
+
- code [ref=e435]: not
|
|
508
|
+
- text: ":"
|
|
509
|
+
- code [ref=e439]:
|
|
510
|
+
- generic [ref=e440]: "{% if x %}yes{% endif %}"
|
|
511
|
+
- generic [ref=e441]: "{% if not x %}no{% endif %}"
|
|
512
|
+
- listitem [ref=e442]:
|
|
513
|
+
- strong [ref=e443]:
|
|
514
|
+
- code [ref=e444]: "{% if %} / {% elseif %} / {% else %}"
|
|
515
|
+
- text: → nested
|
|
516
|
+
- code [ref=e445]: if
|
|
517
|
+
- text: ":"
|
|
518
|
+
- code [ref=e449]:
|
|
519
|
+
- generic [ref=e450]: "{% if x %}a{% endif %}"
|
|
520
|
+
- generic [ref=e451]: "{% if not x %}"
|
|
521
|
+
- generic [ref=e452]: "{% if y %}b{% endif %}"
|
|
522
|
+
- generic [ref=e453]: "{% if not y %}c{% endif %}"
|
|
523
|
+
- generic [ref=e454]: "{% endif %}"
|
|
524
|
+
- text: Uglier than the native form but semantically equivalent.
|
|
525
|
+
- listitem [ref=e455]:
|
|
526
|
+
- strong [ref=e456]:
|
|
527
|
+
- code [ref=e457]: "{% for … %}{% else %}…{% endfor %}"
|
|
528
|
+
- text: → guard with
|
|
529
|
+
- code [ref=e458]: "{% if items|length %}"
|
|
530
|
+
- text: "before the loop:"
|
|
531
|
+
- code [ref=e462]:
|
|
532
|
+
- generic [ref=e463]: "{% if items|length %}"
|
|
533
|
+
- generic [ref=e464]: "{% for item in items %}{{ item }}{% endfor %}"
|
|
534
|
+
- generic [ref=e465]: "{% endif %}"
|
|
535
|
+
- generic [ref=e466]: "{% if not items|length %}empty{% endif %}"
|
|
536
|
+
- heading "Other known gapsDirect link to Other known gaps" [level=2] [ref=e467]:
|
|
537
|
+
- text: Other known gaps
|
|
538
|
+
- link "Direct link to Other known gaps" [ref=e468] [cursor=pointer]:
|
|
539
|
+
- /url: "#other-known-gaps"
|
|
540
|
+
- text: "#"
|
|
541
|
+
- paragraph [ref=e469]: "These are tracked but not yet implemented:"
|
|
542
|
+
- list [ref=e470]:
|
|
543
|
+
- listitem [ref=e471]:
|
|
544
|
+
- strong [ref=e472]:
|
|
545
|
+
- code [ref=e473]: date_modify
|
|
546
|
+
- text: filter.
|
|
547
|
+
- text: Relies on DateTime-object mutation; deferred.
|
|
548
|
+
- listitem [ref=e474]:
|
|
549
|
+
- strong [ref=e475]:
|
|
550
|
+
- text: Locale-aware
|
|
551
|
+
- code [ref=e476]: date
|
|
552
|
+
- text: /
|
|
553
|
+
- code [ref=e477]: number_format
|
|
554
|
+
- text: .
|
|
555
|
+
- text: swig-twig's
|
|
556
|
+
- code [ref=e478]: date
|
|
557
|
+
- text: uses the shared PHP-style formatter from
|
|
558
|
+
- code [ref=e479]: "@rhinostone/swig-core"
|
|
559
|
+
- text: ; locale rules (weekday names, currency formats) are not plumbed through yet.
|
|
560
|
+
- listitem [ref=e480]:
|
|
561
|
+
- strong [ref=e481]: Runtime bracket-access guard.
|
|
562
|
+
- code [ref=e482]: "{{ foo[varname] }}"
|
|
563
|
+
- text: with
|
|
564
|
+
- code [ref=e483]: varname
|
|
565
|
+
- text: resolving to
|
|
566
|
+
- code [ref=e484]: "\"__proto__\""
|
|
567
|
+
- text: at runtime is not blocked. Documented at the engine level — see
|
|
568
|
+
- link "Security — what the guards do NOT protect against" [ref=e485] [cursor=pointer]:
|
|
569
|
+
- /url: /docs/swig/security#what-the-guards-do-not-protect-against
|
|
570
|
+
- text: .
|
|
571
|
+
- heading "Reporting a rejection that feels wrongDirect link to Reporting a rejection that feels wrong" [level=2] [ref=e486]:
|
|
572
|
+
- text: Reporting a rejection that feels wrong
|
|
573
|
+
- link "Direct link to Reporting a rejection that feels wrong" [ref=e487] [cursor=pointer]:
|
|
574
|
+
- /url: "#reporting-a-rejection-that-feels-wrong"
|
|
575
|
+
- text: "#"
|
|
576
|
+
- paragraph [ref=e488]:
|
|
577
|
+
- text: If the rejection you hit isn't listed here, please
|
|
578
|
+
- link "open an issue on gina-io/swig" [ref=e489] [cursor=pointer]:
|
|
579
|
+
- /url: https://github.com/gina-io/swig/issues
|
|
580
|
+
- text: open an issue on
|
|
581
|
+
- code [ref=e490]: gina-io/swig
|
|
582
|
+
- text: with a minimal reproducer — we track rejections against the design-doc subset and may be able to promote a gap to a supported feature.
|
|
583
|
+
- generic [ref=e491]:
|
|
584
|
+
- generic [ref=e492]:
|
|
585
|
+
- generic [ref=e493]: Was this page helpful?
|
|
586
|
+
- generic [ref=e494]:
|
|
587
|
+
- button "Yes, this was helpful" [ref=e495] [cursor=pointer]:
|
|
588
|
+
- generic [ref=e496]: 👍
|
|
589
|
+
- generic [ref=e497]: –
|
|
590
|
+
- button "No, this was not helpful" [ref=e498] [cursor=pointer]:
|
|
591
|
+
- generic [ref=e499]: 👎
|
|
592
|
+
- generic [ref=e500]: –
|
|
593
|
+
- link "Edit this page" [ref=e504] [cursor=pointer]:
|
|
594
|
+
- /url: https://github.com/gina-io/docs/tree/main/docs/swig/twig/non-goals.mdx
|
|
595
|
+
- img [ref=e505]
|
|
596
|
+
- text: Edit this page
|
|
597
|
+
- navigation "Docs pages" [ref=e509]:
|
|
598
|
+
- link "Previous « Parity" [ref=e510] [cursor=pointer]:
|
|
599
|
+
- /url: /docs/swig/twig/parity
|
|
600
|
+
- generic [ref=e511]: Previous
|
|
601
|
+
- generic [ref=e512]: « Parity
|
|
602
|
+
- link "Next From upstream Twig »" [ref=e513] [cursor=pointer]:
|
|
603
|
+
- /url: /docs/swig/twig/migration
|
|
604
|
+
- generic [ref=e514]: Next
|
|
605
|
+
- generic [ref=e515]: From upstream Twig »
|
|
606
|
+
- list [ref=e519]:
|
|
607
|
+
- listitem [ref=e520]:
|
|
608
|
+
- link "Unsupported tags" [ref=e521] [cursor=pointer]:
|
|
609
|
+
- /url: "#unsupported-tags"
|
|
610
|
+
- list [ref=e522]:
|
|
611
|
+
- listitem [ref=e523]:
|
|
612
|
+
- link "Won't support (security)" [ref=e524] [cursor=pointer]:
|
|
613
|
+
- /url: "#wont-support-security"
|
|
614
|
+
- listitem [ref=e525]:
|
|
615
|
+
- link "Deferred" [ref=e526] [cursor=pointer]:
|
|
616
|
+
- /url: "#deferred"
|
|
617
|
+
- listitem [ref=e527]:
|
|
618
|
+
- link "Macro kwargs" [ref=e528] [cursor=pointer]:
|
|
619
|
+
- /url: "#macro-kwargs"
|
|
620
|
+
- listitem [ref=e529]:
|
|
621
|
+
- 'link "Dynamic {% extends %} and {% from %}" [ref=e530] [cursor=pointer]':
|
|
622
|
+
- /url: "#dynamic--extends--and--from-"
|
|
623
|
+
- text: Dynamic
|
|
624
|
+
- code [ref=e531]: "{% extends %}"
|
|
625
|
+
- text: and
|
|
626
|
+
- code [ref=e532]: "{% from %}"
|
|
627
|
+
- listitem [ref=e533]:
|
|
628
|
+
- link "Bracket-notation set" [ref=e534] [cursor=pointer]:
|
|
629
|
+
- /url: "#bracket-notation-set"
|
|
630
|
+
- text: Bracket-notation
|
|
631
|
+
- code [ref=e535]: set
|
|
632
|
+
- listitem [ref=e536]:
|
|
633
|
+
- link "Conditional branches (deferred)" [ref=e537] [cursor=pointer]:
|
|
634
|
+
- /url: "#conditional-branches-deferred"
|
|
635
|
+
- listitem [ref=e538]:
|
|
636
|
+
- link "Other known gaps" [ref=e539] [cursor=pointer]:
|
|
637
|
+
- /url: "#other-known-gaps"
|
|
638
|
+
- listitem [ref=e540]:
|
|
639
|
+
- link "Reporting a rejection that feels wrong" [ref=e541] [cursor=pointer]:
|
|
640
|
+
- /url: "#reporting-a-rejection-that-feels-wrong"
|
|
641
|
+
- contentinfo [ref=e542]:
|
|
642
|
+
- generic [ref=e543]:
|
|
643
|
+
- generic [ref=e544]:
|
|
644
|
+
- generic [ref=e545]:
|
|
645
|
+
- generic [ref=e546]: Docs
|
|
646
|
+
- list [ref=e547]:
|
|
647
|
+
- listitem [ref=e548]:
|
|
648
|
+
- link "Getting Started" [ref=e549] [cursor=pointer]:
|
|
649
|
+
- /url: /docs/intro
|
|
650
|
+
- generic [ref=e550]:
|
|
651
|
+
- generic [ref=e551]: Community
|
|
652
|
+
- list [ref=e552]:
|
|
653
|
+
- listitem [ref=e553]:
|
|
654
|
+
- link "GitHub Issues(opens in new tab)" [ref=e554] [cursor=pointer]:
|
|
655
|
+
- /url: https://github.com/gina-io/gina/issues
|
|
656
|
+
- text: GitHub Issues
|
|
657
|
+
- img "(opens in new tab)" [ref=e555]
|
|
658
|
+
- listitem [ref=e557]:
|
|
659
|
+
- link "GitHub Discussions(opens in new tab)" [ref=e558] [cursor=pointer]:
|
|
660
|
+
- /url: https://github.com/gina-io/gina/discussions
|
|
661
|
+
- text: GitHub Discussions
|
|
662
|
+
- img "(opens in new tab)" [ref=e559]
|
|
663
|
+
- listitem [ref=e561]:
|
|
664
|
+
- link "Support Gina" [ref=e562] [cursor=pointer]:
|
|
665
|
+
- /url: /docs/support
|
|
666
|
+
- generic [ref=e563]:
|
|
667
|
+
- generic [ref=e564]: More
|
|
668
|
+
- list [ref=e565]:
|
|
669
|
+
- listitem [ref=e566]:
|
|
670
|
+
- link "npm(opens in new tab)" [ref=e567] [cursor=pointer]:
|
|
671
|
+
- /url: https://www.npmjs.com/package/gina
|
|
672
|
+
- text: npm
|
|
673
|
+
- img "(opens in new tab)" [ref=e568]
|
|
674
|
+
- generic [ref=e571]: Copyright © 2009-2026 gina-io.
|
|
675
|
+
- button "Collapse sidebar" [ref=e573] [cursor=pointer]:
|
|
676
|
+
- img [ref=e574]
|