@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.
Files changed (33) hide show
  1. package/.changes/v1.0.0-pre1.md +2 -2
  2. package/.changes/v1.0.0.md +1 -1
  3. package/.changes/v2.0.0-alpha.8.md +4 -0
  4. package/.changes/v2.0.0.md +6 -0
  5. package/.playwright-mcp/console-2026-04-22T15-34-20-480Z.log +2 -0
  6. package/.playwright-mcp/console-2026-04-22T15-35-04-265Z.log +11 -0
  7. package/.playwright-mcp/console-2026-04-22T15-37-26-953Z.log +1 -0
  8. package/.playwright-mcp/console-2026-04-22T15-51-15-160Z.log +148 -0
  9. package/.playwright-mcp/console-2026-04-22T15-51-33-405Z.log +74 -0
  10. package/.playwright-mcp/console-2026-04-22T15-51-53-922Z.log +74 -0
  11. package/.playwright-mcp/console-2026-04-22T15-53-10-736Z.log +74 -0
  12. package/.playwright-mcp/console-2026-04-22T15-53-40-091Z.log +883 -0
  13. package/.playwright-mcp/console-2026-04-22T16-12-02-541Z.log +74 -0
  14. package/.playwright-mcp/console-2026-04-22T16-33-44-982Z.log +13973 -0
  15. package/.playwright-mcp/page-2026-04-22T15-34-24-524Z.yml +1 -0
  16. package/.playwright-mcp/page-2026-04-22T15-35-04-346Z.yml +0 -0
  17. package/.playwright-mcp/page-2026-04-22T15-37-27-039Z.yml +5 -0
  18. package/.playwright-mcp/page-2026-04-22T15-51-15-600Z.yml +76 -0
  19. package/.playwright-mcp/page-2026-04-22T15-51-33-605Z.yml +0 -0
  20. package/.playwright-mcp/page-2026-04-22T15-51-54-206Z.yml +676 -0
  21. package/.playwright-mcp/page-2026-04-22T15-53-11-277Z.yml +632 -0
  22. package/.playwright-mcp/page-2026-04-22T15-53-40-297Z.yml +0 -0
  23. package/.playwright-mcp/page-2026-04-22T16-12-02-855Z.yml +0 -0
  24. package/.playwright-mcp/page-2026-04-22T16-33-45-281Z.yml +0 -0
  25. package/HISTORY.md +15 -3
  26. package/README.md +32 -6
  27. package/ROADMAP.md +29 -6
  28. package/dist/swig.js +3 -3
  29. package/dist/swig.min.js +3 -3
  30. package/dist/swig.min.js.map +1 -1
  31. package/lib/swig.js +2 -2
  32. package/package.json +2 -2
  33. package/rhinostone-swig-2.0.0-alpha.7.tgz +0 -0
@@ -0,0 +1,632 @@
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)" [ref=e23] [cursor=pointer]:
30
+ - img [ref=e24]
31
+ - generic [ref=e27]:
32
+ - textbox "Search" [ref=e28]
33
+ - generic:
34
+ - generic: ⌘
35
+ - generic: K
36
+ - generic [ref=e31]:
37
+ - complementary [ref=e32]:
38
+ - generic [ref=e33]:
39
+ - generic [ref=e34]:
40
+ - link "@rhinostone/swig v1.6.0" [ref=e35] [cursor=pointer]:
41
+ - /url: https://www.npmjs.com/package/@rhinostone/swig
42
+ - generic [ref=e36]: "@rhinostone/swig"
43
+ - generic [ref=e37]: v1.6.0
44
+ - link "gina-io/swig on GitHub" [ref=e38] [cursor=pointer]:
45
+ - /url: https://github.com/gina-io/swig
46
+ - img [ref=e39]
47
+ - navigation "Docs sidebar" [ref=e42]:
48
+ - list [ref=e43]:
49
+ - listitem [ref=e44]:
50
+ - link "Overview" [ref=e45] [cursor=pointer]:
51
+ - /url: /docs/swig/
52
+ - img [ref=e47]
53
+ - generic "Overview" [ref=e50]
54
+ - listitem [ref=e51]:
55
+ - link "Getting Started" [ref=e52] [cursor=pointer]:
56
+ - /url: /docs/swig/getting-started
57
+ - img [ref=e54]
58
+ - generic "Getting Started" [ref=e59]
59
+ - listitem [ref=e60]:
60
+ - link "Template Syntax" [ref=e61] [cursor=pointer]:
61
+ - /url: /docs/swig/syntax
62
+ - img [ref=e63]
63
+ - generic "Template Syntax" [ref=e66]
64
+ - listitem [ref=e67]:
65
+ - link "Tags" [ref=e68] [cursor=pointer]:
66
+ - /url: /docs/swig/tags
67
+ - img [ref=e70]
68
+ - generic "Tags" [ref=e72]
69
+ - listitem [ref=e73]:
70
+ - link "Filters" [ref=e74] [cursor=pointer]:
71
+ - /url: /docs/swig/filters
72
+ - img [ref=e76]
73
+ - generic "Filters" [ref=e78]
74
+ - listitem [ref=e79]:
75
+ - link "API" [ref=e80] [cursor=pointer]:
76
+ - /url: /docs/swig/api
77
+ - img [ref=e82]
78
+ - generic "API" [ref=e85]
79
+ - listitem [ref=e86]:
80
+ - link "Loaders" [ref=e87] [cursor=pointer]:
81
+ - /url: /docs/swig/loaders
82
+ - img [ref=e89]
83
+ - generic "Loaders" [ref=e93]
84
+ - listitem [ref=e94]:
85
+ - link "Extending Swig" [ref=e95] [cursor=pointer]:
86
+ - /url: /docs/swig/extending
87
+ - img [ref=e97]
88
+ - generic "Extending Swig" [ref=e99]
89
+ - listitem [ref=e100]:
90
+ - link "CLI" [ref=e101] [cursor=pointer]:
91
+ - /url: /docs/swig/cli
92
+ - img [ref=e103]
93
+ - generic "CLI" [ref=e105]
94
+ - listitem [ref=e106]:
95
+ - link "Browser Usage" [ref=e107] [cursor=pointer]:
96
+ - /url: /docs/swig/browser
97
+ - img [ref=e109]
98
+ - generic "Browser Usage" [ref=e111]
99
+ - listitem [ref=e112]:
100
+ - link "Security" [ref=e113] [cursor=pointer]:
101
+ - /url: /docs/swig/security
102
+ - img [ref=e115]
103
+ - generic "Security" [ref=e117]
104
+ - listitem [ref=e118]:
105
+ - generic [ref=e119]:
106
+ - link "Twig Frontend" [ref=e120] [cursor=pointer]:
107
+ - /url: /docs/swig/twig/
108
+ - generic "Twig Frontend" [ref=e121]
109
+ - button "Collapse sidebar category 'Twig Frontend'" [expanded] [ref=e122] [cursor=pointer]
110
+ - list [ref=e123]:
111
+ - listitem [ref=e124]:
112
+ - link "Parity" [ref=e125] [cursor=pointer]:
113
+ - /url: /docs/swig/twig/parity
114
+ - generic "Parity" [ref=e126]
115
+ - listitem [ref=e127]:
116
+ - link "Non-Goals" [ref=e128] [cursor=pointer]:
117
+ - /url: /docs/swig/twig/non-goals
118
+ - generic "Non-Goals" [ref=e129]
119
+ - listitem [ref=e130]:
120
+ - link "From upstream Twig" [ref=e131] [cursor=pointer]:
121
+ - /url: /docs/swig/twig/migration
122
+ - generic "From upstream Twig" [ref=e132]
123
+ - listitem [ref=e133]:
124
+ - link "Browser Usage" [ref=e134] [cursor=pointer]:
125
+ - /url: /docs/swig/twig/browser
126
+ - img [ref=e136]
127
+ - generic "Browser Usage" [ref=e138]
128
+ - listitem [ref=e139]:
129
+ - link "Migration Guide" [ref=e140] [cursor=pointer]:
130
+ - /url: /docs/swig/migration
131
+ - img [ref=e142]
132
+ - generic "Migration Guide" [ref=e144]
133
+ - main [ref=e145]:
134
+ - generic [ref=e147]:
135
+ - generic [ref=e149]:
136
+ - article [ref=e150]:
137
+ - navigation "Breadcrumbs" [ref=e151]:
138
+ - list [ref=e152]:
139
+ - listitem [ref=e153]:
140
+ - link "Home page" [ref=e154] [cursor=pointer]:
141
+ - /url: /docs/
142
+ - img [ref=e155]
143
+ - listitem [ref=e157]:
144
+ - link "Twig Frontend" [ref=e158] [cursor=pointer]:
145
+ - /url: /docs/swig/twig/
146
+ - listitem [ref=e159]:
147
+ - generic [ref=e160]: Browser Usage
148
+ - generic [ref=e161]:
149
+ - heading "Twig in the Browser" [level=1] [ref=e163]
150
+ - generic [ref=e164]:
151
+ - generic [ref=e165]:
152
+ - generic [ref=e166]:
153
+ - img [ref=e167]
154
+ - text: 4 min read
155
+ - generic [ref=e170]: Intermediate
156
+ - generic [ref=e171]:
157
+ - generic [ref=e172]: Prerequisites
158
+ - list [ref=e173]:
159
+ - listitem [ref=e174]:
160
+ - link "Overview" [ref=e175] [cursor=pointer]:
161
+ - /url: /docs/swig/twig/
162
+ - listitem [ref=e176]:
163
+ - link "Swig Browser Usage" [ref=e177] [cursor=pointer]:
164
+ - /url: /docs/swig/browser
165
+ - listitem [ref=e178]:
166
+ - link "Security — the parser is the boundary" [ref=e179] [cursor=pointer]:
167
+ - /url: /docs/swig/security#the-parser-is-the-boundary
168
+ - listitem [ref=e180]:
169
+ - link "Loaders" [ref=e181] [cursor=pointer]:
170
+ - /url: /docs/swig/loaders
171
+ - paragraph [ref=e182]:
172
+ - code [ref=e183]: "@rhinostone/swig-twig"
173
+ - text: runs in the browser the same way
174
+ - code [ref=e184]: "@rhinostone/swig"
175
+ - text: does — memory loader only, no
176
+ - code [ref=e185]: fs
177
+ - text: access, identical autoescape and CVE-2023-25345 guard perimeter inherited from
178
+ - code [ref=e186]: "@rhinostone/swig-core"
179
+ - text: . The package does not ship a pre-built browser bundle; consumers bundle through their own toolchain (esbuild, Vite, Webpack, Rollup).
180
+ - paragraph [ref=e187]:
181
+ - text: "This page covers the Twig-specific bits: how to bundle, how to install the peer-dependency pair during alpha, and a minimal first-template example using Twig syntax. For the shared-engine flow — AOT pre-compile + prime + render, memory-loader idiom, cache semantics,"
182
+ - code [ref=e188]: swig run
183
+ - text: warnings — see
184
+ - link "Swig Browser Usage" [ref=e189] [cursor=pointer]:
185
+ - /url: /docs/swig/browser
186
+ - text: . It applies verbatim because both frontends lower to the same
187
+ - code [ref=e190]: swig-core
188
+ - text: backend.
189
+ - heading "ArchitectureDirect link to Architecture" [level=2] [ref=e191]:
190
+ - text: Architecture
191
+ - link "Direct link to Architecture" [ref=e192] [cursor=pointer]:
192
+ - /url: "#architecture"
193
+ - text: "#"
194
+ - paragraph [ref=e193]: "Same flow as native swig, with two deltas:"
195
+ - list [ref=e194]:
196
+ - listitem [ref=e195]:
197
+ - text: The entry point is
198
+ - code [ref=e196]: "@rhinostone/swig-twig"
199
+ - text: instead of
200
+ - code [ref=e197]: "@rhinostone/swig"
201
+ - text: .
202
+ - listitem [ref=e198]:
203
+ - text: Your bundler resolves both
204
+ - code [ref=e199]: "@rhinostone/swig-twig"
205
+ - text: and its peer
206
+ - code [ref=e200]: "@rhinostone/swig-core"
207
+ - text: .
208
+ - heading "InstallDirect link to Install" [level=2] [ref=e201]:
209
+ - text: Install
210
+ - link "Direct link to Install" [ref=e202] [cursor=pointer]:
211
+ - /url: "#install"
212
+ - text: "#"
213
+ - generic [ref=e204]:
214
+ - code [ref=e206]:
215
+ - generic [ref=e207]: npm install @rhinostone/swig-twig@alpha @rhinostone/swig-core@alpha
216
+ - button "Copy code to clipboard" [ref=e209] [cursor=pointer]:
217
+ - generic [ref=e210]:
218
+ - img [ref=e211]
219
+ - img [ref=e213]
220
+ - paragraph [ref=e215]:
221
+ - text: During the
222
+ - code [ref=e216]: 2.0.0-alpha.x
223
+ - text: cycle the IR ABI is unstable across minors, so
224
+ - code [ref=e217]: "@rhinostone/swig-twig"
225
+ - text: "'s"
226
+ - code [ref=e218]: peerDependencies
227
+ - text: pin
228
+ - code [ref=e219]: "@rhinostone/swig-core"
229
+ - text: to the
230
+ - strong [ref=e220]: exact matching version
231
+ - text: (no caret). Install both together at the same alpha — mixing minors is not supported.
232
+ - generic [ref=e222]:
233
+ - code [ref=e224]:
234
+ - generic [ref=e225]: // package.json — example consumer during alpha
235
+ - generic [ref=e226]: "{"
236
+ - generic [ref=e227]: "\"dependencies\": {"
237
+ - generic [ref=e228]: "\"@rhinostone/swig-core\": \"2.0.0-alpha.8\","
238
+ - generic [ref=e229]: "\"@rhinostone/swig-twig\": \"2.0.0-alpha.8\""
239
+ - generic [ref=e230]: "}"
240
+ - generic [ref=e231]: "}"
241
+ - button "Copy code to clipboard" [ref=e233] [cursor=pointer]:
242
+ - generic [ref=e234]:
243
+ - img [ref=e235]
244
+ - img [ref=e237]
245
+ - paragraph [ref=e239]:
246
+ - text: At
247
+ - code [ref=e240]: 2.0.0
248
+ - text: stable the pin loosens to
249
+ - code [ref=e241]: ^2.0.0
250
+ - text: .
251
+ - heading "Bundle with esbuildDirect link to Bundle with esbuild" [level=2] [ref=e242]:
252
+ - text: Bundle with esbuild
253
+ - link "Direct link to Bundle with esbuild" [ref=e243] [cursor=pointer]:
254
+ - /url: "#bundle-with-esbuild"
255
+ - text: "#"
256
+ - paragraph [ref=e244]: "Minimal recipe. Two things to wire up:"
257
+ - list [ref=e245]:
258
+ - listitem [ref=e246]:
259
+ - code [ref=e247]: fs
260
+ - text: is stubbed to an empty object (the filesystem loader at
261
+ - code [ref=e248]: "@rhinostone/swig-core/lib/loaders/filesystem.js"
262
+ - text: guards itself when
263
+ - code [ref=e249]: fs.readFileSync
264
+ - text: is missing, so an empty stub is enough to make it a no-op in the browser).
265
+ - listitem [ref=e250]:
266
+ - code [ref=e251]: path
267
+ - text: is aliased to
268
+ - link "path-browserify" [ref=e252] [cursor=pointer]:
269
+ - /url: https://www.npmjs.com/package/path-browserify
270
+ - code [ref=e253]: path-browserify
271
+ - text: because the shared loader normalises paths.
272
+ - paragraph [ref=e254]:
273
+ - text: This mirrors the native-swig build recipe at
274
+ - link "gina-io/swig/Makefile:58" [ref=e255] [cursor=pointer]:
275
+ - /url: https://github.com/gina-io/swig/blob/develop/Makefile#L58
276
+ - text: .
277
+ - generic [ref=e257]:
278
+ - code [ref=e259]:
279
+ - generic [ref=e260]: // entry.js
280
+ - generic [ref=e261]: var twig = require('@rhinostone/swig-twig');
281
+ - generic [ref=e262]: "twig.setDefaults({"
282
+ - generic [ref=e263]: "loader: twig.loaders.memory({"
283
+ - generic [ref=e264]: "'greeting.twig': 'Hello {{ name|upper }}!'"
284
+ - generic [ref=e265]: "})"
285
+ - generic [ref=e266]: "});"
286
+ - generic [ref=e267]: window.twig = twig;
287
+ - button "Copy code to clipboard" [ref=e269] [cursor=pointer]:
288
+ - generic [ref=e270]:
289
+ - img [ref=e271]
290
+ - img [ref=e273]
291
+ - generic [ref=e276]:
292
+ - code [ref=e278]:
293
+ - generic [ref=e279]: // stubs/fs.js — empty stub, turns the filesystem loader into a no-op
294
+ - generic [ref=e280]: "module.exports = {};"
295
+ - button "Copy code to clipboard" [ref=e282] [cursor=pointer]:
296
+ - generic [ref=e283]:
297
+ - img [ref=e284]
298
+ - img [ref=e286]
299
+ - generic [ref=e289]:
300
+ - code [ref=e291]:
301
+ - generic [ref=e292]: // build.js
302
+ - generic [ref=e293]: "require('esbuild').build({"
303
+ - generic [ref=e294]: "entryPoints: ['entry.js'],"
304
+ - generic [ref=e295]: "bundle: true,"
305
+ - generic [ref=e296]: "format: 'iife',"
306
+ - generic [ref=e297]: "outfile: 'dist/browser.js',"
307
+ - generic [ref=e298]: "alias: {"
308
+ - generic [ref=e299]: "fs: './stubs/fs.js',"
309
+ - generic [ref=e300]: "path: 'path-browserify'"
310
+ - generic [ref=e301]: "},"
311
+ - generic [ref=e302]: "minify: true,"
312
+ - generic [ref=e303]: "sourcemap: true"
313
+ - generic [ref=e304]: "}).catch(function (err) { console.error(err); process.exit(1); });"
314
+ - button "Copy code to clipboard" [ref=e306] [cursor=pointer]:
315
+ - generic [ref=e307]:
316
+ - img [ref=e308]
317
+ - img [ref=e310]
318
+ - generic [ref=e313]:
319
+ - code [ref=e315]:
320
+ - generic [ref=e316]: node build.js
321
+ - button "Copy code to clipboard" [ref=e318] [cursor=pointer]:
322
+ - generic [ref=e319]:
323
+ - img [ref=e320]
324
+ - img [ref=e322]
325
+ - paragraph [ref=e324]:
326
+ - text: Load it with a
327
+ - code [ref=e325]: <script>
328
+ - text: "tag:"
329
+ - generic [ref=e327]:
330
+ - code [ref=e329]:
331
+ - generic [ref=e330]: <!doctype html>
332
+ - generic [ref=e331]: <html>
333
+ - generic [ref=e332]: <body>
334
+ - generic [ref=e333]: <div id="out"></div>
335
+ - generic [ref=e334]:
336
+ - text: <script src="dist/browser.js">
337
+ - text: </script>
338
+ - generic [ref=e335]: <script>
339
+ - generic [ref=e336]: "var out = window.twig.render('Hello {{ name|upper }}!', {"
340
+ - generic [ref=e337]: "locals: { name: 'world' }"
341
+ - generic [ref=e338]: "});"
342
+ - generic [ref=e339]: document.querySelector('#out').textContent = out;
343
+ - generic [ref=e340]: // → "Hello WORLD!"
344
+ - generic [ref=e341]: </script>
345
+ - generic [ref=e342]: </body>
346
+ - generic [ref=e343]: </html>
347
+ - button "Copy code to clipboard" [ref=e345] [cursor=pointer]:
348
+ - generic [ref=e346]:
349
+ - img [ref=e347]
350
+ - img [ref=e349]
351
+ - paragraph [ref=e351]:
352
+ - strong [ref=e352]: Other bundlers follow the same shape.
353
+ - link "Vite" [ref=e353] [cursor=pointer]:
354
+ - /url: https://vitejs.dev/
355
+ - text: and
356
+ - link "Rollup" [ref=e354] [cursor=pointer]:
357
+ - /url: https://rollupjs.org/
358
+ - text: use
359
+ - code [ref=e355]: resolve.alias
360
+ - text: (Vite) or the
361
+ - link "@rollup/plugin-alias" [ref=e356] [cursor=pointer]:
362
+ - /url: https://www.npmjs.com/package/@rollup/plugin-alias
363
+ - code [ref=e357]: "@rollup/plugin-alias"
364
+ - text: plugin.
365
+ - link "Webpack" [ref=e358] [cursor=pointer]:
366
+ - /url: https://webpack.js.org/
367
+ - text: uses
368
+ - code [ref=e359]: resolve.alias
369
+ - text: +
370
+ - code [ref=e360]: "resolve.fallback: { fs: false }"
371
+ - text: ". The requirement is always: stub"
372
+ - code [ref=e361]: fs
373
+ - text: ", alias"
374
+ - code [ref=e362]: path
375
+ - text: .
376
+ - heading "Extends + includeDirect link to Extends + include" [level=2] [ref=e363]:
377
+ - text: Extends + include
378
+ - link "Direct link to Extends + include" [ref=e364] [cursor=pointer]:
379
+ - /url: "#extends--include"
380
+ - text: "#"
381
+ - paragraph [ref=e365]:
382
+ - text: For templates using
383
+ - code [ref=e366]: "{% extends %}"
384
+ - text: or
385
+ - code [ref=e367]: "{% include %}"
386
+ - text: ", prime the memory loader with every referenced file before rendering:"
387
+ - generic [ref=e369]:
388
+ - code [ref=e371]:
389
+ - generic [ref=e372]: "twig.setDefaults({"
390
+ - generic [ref=e373]: "loader: twig.loaders.memory({"
391
+ - generic [ref=e374]: "'layout.twig': '<!doctype html><title>{{ title }}</title>{% block body %}{% endblock %}',"
392
+ - generic [ref=e375]: "'page.twig': '{% extends \"layout.twig\" %}{% block body %}Hello {{ name|upper }}!{% endblock %}'"
393
+ - generic [ref=e376]: "})"
394
+ - generic [ref=e377]: "});"
395
+ - generic [ref=e378]: "twig.renderFile('page.twig', { title: 'Hi', name: 'world' });"
396
+ - generic [ref=e379]: // → "<!doctype html><title>Hi</title>Hello WORLD!"
397
+ - generic [ref=e380]:
398
+ - button "Toggle word wrap" [ref=e381] [cursor=pointer]:
399
+ - img [ref=e382]
400
+ - button "Copy code to clipboard" [ref=e384] [cursor=pointer]:
401
+ - generic [ref=e385]:
402
+ - img [ref=e386]
403
+ - img [ref=e388]
404
+ - paragraph [ref=e390]:
405
+ - text: The loader contract is identical across frontends — see
406
+ - link "Loaders" [ref=e391] [cursor=pointer]:
407
+ - /url: /docs/swig/loaders
408
+ - text: .
409
+ - heading "AOT pre-compilationDirect link to AOT pre-compilation" [level=2] [ref=e392]:
410
+ - text: AOT pre-compilation
411
+ - link "Direct link to AOT pre-compilation" [ref=e393] [cursor=pointer]:
412
+ - /url: "#aot-pre-compilation"
413
+ - text: "#"
414
+ - paragraph [ref=e394]:
415
+ - text: If bundle size or cold-render latency matters, pre-compile Twig templates to plain JavaScript at build time, ship only the runtime (the compiled
416
+ - code [ref=e395]: _output += …
417
+ - text: functions) to the browser, and skip the parser entirely. The flow is identical to native swig's — see
418
+ - link "Swig Browser Usage → Pre-compile + prime + render" [ref=e396] [cursor=pointer]:
419
+ - /url: /docs/swig/browser#pre-compile--prime--render
420
+ - text: . The compiled output is the same shape regardless of which frontend produced it, because both lower to
421
+ - code [ref=e397]: swig-core
422
+ - text: IR before codegen.
423
+ - heading "Bundle sizeDirect link to Bundle size" [level=2] [ref=e398]:
424
+ - text: Bundle size
425
+ - link "Direct link to Bundle size" [ref=e399] [cursor=pointer]:
426
+ - /url: "#bundle-size"
427
+ - text: "#"
428
+ - paragraph [ref=e400]:
429
+ - text: As of
430
+ - code [ref=e401]: 2.0.0-alpha.8
431
+ - text: ", a minified browser bundle of"
432
+ - code [ref=e402]: "@rhinostone/swig-twig"
433
+ - text: plus its transitive
434
+ - code [ref=e403]: "@rhinostone/swig-core"
435
+ - text: dependency weighs approximately
436
+ - strong [ref=e404]: 82 KB minified
437
+ - text: (~27 KB gzipped) via esbuild
438
+ - code [ref=e405]: "--minify --format=iife"
439
+ - text: — measured against an entry point that imports the default Twig instance, wires up a memory loader, and exposes it on
440
+ - code [ref=e406]: window.twig
441
+ - text: .
442
+ - paragraph [ref=e407]: For perf-critical paths, prefer AOT pre-compilation (previous section) — the runtime footprint drops substantially because the lexer and parser are not shipped.
443
+ - heading "Security modelDirect link to Security model" [level=2] [ref=e408]:
444
+ - text: Security model
445
+ - link "Direct link to Security model" [ref=e409] [cursor=pointer]:
446
+ - /url: "#security-model"
447
+ - text: "#"
448
+ - paragraph [ref=e410]:
449
+ - code [ref=e411]: "@rhinostone/swig-twig"
450
+ - text: inherits every security guarantee of
451
+ - code [ref=e412]: "@rhinostone/swig-core"
452
+ - text: ":"
453
+ - list [ref=e413]:
454
+ - listitem [ref=e414]:
455
+ - strong [ref=e415]: Autoescape is on by default.
456
+ - text: HTML entities are escaped on every
457
+ - code [ref=e416]: "{{ … }}"
458
+ - text: output unless the expression is explicitly marked
459
+ - code [ref=e417]: safe
460
+ - text: (built-in filter) or produced by a user filter declaring
461
+ - code [ref=e418]: .safe = true
462
+ - text: .
463
+ - listitem [ref=e419]:
464
+ - strong [ref=e420]:
465
+ - text: CVE-2023-25345
466
+ - code [ref=e421]: _dangerousProps
467
+ - text: guards.
468
+ - code [ref=e422]: __proto__
469
+ - text: ","
470
+ - code [ref=e423]: constructor
471
+ - text: ", and"
472
+ - code [ref=e424]: prototype
473
+ - text: are rejected at parse time in every context that writes to
474
+ - code [ref=e425]: _ctx
475
+ - text: ": dotted access, bracket-notation with string literals,"
476
+ - code [ref=e426]: "{% set %}"
477
+ - text: LHS,
478
+ - code [ref=e427]: "{% for %}"
479
+ - text: loop variables,
480
+ - code [ref=e428]: "{% macro %}"
481
+ - text: names, and
482
+ - code [ref=e429]: "{% import %}"
483
+ - text: aliases. The guards are frontend-agnostic — both native swig and Twig run them.
484
+ - listitem [ref=e430]:
485
+ - strong [ref=e431]: Template source is trusted.
486
+ - text: Same model as upstream Twig, Jinja2, and Django. Never pass user-controlled strings directly to
487
+ - code [ref=e432]: twig.render(source, locals)
488
+ - text: ; keep locals data-only.
489
+ - paragraph [ref=e433]:
490
+ - text: See
491
+ - link "Security — the parser is the boundary" [ref=e434] [cursor=pointer]:
492
+ - /url: /docs/swig/security#the-parser-is-the-boundary
493
+ - text: for the full story.
494
+ - heading "GotchasDirect link to Gotchas" [level=2] [ref=e435]:
495
+ - text: Gotchas
496
+ - link "Direct link to Gotchas" [ref=e436] [cursor=pointer]:
497
+ - /url: "#gotchas"
498
+ - text: "#"
499
+ - list [ref=e437]:
500
+ - listitem [ref=e438]:
501
+ - strong [ref=e439]: Filesystem loader throws in the browser.
502
+ - text: Always call
503
+ - code [ref=e440]: "twig.setDefaults({ loader: twig.loaders.memory({}) })"
504
+ - text: at module init time — before any render.
505
+ - listitem [ref=e441]:
506
+ - strong [ref=e442]: Peer-dependency pinning.
507
+ - text: During alpha,
508
+ - code [ref=e443]: "@rhinostone/swig-core"
509
+ - text: and
510
+ - code [ref=e444]: "@rhinostone/swig-twig"
511
+ - text: must be installed at the
512
+ - strong [ref=e445]: exact same version
513
+ - text: . Bundlers that rely on npm's
514
+ - code [ref=e446]: node_modules/@rhinostone/swig-core
515
+ - text: symlink (as in a workspace dev checkout) can mask a missing
516
+ - code [ref=e447]: dependencies
517
+ - text: declaration. Verify by bundling against a fresh
518
+ - code [ref=e448]: node_modules
519
+ - text: .
520
+ - listitem [ref=e449]:
521
+ - strong [ref=e450]: Cache persistence.
522
+ - text: The compiled-template cache lives on the Twig instance — reset on page reload. Hot-reload consumers should call
523
+ - code [ref=e451]: twig.invalidateCache()
524
+ - text: before re-priming.
525
+ - listitem [ref=e452]:
526
+ - strong [ref=e453]:
527
+ - code [ref=e454]: swig run
528
+ - text: is not a sandbox.
529
+ - text: Same warning as the native frontend — the function body is
530
+ - code [ref=e455]: eval
531
+ - text: "'d. Never hand user-controlled strings to it."
532
+ - heading "Gina's browser buildDirect link to Gina's browser build" [level=2] [ref=e456]:
533
+ - text: Gina's browser build
534
+ - link "Direct link to Gina's browser build" [ref=e457] [cursor=pointer]:
535
+ - /url: "#ginas-browser-build"
536
+ - text: "#"
537
+ - paragraph [ref=e458]:
538
+ - text: Gina's vendored browser build at
539
+ - code [ref=e459]: framework/v*/core/deps/swig-client/swig.js
540
+ - text: currently ships the native-swig bundle only. A vendored swig-twig bundle is scheduled for the
541
+ - code [ref=e460]: 2.0.0
542
+ - text: stable release; track progress in the
543
+ - link "project roadmap" [ref=e461] [cursor=pointer]:
544
+ - /url: https://github.com/gina-io/swig/blob/develop/ROADMAP.md
545
+ - text: .
546
+ - generic [ref=e462]:
547
+ - generic [ref=e463]:
548
+ - generic [ref=e464]: Was this page helpful?
549
+ - generic [ref=e465]:
550
+ - button "Yes, this was helpful" [ref=e466] [cursor=pointer]:
551
+ - generic [ref=e467]: 👍
552
+ - generic [ref=e468]: –
553
+ - button "No, this was not helpful" [ref=e469] [cursor=pointer]:
554
+ - generic [ref=e470]: 👎
555
+ - generic [ref=e471]: –
556
+ - link "Edit this page" [ref=e475] [cursor=pointer]:
557
+ - /url: https://github.com/gina-io/docs/tree/main/docs/swig/twig/browser.mdx
558
+ - img [ref=e476]
559
+ - text: Edit this page
560
+ - navigation "Docs pages" [ref=e480]:
561
+ - link "Previous « From upstream Twig" [ref=e481] [cursor=pointer]:
562
+ - /url: /docs/swig/twig/migration
563
+ - generic [ref=e482]: Previous
564
+ - generic [ref=e483]: « From upstream Twig
565
+ - link "Next Migration Guide »" [ref=e484] [cursor=pointer]:
566
+ - /url: /docs/swig/migration
567
+ - generic [ref=e485]: Next
568
+ - generic [ref=e486]: Migration Guide »
569
+ - list [ref=e490]:
570
+ - listitem [ref=e491]:
571
+ - link "Architecture" [ref=e492] [cursor=pointer]:
572
+ - /url: "#architecture"
573
+ - listitem [ref=e493]:
574
+ - link "Install" [ref=e494] [cursor=pointer]:
575
+ - /url: "#install"
576
+ - listitem [ref=e495]:
577
+ - link "Bundle with esbuild" [ref=e496] [cursor=pointer]:
578
+ - /url: "#bundle-with-esbuild"
579
+ - listitem [ref=e497]:
580
+ - link "Extends + include" [ref=e498] [cursor=pointer]:
581
+ - /url: "#extends--include"
582
+ - listitem [ref=e499]:
583
+ - link "AOT pre-compilation" [ref=e500] [cursor=pointer]:
584
+ - /url: "#aot-pre-compilation"
585
+ - listitem [ref=e501]:
586
+ - link "Bundle size" [ref=e502] [cursor=pointer]:
587
+ - /url: "#bundle-size"
588
+ - listitem [ref=e503]:
589
+ - link "Security model" [ref=e504] [cursor=pointer]:
590
+ - /url: "#security-model"
591
+ - listitem [ref=e505]:
592
+ - link "Gotchas" [ref=e506] [cursor=pointer]:
593
+ - /url: "#gotchas"
594
+ - listitem [ref=e507]:
595
+ - link "Gina's browser build" [ref=e508] [cursor=pointer]:
596
+ - /url: "#ginas-browser-build"
597
+ - contentinfo [ref=e509]:
598
+ - generic [ref=e510]:
599
+ - generic [ref=e511]:
600
+ - generic [ref=e512]:
601
+ - generic [ref=e513]: Docs
602
+ - list [ref=e514]:
603
+ - listitem [ref=e515]:
604
+ - link "Getting Started" [ref=e516] [cursor=pointer]:
605
+ - /url: /docs/intro
606
+ - generic [ref=e517]:
607
+ - generic [ref=e518]: Community
608
+ - list [ref=e519]:
609
+ - listitem [ref=e520]:
610
+ - link "GitHub Issues(opens in new tab)" [ref=e521] [cursor=pointer]:
611
+ - /url: https://github.com/gina-io/gina/issues
612
+ - text: GitHub Issues
613
+ - img "(opens in new tab)" [ref=e522]
614
+ - listitem [ref=e524]:
615
+ - link "GitHub Discussions(opens in new tab)" [ref=e525] [cursor=pointer]:
616
+ - /url: https://github.com/gina-io/gina/discussions
617
+ - text: GitHub Discussions
618
+ - img "(opens in new tab)" [ref=e526]
619
+ - listitem [ref=e528]:
620
+ - link "Support Gina" [ref=e529] [cursor=pointer]:
621
+ - /url: /docs/support
622
+ - generic [ref=e530]:
623
+ - generic [ref=e531]: More
624
+ - list [ref=e532]:
625
+ - listitem [ref=e533]:
626
+ - link "npm(opens in new tab)" [ref=e534] [cursor=pointer]:
627
+ - /url: https://www.npmjs.com/package/gina
628
+ - text: npm
629
+ - img "(opens in new tab)" [ref=e535]
630
+ - generic [ref=e538]: Copyright © 2009-2026 gina-io.
631
+ - button "Collapse sidebar" [ref=e540] [cursor=pointer]:
632
+ - img [ref=e541]