@funkai/cli 0.1.0 → 0.1.2

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 (42) hide show
  1. package/.turbo/turbo-build.log +15 -16
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +1 -1
  4. package/dist/index.mjs +5018 -298
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +5 -5
  7. package/src/commands/generate.ts +4 -62
  8. package/src/commands/prompts/generate.ts +75 -48
  9. package/src/commands/prompts/lint.ts +62 -44
  10. package/src/commands/prompts/setup.ts +2 -7
  11. package/src/commands/validate.ts +4 -27
  12. package/src/index.ts +6 -1
  13. package/src/lib/prompts/codegen.ts +12 -16
  14. package/src/lib/prompts/flatten.ts +2 -7
  15. package/src/lib/prompts/frontmatter.ts +28 -33
  16. package/src/lib/prompts/lint.ts +8 -8
  17. package/src/lib/prompts/paths.ts +5 -5
  18. package/src/lib/prompts/pipeline.ts +6 -7
  19. package/.turbo/turbo-test$colon$coverage.log +0 -36
  20. package/.turbo/turbo-test.log +0 -26
  21. package/.turbo/turbo-typecheck.log +0 -4
  22. package/coverage/lcov-report/base.css +0 -224
  23. package/coverage/lcov-report/block-navigation.js +0 -87
  24. package/coverage/lcov-report/commands/create.ts.html +0 -208
  25. package/coverage/lcov-report/commands/generate.ts.html +0 -388
  26. package/coverage/lcov-report/commands/index.html +0 -161
  27. package/coverage/lcov-report/commands/lint.ts.html +0 -331
  28. package/coverage/lcov-report/commands/setup.ts.html +0 -493
  29. package/coverage/lcov-report/favicon.png +0 -0
  30. package/coverage/lcov-report/index.html +0 -131
  31. package/coverage/lcov-report/lib/codegen.ts.html +0 -805
  32. package/coverage/lcov-report/lib/extract-variables.ts.html +0 -181
  33. package/coverage/lcov-report/lib/flatten.ts.html +0 -385
  34. package/coverage/lcov-report/lib/frontmatter.ts.html +0 -487
  35. package/coverage/lcov-report/lib/index.html +0 -191
  36. package/coverage/lcov-report/lib/lint.ts.html +0 -307
  37. package/coverage/lcov-report/lib/paths.ts.html +0 -487
  38. package/coverage/lcov-report/prettify.css +0 -1
  39. package/coverage/lcov-report/prettify.js +0 -2
  40. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  41. package/coverage/lcov-report/sorter.js +0 -210
  42. package/coverage/lcov.info +0 -749
@@ -1,181 +0,0 @@
1
-
2
- <!doctype html>
3
- <html lang="en">
4
-
5
- <head>
6
- <title>Code coverage report for lib/extract-variables.ts</title>
7
- <meta charset="utf-8" />
8
- <link rel="stylesheet" href="../prettify.css" />
9
- <link rel="stylesheet" href="../base.css" />
10
- <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
- <meta name="viewport" content="width=device-width, initial-scale=1" />
12
- <style type='text/css'>
13
- .coverage-summary .sorter {
14
- background-image: url(../sort-arrow-sprite.png);
15
- }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <div class='wrapper'>
21
- <div class='pad1'>
22
- <h1><a href="../index.html">All files</a> / <a href="index.html">lib</a> extract-variables.ts</h1>
23
- <div class='clearfix'>
24
-
25
- <div class='fl pad1y space-right2'>
26
- <span class="strong">100% </span>
27
- <span class="quiet">Statements</span>
28
- <span class='fraction'>10/10</span>
29
- </div>
30
-
31
-
32
- <div class='fl pad1y space-right2'>
33
- <span class="strong">75% </span>
34
- <span class="quiet">Branches</span>
35
- <span class='fraction'>3/4</span>
36
- </div>
37
-
38
-
39
- <div class='fl pad1y space-right2'>
40
- <span class="strong">100% </span>
41
- <span class="quiet">Functions</span>
42
- <span class='fraction'>2/2</span>
43
- </div>
44
-
45
-
46
- <div class='fl pad1y space-right2'>
47
- <span class="strong">100% </span>
48
- <span class="quiet">Lines</span>
49
- <span class='fraction'>10/10</span>
50
- </div>
51
-
52
-
53
- </div>
54
- <p class="quiet">
55
- Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
- </p>
57
- <template id="filterTemplate">
58
- <div class="quiet">
59
- Filter:
60
- <input type="search" id="fileSearch">
61
- </div>
62
- </template>
63
- </div>
64
- <div class='status-line high'></div>
65
- <pre><table class="coverage">
66
- <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
- <a name='L2'></a><a href='#L2'>2</a>
68
- <a name='L3'></a><a href='#L3'>3</a>
69
- <a name='L4'></a><a href='#L4'>4</a>
70
- <a name='L5'></a><a href='#L5'>5</a>
71
- <a name='L6'></a><a href='#L6'>6</a>
72
- <a name='L7'></a><a href='#L7'>7</a>
73
- <a name='L8'></a><a href='#L8'>8</a>
74
- <a name='L9'></a><a href='#L9'>9</a>
75
- <a name='L10'></a><a href='#L10'>10</a>
76
- <a name='L11'></a><a href='#L11'>11</a>
77
- <a name='L12'></a><a href='#L12'>12</a>
78
- <a name='L13'></a><a href='#L13'>13</a>
79
- <a name='L14'></a><a href='#L14'>14</a>
80
- <a name='L15'></a><a href='#L15'>15</a>
81
- <a name='L16'></a><a href='#L16'>16</a>
82
- <a name='L17'></a><a href='#L17'>17</a>
83
- <a name='L18'></a><a href='#L18'>18</a>
84
- <a name='L19'></a><a href='#L19'>19</a>
85
- <a name='L20'></a><a href='#L20'>20</a>
86
- <a name='L21'></a><a href='#L21'>21</a>
87
- <a name='L22'></a><a href='#L22'>22</a>
88
- <a name='L23'></a><a href='#L23'>23</a>
89
- <a name='L24'></a><a href='#L24'>24</a>
90
- <a name='L25'></a><a href='#L25'>25</a>
91
- <a name='L26'></a><a href='#L26'>26</a>
92
- <a name='L27'></a><a href='#L27'>27</a>
93
- <a name='L28'></a><a href='#L28'>28</a>
94
- <a name='L29'></a><a href='#L29'>29</a>
95
- <a name='L30'></a><a href='#L30'>30</a>
96
- <a name='L31'></a><a href='#L31'>31</a>
97
- <a name='L32'></a><a href='#L32'>32</a>
98
- <a name='L33'></a><a href='#L33'>33</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
99
- <span class="cline-any cline-neutral">&nbsp;</span>
100
- <span class="cline-any cline-yes">1x</span>
101
- <span class="cline-any cline-neutral">&nbsp;</span>
102
- <span class="cline-any cline-neutral">&nbsp;</span>
103
- <span class="cline-any cline-neutral">&nbsp;</span>
104
- <span class="cline-any cline-neutral">&nbsp;</span>
105
- <span class="cline-any cline-neutral">&nbsp;</span>
106
- <span class="cline-any cline-neutral">&nbsp;</span>
107
- <span class="cline-any cline-neutral">&nbsp;</span>
108
- <span class="cline-any cline-neutral">&nbsp;</span>
109
- <span class="cline-any cline-neutral">&nbsp;</span>
110
- <span class="cline-any cline-neutral">&nbsp;</span>
111
- <span class="cline-any cline-neutral">&nbsp;</span>
112
- <span class="cline-any cline-yes">11x</span>
113
- <span class="cline-any cline-yes">11x</span>
114
- <span class="cline-any cline-yes">11x</span>
115
- <span class="cline-any cline-neutral">&nbsp;</span>
116
- <span class="cline-any cline-yes">11x</span>
117
- <span class="cline-any cline-neutral">&nbsp;</span>
118
- <span class="cline-any cline-yes">12x</span>
119
- <span class="cline-any cline-neutral">&nbsp;</span>
120
- <span class="cline-any cline-yes">12x</span>
121
- <span class="cline-any cline-yes">3x</span>
122
- <span class="cline-any cline-neutral">&nbsp;</span>
123
- <span class="cline-any cline-neutral">&nbsp;</span>
124
- <span class="cline-any cline-yes">9x</span>
125
- <span class="cline-any cline-neutral">&nbsp;</span>
126
- <span class="cline-any cline-neutral">&nbsp;</span>
127
- <span class="cline-any cline-neutral">&nbsp;</span>
128
- <span class="cline-any cline-yes">11x</span>
129
- <span class="cline-any cline-neutral">&nbsp;</span>
130
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { Liquid } from "liquidjs";
131
- &nbsp;
132
- const DANGEROUS_NAMES = new Set(["constructor", "__proto__", "prototype"]);
133
- &nbsp;
134
- /**
135
- * Extract top-level variable names from a Liquid template string.
136
- *
137
- * Uses LiquidJS's built-in `variablesSync` to parse the template AST
138
- * and extract all referenced variable names. Only returns the root
139
- * variable name (e.g. `user` from `{{ user.name }}`).
140
- *
141
- * @throws {Error} If a variable name is dangerous (e.g. `__proto__`)
142
- */
143
- export function extractVariables(template: string): string[] {
144
- const engine = new Liquid();
145
- const parsed = engine.parse(template);
146
- const variables = engine.variablesSync(parsed);
147
- &nbsp;
148
- const roots = new Set(
149
- variables.map((variable) =&gt; {
150
- const root = Array.isArray(variable) ? <span class="branch-0 cbranch-no" title="branch not covered" >String(variable[0]) : S</span>tring(variable);
151
- &nbsp;
152
- if (DANGEROUS_NAMES.has(root)) {
153
- throw new Error(`Dangerous variable name "${root}" is not allowed in prompt templates`);
154
- }
155
- &nbsp;
156
- return root;
157
- }),
158
- );
159
- &nbsp;
160
- return [...roots].toSorted();
161
- }
162
- &nbsp;</pre></td></tr></table></pre>
163
-
164
- <div class='push'></div><!-- for sticky footer -->
165
- </div><!-- /wrapper -->
166
- <div class='footer quiet pad2 space-top1 center small'>
167
- Code coverage generated by
168
- <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
169
- at 2026-03-13T00:28:56.897Z
170
- </div>
171
- <script src="../prettify.js"></script>
172
- <script>
173
- window.onload = function () {
174
- prettyPrint();
175
- };
176
- </script>
177
- <script src="../sorter.js"></script>
178
- <script src="../block-navigation.js"></script>
179
- </body>
180
- </html>
181
-
@@ -1,385 +0,0 @@
1
-
2
- <!doctype html>
3
- <html lang="en">
4
-
5
- <head>
6
- <title>Code coverage report for lib/flatten.ts</title>
7
- <meta charset="utf-8" />
8
- <link rel="stylesheet" href="../prettify.css" />
9
- <link rel="stylesheet" href="../base.css" />
10
- <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
- <meta name="viewport" content="width=device-width, initial-scale=1" />
12
- <style type='text/css'>
13
- .coverage-summary .sorter {
14
- background-image: url(../sort-arrow-sprite.png);
15
- }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <div class='wrapper'>
21
- <div class='pad1'>
22
- <h1><a href="../index.html">All files</a> / <a href="index.html">lib</a> flatten.ts</h1>
23
- <div class='clearfix'>
24
-
25
- <div class='fl pad1y space-right2'>
26
- <span class="strong">100% </span>
27
- <span class="quiet">Statements</span>
28
- <span class='fraction'>34/34</span>
29
- </div>
30
-
31
-
32
- <div class='fl pad1y space-right2'>
33
- <span class="strong">100% </span>
34
- <span class="quiet">Branches</span>
35
- <span class='fraction'>4/4</span>
36
- </div>
37
-
38
-
39
- <div class='fl pad1y space-right2'>
40
- <span class="strong">100% </span>
41
- <span class="quiet">Functions</span>
42
- <span class='fraction'>11/11</span>
43
- </div>
44
-
45
-
46
- <div class='fl pad1y space-right2'>
47
- <span class="strong">100% </span>
48
- <span class="quiet">Lines</span>
49
- <span class='fraction'>32/32</span>
50
- </div>
51
-
52
-
53
- </div>
54
- <p class="quiet">
55
- Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
- </p>
57
- <template id="filterTemplate">
58
- <div class="quiet">
59
- Filter:
60
- <input type="search" id="fileSearch">
61
- </div>
62
- </template>
63
- </div>
64
- <div class='status-line high'></div>
65
- <pre><table class="coverage">
66
- <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
- <a name='L2'></a><a href='#L2'>2</a>
68
- <a name='L3'></a><a href='#L3'>3</a>
69
- <a name='L4'></a><a href='#L4'>4</a>
70
- <a name='L5'></a><a href='#L5'>5</a>
71
- <a name='L6'></a><a href='#L6'>6</a>
72
- <a name='L7'></a><a href='#L7'>7</a>
73
- <a name='L8'></a><a href='#L8'>8</a>
74
- <a name='L9'></a><a href='#L9'>9</a>
75
- <a name='L10'></a><a href='#L10'>10</a>
76
- <a name='L11'></a><a href='#L11'>11</a>
77
- <a name='L12'></a><a href='#L12'>12</a>
78
- <a name='L13'></a><a href='#L13'>13</a>
79
- <a name='L14'></a><a href='#L14'>14</a>
80
- <a name='L15'></a><a href='#L15'>15</a>
81
- <a name='L16'></a><a href='#L16'>16</a>
82
- <a name='L17'></a><a href='#L17'>17</a>
83
- <a name='L18'></a><a href='#L18'>18</a>
84
- <a name='L19'></a><a href='#L19'>19</a>
85
- <a name='L20'></a><a href='#L20'>20</a>
86
- <a name='L21'></a><a href='#L21'>21</a>
87
- <a name='L22'></a><a href='#L22'>22</a>
88
- <a name='L23'></a><a href='#L23'>23</a>
89
- <a name='L24'></a><a href='#L24'>24</a>
90
- <a name='L25'></a><a href='#L25'>25</a>
91
- <a name='L26'></a><a href='#L26'>26</a>
92
- <a name='L27'></a><a href='#L27'>27</a>
93
- <a name='L28'></a><a href='#L28'>28</a>
94
- <a name='L29'></a><a href='#L29'>29</a>
95
- <a name='L30'></a><a href='#L30'>30</a>
96
- <a name='L31'></a><a href='#L31'>31</a>
97
- <a name='L32'></a><a href='#L32'>32</a>
98
- <a name='L33'></a><a href='#L33'>33</a>
99
- <a name='L34'></a><a href='#L34'>34</a>
100
- <a name='L35'></a><a href='#L35'>35</a>
101
- <a name='L36'></a><a href='#L36'>36</a>
102
- <a name='L37'></a><a href='#L37'>37</a>
103
- <a name='L38'></a><a href='#L38'>38</a>
104
- <a name='L39'></a><a href='#L39'>39</a>
105
- <a name='L40'></a><a href='#L40'>40</a>
106
- <a name='L41'></a><a href='#L41'>41</a>
107
- <a name='L42'></a><a href='#L42'>42</a>
108
- <a name='L43'></a><a href='#L43'>43</a>
109
- <a name='L44'></a><a href='#L44'>44</a>
110
- <a name='L45'></a><a href='#L45'>45</a>
111
- <a name='L46'></a><a href='#L46'>46</a>
112
- <a name='L47'></a><a href='#L47'>47</a>
113
- <a name='L48'></a><a href='#L48'>48</a>
114
- <a name='L49'></a><a href='#L49'>49</a>
115
- <a name='L50'></a><a href='#L50'>50</a>
116
- <a name='L51'></a><a href='#L51'>51</a>
117
- <a name='L52'></a><a href='#L52'>52</a>
118
- <a name='L53'></a><a href='#L53'>53</a>
119
- <a name='L54'></a><a href='#L54'>54</a>
120
- <a name='L55'></a><a href='#L55'>55</a>
121
- <a name='L56'></a><a href='#L56'>56</a>
122
- <a name='L57'></a><a href='#L57'>57</a>
123
- <a name='L58'></a><a href='#L58'>58</a>
124
- <a name='L59'></a><a href='#L59'>59</a>
125
- <a name='L60'></a><a href='#L60'>60</a>
126
- <a name='L61'></a><a href='#L61'>61</a>
127
- <a name='L62'></a><a href='#L62'>62</a>
128
- <a name='L63'></a><a href='#L63'>63</a>
129
- <a name='L64'></a><a href='#L64'>64</a>
130
- <a name='L65'></a><a href='#L65'>65</a>
131
- <a name='L66'></a><a href='#L66'>66</a>
132
- <a name='L67'></a><a href='#L67'>67</a>
133
- <a name='L68'></a><a href='#L68'>68</a>
134
- <a name='L69'></a><a href='#L69'>69</a>
135
- <a name='L70'></a><a href='#L70'>70</a>
136
- <a name='L71'></a><a href='#L71'>71</a>
137
- <a name='L72'></a><a href='#L72'>72</a>
138
- <a name='L73'></a><a href='#L73'>73</a>
139
- <a name='L74'></a><a href='#L74'>74</a>
140
- <a name='L75'></a><a href='#L75'>75</a>
141
- <a name='L76'></a><a href='#L76'>76</a>
142
- <a name='L77'></a><a href='#L77'>77</a>
143
- <a name='L78'></a><a href='#L78'>78</a>
144
- <a name='L79'></a><a href='#L79'>79</a>
145
- <a name='L80'></a><a href='#L80'>80</a>
146
- <a name='L81'></a><a href='#L81'>81</a>
147
- <a name='L82'></a><a href='#L82'>82</a>
148
- <a name='L83'></a><a href='#L83'>83</a>
149
- <a name='L84'></a><a href='#L84'>84</a>
150
- <a name='L85'></a><a href='#L85'>85</a>
151
- <a name='L86'></a><a href='#L86'>86</a>
152
- <a name='L87'></a><a href='#L87'>87</a>
153
- <a name='L88'></a><a href='#L88'>88</a>
154
- <a name='L89'></a><a href='#L89'>89</a>
155
- <a name='L90'></a><a href='#L90'>90</a>
156
- <a name='L91'></a><a href='#L91'>91</a>
157
- <a name='L92'></a><a href='#L92'>92</a>
158
- <a name='L93'></a><a href='#L93'>93</a>
159
- <a name='L94'></a><a href='#L94'>94</a>
160
- <a name='L95'></a><a href='#L95'>95</a>
161
- <a name='L96'></a><a href='#L96'>96</a>
162
- <a name='L97'></a><a href='#L97'>97</a>
163
- <a name='L98'></a><a href='#L98'>98</a>
164
- <a name='L99'></a><a href='#L99'>99</a>
165
- <a name='L100'></a><a href='#L100'>100</a>
166
- <a name='L101'></a><a href='#L101'>101</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
167
- <span class="cline-any cline-neutral">&nbsp;</span>
168
- <span class="cline-any cline-neutral">&nbsp;</span>
169
- <span class="cline-any cline-neutral">&nbsp;</span>
170
- <span class="cline-any cline-yes">1x</span>
171
- <span class="cline-any cline-yes">1x</span>
172
- <span class="cline-any cline-neutral">&nbsp;</span>
173
- <span class="cline-any cline-neutral">&nbsp;</span>
174
- <span class="cline-any cline-neutral">&nbsp;</span>
175
- <span class="cline-any cline-neutral">&nbsp;</span>
176
- <span class="cline-any cline-neutral">&nbsp;</span>
177
- <span class="cline-any cline-neutral">&nbsp;</span>
178
- <span class="cline-any cline-neutral">&nbsp;</span>
179
- <span class="cline-any cline-neutral">&nbsp;</span>
180
- <span class="cline-any cline-neutral">&nbsp;</span>
181
- <span class="cline-any cline-neutral">&nbsp;</span>
182
- <span class="cline-any cline-neutral">&nbsp;</span>
183
- <span class="cline-any cline-neutral">&nbsp;</span>
184
- <span class="cline-any cline-neutral">&nbsp;</span>
185
- <span class="cline-any cline-neutral">&nbsp;</span>
186
- <span class="cline-any cline-yes">18x</span>
187
- <span class="cline-any cline-yes">18x</span>
188
- <span class="cline-any cline-neutral">&nbsp;</span>
189
- <span class="cline-any cline-yes">35x</span>
190
- <span class="cline-any cline-neutral">&nbsp;</span>
191
- <span class="cline-any cline-yes">18x</span>
192
- <span class="cline-any cline-yes">46x</span>
193
- <span class="cline-any cline-yes">33x</span>
194
- <span class="cline-any cline-yes">3x</span>
195
- <span class="cline-any cline-neutral">&nbsp;</span>
196
- <span class="cline-any cline-neutral">&nbsp;</span>
197
- <span class="cline-any cline-neutral">&nbsp;</span>
198
- <span class="cline-any cline-neutral">&nbsp;</span>
199
- <span class="cline-any cline-neutral">&nbsp;</span>
200
- <span class="cline-any cline-yes">30x</span>
201
- <span class="cline-any cline-neutral">&nbsp;</span>
202
- <span class="cline-any cline-neutral">&nbsp;</span>
203
- <span class="cline-any cline-yes">15x</span>
204
- <span class="cline-any cline-neutral">&nbsp;</span>
205
- <span class="cline-any cline-neutral">&nbsp;</span>
206
- <span class="cline-any cline-neutral">&nbsp;</span>
207
- <span class="cline-any cline-neutral">&nbsp;</span>
208
- <span class="cline-any cline-neutral">&nbsp;</span>
209
- <span class="cline-any cline-neutral">&nbsp;</span>
210
- <span class="cline-any cline-yes">27x</span>
211
- <span class="cline-any cline-neutral">&nbsp;</span>
212
- <span class="cline-any cline-yes">27x</span>
213
- <span class="cline-any cline-yes">22x</span>
214
- <span class="cline-any cline-yes">22x</span>
215
- <span class="cline-any cline-yes">22x</span>
216
- <span class="cline-any cline-yes">18x</span>
217
- <span class="cline-any cline-yes">4x</span>
218
- <span class="cline-any cline-yes">22x</span>
219
- <span class="cline-any cline-yes">18x</span>
220
- <span class="cline-any cline-yes">4x</span>
221
- <span class="cline-any cline-neutral">&nbsp;</span>
222
- <span class="cline-any cline-yes">22x</span>
223
- <span class="cline-any cline-neutral">&nbsp;</span>
224
- <span class="cline-any cline-neutral">&nbsp;</span>
225
- <span class="cline-any cline-yes">24x</span>
226
- <span class="cline-any cline-neutral">&nbsp;</span>
227
- <span class="cline-any cline-neutral">&nbsp;</span>
228
- <span class="cline-any cline-neutral">&nbsp;</span>
229
- <span class="cline-any cline-neutral">&nbsp;</span>
230
- <span class="cline-any cline-neutral">&nbsp;</span>
231
- <span class="cline-any cline-neutral">&nbsp;</span>
232
- <span class="cline-any cline-neutral">&nbsp;</span>
233
- <span class="cline-any cline-neutral">&nbsp;</span>
234
- <span class="cline-any cline-neutral">&nbsp;</span>
235
- <span class="cline-any cline-neutral">&nbsp;</span>
236
- <span class="cline-any cline-neutral">&nbsp;</span>
237
- <span class="cline-any cline-neutral">&nbsp;</span>
238
- <span class="cline-any cline-neutral">&nbsp;</span>
239
- <span class="cline-any cline-neutral">&nbsp;</span>
240
- <span class="cline-any cline-neutral">&nbsp;</span>
241
- <span class="cline-any cline-neutral">&nbsp;</span>
242
- <span class="cline-any cline-neutral">&nbsp;</span>
243
- <span class="cline-any cline-yes">27x</span>
244
- <span class="cline-any cline-yes">27x</span>
245
- <span class="cline-any cline-yes">6x</span>
246
- <span class="cline-any cline-neutral">&nbsp;</span>
247
- <span class="cline-any cline-neutral">&nbsp;</span>
248
- <span class="cline-any cline-yes">18x</span>
249
- <span class="cline-any cline-neutral">&nbsp;</span>
250
- <span class="cline-any cline-neutral">&nbsp;</span>
251
- <span class="cline-any cline-neutral">&nbsp;</span>
252
- <span class="cline-any cline-neutral">&nbsp;</span>
253
- <span class="cline-any cline-neutral">&nbsp;</span>
254
- <span class="cline-any cline-yes">18x</span>
255
- <span class="cline-any cline-yes">19x</span>
256
- <span class="cline-any cline-neutral">&nbsp;</span>
257
- <span class="cline-any cline-yes">29x</span>
258
- <span class="cline-any cline-neutral">&nbsp;</span>
259
- <span class="cline-any cline-neutral">&nbsp;</span>
260
- <span class="cline-any cline-neutral">&nbsp;</span>
261
- <span class="cline-any cline-yes">19x</span>
262
- <span class="cline-any cline-neutral">&nbsp;</span>
263
- <span class="cline-any cline-neutral">&nbsp;</span>
264
- <span class="cline-any cline-yes">18x</span>
265
- <span class="cline-any cline-neutral">&nbsp;</span>
266
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { Liquid } from "liquidjs";
267
- import { match } from "ts-pattern";
268
- &nbsp;
269
- // oxlint-disable-next-line security/detect-unsafe-regex -- template parsing, not adversarial input
270
- const RENDER_TAG_RE = /\{%-?\s*render\s+'([^']+)'(?:\s*,\s*(.*?))?\s*-?%\}/g;
271
- const LITERAL_PARAM_RE = /(\w+)\s*:\s*'([^']*)'/g;
272
- &nbsp;
273
- interface RenderTag {
274
- fullMatch: string;
275
- partialName: string;
276
- params: Record&lt;string, string&gt;;
277
- }
278
- &nbsp;
279
- /**
280
- * Parse literal string parameters from a render tag's param string.
281
- *
282
- * Only supports literal string values (e.g. `role: 'Bot'`).
283
- * Throws if a parameter value is a variable reference.
284
- */
285
- function parseParams(raw: string, partialName: string): Record&lt;string, string&gt; {
286
- const params: Record&lt;string, string&gt; = {};
287
- const literalMatches = [...raw.matchAll(LITERAL_PARAM_RE)];
288
- &nbsp;
289
- const allParamNames = [...raw.matchAll(/(\w+)\s*:/g)].map((m) =&gt; m[1]);
290
- &nbsp;
291
- for (const name of allParamNames) {
292
- const literal = literalMatches.find((m) =&gt; m[1] === name);
293
- if (!literal) {
294
- throw new Error(
295
- `Cannot flatten {% render '${partialName}' %}: parameter "${name}" uses a variable reference. ` +
296
- "Only literal string values are supported at codegen time.",
297
- );
298
- }
299
- // oxlint-disable-next-line security/detect-object-injection -- safe: name extracted from regex match on template literal
300
- params[name] = literal[2];
301
- }
302
- &nbsp;
303
- return params;
304
- }
305
- &nbsp;
306
- /**
307
- * Find all `{% render %}` tags in a template string.
308
- */
309
- function parseRenderTags(template: string): RenderTag[] {
310
- const tags: RenderTag[] = [];
311
- &nbsp;
312
- for (const m of template.matchAll(RENDER_TAG_RE)) {
313
- const fullMatch = m[0];
314
- const partialName = m[1];
315
- const rawParams = match(m[2] != null)
316
- .with(true, () =&gt; m[2].trim())
317
- .otherwise(() =&gt; "");
318
- const params = match(rawParams.length &gt; 0)
319
- .with(true, () =&gt; parseParams(rawParams, partialName))
320
- .otherwise(() =&gt; ({}));
321
- &nbsp;
322
- tags.push({ fullMatch, partialName, params });
323
- }
324
- &nbsp;
325
- return tags;
326
- }
327
- &nbsp;
328
- /**
329
- * Flatten `{% render %}` partial tags in a template at codegen time.
330
- *
331
- * Finds all `{% render 'name', key: 'value' %}` tags, reads the
332
- * corresponding partial file, renders it with the literal parameters,
333
- * and replaces the tag with the rendered output.
334
- *
335
- * All other Liquid expressions (`{{ var }}`, `{% if %}`, `{% for %}`)
336
- * are preserved for runtime rendering.
337
- *
338
- * @param template - Template string (frontmatter already stripped).
339
- * @param partialsDirs - Directories to search for partial `.prompt` files.
340
- * @returns Flattened template with all render tags resolved.
341
- */
342
- export function flattenPartials(template: string, partialsDirs: string[]): string {
343
- const tags = parseRenderTags(template);
344
- if (tags.length === 0) {
345
- return template;
346
- }
347
- &nbsp;
348
- const engine = new Liquid({
349
- root: partialsDirs,
350
- partials: partialsDirs,
351
- extname: ".prompt",
352
- });
353
- &nbsp;
354
- const result = tags.reduce((acc, tag) =&gt; {
355
- const rendered = engine.parseAndRenderSync(
356
- `{% render '${tag.partialName}' ${Object.entries(tag.params)
357
- .map(([k, v]) =&gt; `${k}: '${v}'`)
358
- .join(", ")} %}`,
359
- );
360
- &nbsp;
361
- return acc.replace(tag.fullMatch, rendered);
362
- }, template);
363
- &nbsp;
364
- return result;
365
- }
366
- &nbsp;</pre></td></tr></table></pre>
367
-
368
- <div class='push'></div><!-- for sticky footer -->
369
- </div><!-- /wrapper -->
370
- <div class='footer quiet pad2 space-top1 center small'>
371
- Code coverage generated by
372
- <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
373
- at 2026-03-13T00:28:56.897Z
374
- </div>
375
- <script src="../prettify.js"></script>
376
- <script>
377
- window.onload = function () {
378
- prettyPrint();
379
- };
380
- </script>
381
- <script src="../sorter.js"></script>
382
- <script src="../block-navigation.js"></script>
383
- </body>
384
- </html>
385
-