0x-lang 0.1.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/LICENSE +15 -0
- package/README.md +357 -0
- package/dist/ast.d.ts +905 -0
- package/dist/ast.js +3 -0
- package/dist/ast.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +190 -0
- package/dist/cli.js.map +1 -0
- package/dist/compiler.d.ts +6 -0
- package/dist/compiler.js +28 -0
- package/dist/compiler.js.map +1 -0
- package/dist/generators/react.d.ts +4 -0
- package/dist/generators/react.js +2304 -0
- package/dist/generators/react.js.map +1 -0
- package/dist/generators/shared.d.ts +3 -0
- package/dist/generators/shared.js +15 -0
- package/dist/generators/shared.js.map +1 -0
- package/dist/generators/svelte.d.ts +2 -0
- package/dist/generators/svelte.js +607 -0
- package/dist/generators/svelte.js.map +1 -0
- package/dist/generators/vue.d.ts +2 -0
- package/dist/generators/vue.js +660 -0
- package/dist/generators/vue.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +101 -0
- package/dist/init.js.map +1 -0
- package/dist/parser.d.ts +7 -0
- package/dist/parser.js +3586 -0
- package/dist/parser.js.map +1 -0
- package/dist/react/chat.jsx +44 -0
- package/dist/react/counter.jsx +30 -0
- package/dist/react/dashboard.jsx +60 -0
- package/dist/react/ecommerce.jsx +63 -0
- package/dist/react/todo.jsx +38 -0
- package/dist/svelte/chat.svelte +41 -0
- package/dist/svelte/counter.svelte +27 -0
- package/dist/svelte/dashboard.svelte +57 -0
- package/dist/svelte/ecommerce.svelte +59 -0
- package/dist/svelte/todo.svelte +35 -0
- package/dist/tokenizer.d.ts +13 -0
- package/dist/tokenizer.js +318 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/validator.d.ts +16 -0
- package/dist/validator.js +315 -0
- package/dist/validator.js.map +1 -0
- package/dist/vue/chat.vue +44 -0
- package/dist/vue/counter.vue +30 -0
- package/dist/vue/dashboard.vue +60 -0
- package/dist/vue/ecommerce.vue +62 -0
- package/dist/vue/todo.vue +38 -0
- package/package.json +63 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
// 0x → Vue 3 (Composition API) Code Generator
|
|
2
|
+
import { SIZE_MAP, unquote, capitalize } from './shared.js';
|
|
3
|
+
import { generateBackendCode } from './react.js';
|
|
4
|
+
function newCtx() {
|
|
5
|
+
return { imports: new Set(), states: new Map(), styles: new Map() };
|
|
6
|
+
}
|
|
7
|
+
export function generateVue(ast) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
for (const node of ast) {
|
|
10
|
+
if (node.type === 'Page' || node.type === 'Component' || node.type === 'App') {
|
|
11
|
+
parts.push(generateVueComponent(node));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
const backend = generateBackendCode(node);
|
|
15
|
+
if (backend)
|
|
16
|
+
parts.push(backend);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const code = parts.join('\n\n');
|
|
20
|
+
return {
|
|
21
|
+
code,
|
|
22
|
+
filename: 'Component.vue',
|
|
23
|
+
imports: [],
|
|
24
|
+
lineCount: code.split('\n').length,
|
|
25
|
+
tokenCount: code.split(/\s+/).length,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function generateVueComponent(node) {
|
|
29
|
+
const c = newCtx();
|
|
30
|
+
for (const child of node.body) {
|
|
31
|
+
if (child.type === 'StateDecl')
|
|
32
|
+
c.states.set(child.name, child);
|
|
33
|
+
if (child.type === 'StyleDecl')
|
|
34
|
+
c.styles.set(child.name, child);
|
|
35
|
+
}
|
|
36
|
+
// Generate script section
|
|
37
|
+
const scriptLines = [];
|
|
38
|
+
const templateParts = [];
|
|
39
|
+
for (const child of node.body) {
|
|
40
|
+
switch (child.type) {
|
|
41
|
+
case 'StateDecl':
|
|
42
|
+
scriptLines.push(genState(child, c));
|
|
43
|
+
break;
|
|
44
|
+
case 'DerivedDecl':
|
|
45
|
+
scriptLines.push(genDerived(child, c));
|
|
46
|
+
break;
|
|
47
|
+
case 'PropDecl':
|
|
48
|
+
scriptLines.push(genProp(child, c));
|
|
49
|
+
break;
|
|
50
|
+
case 'FnDecl':
|
|
51
|
+
scriptLines.push(genFunction(child, c));
|
|
52
|
+
break;
|
|
53
|
+
case 'OnMount':
|
|
54
|
+
scriptLines.push(genOnMount(child, c));
|
|
55
|
+
break;
|
|
56
|
+
case 'OnDestroy':
|
|
57
|
+
scriptLines.push(genOnDestroy(child, c));
|
|
58
|
+
break;
|
|
59
|
+
case 'WatchBlock':
|
|
60
|
+
scriptLines.push(genWatch(child, c));
|
|
61
|
+
break;
|
|
62
|
+
case 'CheckDecl':
|
|
63
|
+
scriptLines.push(genCheck(child, c));
|
|
64
|
+
break;
|
|
65
|
+
case 'ApiDecl':
|
|
66
|
+
scriptLines.push(genApi(child, c));
|
|
67
|
+
break;
|
|
68
|
+
case 'TypeDecl':
|
|
69
|
+
case 'StyleDecl':
|
|
70
|
+
case 'Comment':
|
|
71
|
+
case 'Model':
|
|
72
|
+
case 'DataDecl':
|
|
73
|
+
case 'FormDecl':
|
|
74
|
+
case 'AuthDecl':
|
|
75
|
+
case 'RealtimeDecl':
|
|
76
|
+
case 'RouteDecl': break;
|
|
77
|
+
default:
|
|
78
|
+
templateParts.push(genUINode(child, c));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const importItems = Array.from(c.imports).sort();
|
|
83
|
+
const importLine = importItems.length > 0
|
|
84
|
+
? `import { ${importItems.join(', ')} } from 'vue';`
|
|
85
|
+
: '';
|
|
86
|
+
return [
|
|
87
|
+
'// Generated by 0x',
|
|
88
|
+
'<script setup>',
|
|
89
|
+
importLine,
|
|
90
|
+
'',
|
|
91
|
+
...scriptLines,
|
|
92
|
+
'</script>',
|
|
93
|
+
'',
|
|
94
|
+
'<template>',
|
|
95
|
+
...templateParts.map(t => ` ${t}`),
|
|
96
|
+
'</template>',
|
|
97
|
+
].filter(l => l !== undefined).join('\n');
|
|
98
|
+
}
|
|
99
|
+
// ── State ───────────────────────────────────────────
|
|
100
|
+
function genState(node, c) {
|
|
101
|
+
c.imports.add('ref');
|
|
102
|
+
return `const ${node.name} = ref(${genExpr(node.initial, c)});`;
|
|
103
|
+
}
|
|
104
|
+
function genDerived(node, c) {
|
|
105
|
+
c.imports.add('computed');
|
|
106
|
+
return `const ${node.name} = computed(() => ${genExpr(node.expression, c, true)});`;
|
|
107
|
+
}
|
|
108
|
+
function genProp(node, c) {
|
|
109
|
+
const defaultStr = node.defaultValue ? `, default: ${genExpr(node.defaultValue, c)}` : '';
|
|
110
|
+
return `const props = defineProps({ ${node.name}: { type: ${mapType(node.valueType)}${defaultStr} } });`;
|
|
111
|
+
}
|
|
112
|
+
function genFunction(node, c) {
|
|
113
|
+
const params = node.params.map(p => p.name).join(', ');
|
|
114
|
+
const asyncKw = node.isAsync ? 'async ' : '';
|
|
115
|
+
const body = node.body.map(s => genStmt(s, c)).join('\n ');
|
|
116
|
+
return `${asyncKw}function ${node.name}(${params}) {\n ${body}\n}`;
|
|
117
|
+
}
|
|
118
|
+
function genOnMount(node, c) {
|
|
119
|
+
c.imports.add('onMounted');
|
|
120
|
+
const body = node.body.map(s => genStmt(s, c)).join('\n ');
|
|
121
|
+
return `onMounted(() => {\n ${body}\n});`;
|
|
122
|
+
}
|
|
123
|
+
function genOnDestroy(node, c) {
|
|
124
|
+
c.imports.add('onUnmounted');
|
|
125
|
+
const body = node.body.map(s => genStmt(s, c)).join('\n ');
|
|
126
|
+
return `onUnmounted(() => {\n ${body}\n});`;
|
|
127
|
+
}
|
|
128
|
+
function genWatch(node, c) {
|
|
129
|
+
c.imports.add('watch');
|
|
130
|
+
const body = node.body.map(s => genStmt(s, c)).join('\n ');
|
|
131
|
+
return `watch(${node.variable}, () => {\n ${body}\n});`;
|
|
132
|
+
}
|
|
133
|
+
function genCheck(node, c) {
|
|
134
|
+
return `if (!(${genExpr(node.condition, c, true)})) console.error('${node.message}');`;
|
|
135
|
+
}
|
|
136
|
+
function genApi(node, c) {
|
|
137
|
+
return `async function ${node.name}(params) {\n const res = await fetch('${node.url}', { method: '${node.method}' });\n return res.json();\n}`;
|
|
138
|
+
}
|
|
139
|
+
// ── UI Nodes ────────────────────────────────────────
|
|
140
|
+
function genUINode(node, c) {
|
|
141
|
+
switch (node.type) {
|
|
142
|
+
case 'Layout': return genLayout(node, c);
|
|
143
|
+
case 'Text': return genText(node, c);
|
|
144
|
+
case 'Button': return genButton(node, c);
|
|
145
|
+
case 'Input': return genInput(node, c);
|
|
146
|
+
case 'Image': return genImage(node, c);
|
|
147
|
+
case 'Link': return genLink(node, c);
|
|
148
|
+
case 'Toggle': return genToggle(node, c);
|
|
149
|
+
case 'Select': return genSelect(node, c);
|
|
150
|
+
case 'IfBlock': return genIf(node, c);
|
|
151
|
+
case 'ForBlock': return genFor(node, c);
|
|
152
|
+
case 'ShowBlock': return genShow(node, c);
|
|
153
|
+
case 'HideBlock': return genHide(node, c);
|
|
154
|
+
case 'ComponentCall': return genComponentCall(node, c);
|
|
155
|
+
case 'Table': return genTableUI(node, c);
|
|
156
|
+
case 'Chart': return genChartUI(node, c);
|
|
157
|
+
case 'Stat': return genStatUI(node, c);
|
|
158
|
+
case 'Nav': return genNavUI(node, c);
|
|
159
|
+
case 'Upload': return genUploadUI(node, c);
|
|
160
|
+
case 'Modal': return genModalUI(node, c);
|
|
161
|
+
case 'Toast': return genToastUI(node, c);
|
|
162
|
+
case 'Hero': return genHeroUI(node, c);
|
|
163
|
+
case 'Crud': return genCrudUI(node, c);
|
|
164
|
+
case 'List': return genListUI(node, c);
|
|
165
|
+
case 'Drawer': return genDrawerUI(node, c);
|
|
166
|
+
case 'Command': return genCommandUI(node, c);
|
|
167
|
+
case 'Confirm': return genConfirmUI(node, c);
|
|
168
|
+
case 'Pay': return genPayUI(node, c);
|
|
169
|
+
case 'Cart': return genCartUI(node, c);
|
|
170
|
+
case 'Media': return genMediaUI(node, c);
|
|
171
|
+
case 'Notification': return genNotificationUI(node, c);
|
|
172
|
+
case 'Search': return genSearchUI(node, c);
|
|
173
|
+
case 'Filter': return genFilterUI(node, c);
|
|
174
|
+
case 'Social': return genSocialUI(node, c);
|
|
175
|
+
case 'Profile': return genProfileUI(node, c);
|
|
176
|
+
case 'Features': return genFeaturesUI(node, c);
|
|
177
|
+
case 'Pricing': return genPricingUI(node, c);
|
|
178
|
+
case 'Faq': return genFaqUI(node, c);
|
|
179
|
+
case 'Testimonial': return genTestimonialUI(node, c);
|
|
180
|
+
case 'Footer': return genFooterUI(node, c);
|
|
181
|
+
case 'Admin': return genAdminUI(node, c);
|
|
182
|
+
case 'Seo': return `<!-- SEO configured -->`;
|
|
183
|
+
case 'A11y': return `<!-- a11y configured -->`;
|
|
184
|
+
case 'Animate': return genAnimateUI(node, c);
|
|
185
|
+
case 'Gesture': return `<!-- gesture: ${node.gestureType} -->`;
|
|
186
|
+
case 'Ai': return genAiUI(node, c);
|
|
187
|
+
case 'Emit': return `<!-- emit: ${genExpr(node.channel, c)} -->`;
|
|
188
|
+
case 'Responsive': return genResponsiveUI(node, c);
|
|
189
|
+
case 'Breadcrumb': return genBreadcrumbUI(node, c);
|
|
190
|
+
case 'StatsGrid': return genStatsGridUI(node, c);
|
|
191
|
+
case 'LayoutShell': return `<div class="layout-shell">${node.body.map(ch => genUINode(ch, c)).join('\n')}</div>`;
|
|
192
|
+
case 'SlideOver': return genDrawerUI(node, c);
|
|
193
|
+
case 'Automation': return `<!-- automation configured -->`;
|
|
194
|
+
case 'Dev': return `<!-- dev tools configured -->`;
|
|
195
|
+
case 'Error': return genErrorUI(node, c);
|
|
196
|
+
case 'Loading': return genLoadingUI(node, c);
|
|
197
|
+
case 'Offline': return genOfflineUI(node, c);
|
|
198
|
+
case 'Retry': return `<!-- retry: max=${genExpr(node.maxRetries, c)} -->`;
|
|
199
|
+
case 'Log': return `<!-- log: ${genExpr(node.message, c)} -->`;
|
|
200
|
+
case 'Comment': return `<!-- ${node.text} -->`;
|
|
201
|
+
default: return `<!-- unsupported: ${node.type} -->`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function genLayout(node, c) {
|
|
205
|
+
const style = [];
|
|
206
|
+
if (node.direction === 'grid') {
|
|
207
|
+
style.push('display: grid');
|
|
208
|
+
if (node.props['cols'])
|
|
209
|
+
style.push(`grid-template-columns: repeat(${genExpr(node.props['cols'], c)}, 1fr)`);
|
|
210
|
+
}
|
|
211
|
+
else if (node.direction === 'stack') {
|
|
212
|
+
style.push('position: relative');
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
style.push('display: flex');
|
|
216
|
+
style.push(`flex-direction: ${node.direction === 'row' ? 'row' : 'column'}`);
|
|
217
|
+
}
|
|
218
|
+
for (const [key, val] of Object.entries(node.props)) {
|
|
219
|
+
const v = genExpr(val, c);
|
|
220
|
+
switch (key) {
|
|
221
|
+
case 'gap':
|
|
222
|
+
style.push(`gap: ${v}px`);
|
|
223
|
+
break;
|
|
224
|
+
case 'padding':
|
|
225
|
+
style.push(`padding: ${v}px`);
|
|
226
|
+
break;
|
|
227
|
+
case 'center':
|
|
228
|
+
style.push('align-items: center');
|
|
229
|
+
break;
|
|
230
|
+
case 'between':
|
|
231
|
+
style.push('justify-content: space-between');
|
|
232
|
+
break;
|
|
233
|
+
case 'bg':
|
|
234
|
+
style.push(`background-color: ${v}`);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const styleStr = style.join('; ');
|
|
239
|
+
const children = node.children.map(ch => genUINode(ch, c)).join('\n ');
|
|
240
|
+
return `<div style="${styleStr}">\n ${children}\n</div>`;
|
|
241
|
+
}
|
|
242
|
+
function genText(node, c) {
|
|
243
|
+
const style = genTextStyle(node, c);
|
|
244
|
+
const styleAttr = style ? ` style="${style}"` : '';
|
|
245
|
+
const content = genTextContent(node.content, c);
|
|
246
|
+
return `<span${styleAttr}>${content}</span>`;
|
|
247
|
+
}
|
|
248
|
+
function genButton(node, c) {
|
|
249
|
+
const label = genTextContent(node.label, c);
|
|
250
|
+
const action = genActionExpr(node.action, c);
|
|
251
|
+
return `<button @click="${action}">${label}</button>`;
|
|
252
|
+
}
|
|
253
|
+
function genInput(node, c) {
|
|
254
|
+
const props = [`v-model="${node.binding}"`];
|
|
255
|
+
for (const [key, val] of Object.entries(node.props)) {
|
|
256
|
+
const v = genExpr(val, c);
|
|
257
|
+
if (key === 'placeholder')
|
|
258
|
+
props.push(`placeholder="${unquote(v)}"`);
|
|
259
|
+
if (key === 'type')
|
|
260
|
+
props.push(`type="${unquote(v)}"`);
|
|
261
|
+
}
|
|
262
|
+
return `<input ${props.join(' ')} />`;
|
|
263
|
+
}
|
|
264
|
+
function genImage(node, c) {
|
|
265
|
+
const src = genExpr(node.src, c);
|
|
266
|
+
return `<img :src="${src}" />`;
|
|
267
|
+
}
|
|
268
|
+
function genLink(node, c) {
|
|
269
|
+
const label = genTextContent(node.label, c);
|
|
270
|
+
const href = genExpr(node.href, c);
|
|
271
|
+
return `<a :href="${href}">${label}</a>`;
|
|
272
|
+
}
|
|
273
|
+
function genToggle(node, c) {
|
|
274
|
+
return `<input type="checkbox" v-model="${node.binding}" />`;
|
|
275
|
+
}
|
|
276
|
+
function genSelect(node, c) {
|
|
277
|
+
const options = genExpr(node.options, c);
|
|
278
|
+
return `<select v-model="${node.binding}">\n <option v-for="opt in ${options}" :key="opt" :value="opt">{{ opt }}</option>\n</select>`;
|
|
279
|
+
}
|
|
280
|
+
function genComponentCall(node, c) {
|
|
281
|
+
const props = Object.entries(node.args)
|
|
282
|
+
.filter(([k]) => !k.startsWith('_arg'))
|
|
283
|
+
.map(([k, v]) => `:${k}="${genExpr(v, c)}"`);
|
|
284
|
+
return `<${node.name} ${props.join(' ')} />`;
|
|
285
|
+
}
|
|
286
|
+
function genIf(node, c) {
|
|
287
|
+
const cond = genExpr(node.condition, c);
|
|
288
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
289
|
+
let result = `<div v-if="${cond}">\n ${body}\n</div>`;
|
|
290
|
+
for (const elif of node.elifs) {
|
|
291
|
+
const ec = genExpr(elif.condition, c);
|
|
292
|
+
const eb = elif.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
293
|
+
result += `\n<div v-else-if="${ec}">\n ${eb}\n</div>`;
|
|
294
|
+
}
|
|
295
|
+
if (node.elseBody) {
|
|
296
|
+
const eb = node.elseBody.map(ch => genUINode(ch, c)).join('\n ');
|
|
297
|
+
result += `\n<div v-else>\n ${eb}\n</div>`;
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
function genFor(node, c) {
|
|
302
|
+
const iter = genExpr(node.iterable, c);
|
|
303
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
304
|
+
const binding = node.index ? `(${node.item}, ${node.index})` : node.item;
|
|
305
|
+
return `<div v-for="${binding} in ${iter}" :key="${node.item}">\n ${body}\n</div>`;
|
|
306
|
+
}
|
|
307
|
+
function genShow(node, c) {
|
|
308
|
+
const cond = genExpr(node.condition, c);
|
|
309
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
310
|
+
return `<div v-show="${cond}">\n ${body}\n</div>`;
|
|
311
|
+
}
|
|
312
|
+
function genHide(node, c) {
|
|
313
|
+
const cond = genExpr(node.condition, c);
|
|
314
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
315
|
+
return `<div v-show="!${cond}">\n ${body}\n</div>`;
|
|
316
|
+
}
|
|
317
|
+
// ── Advanced UI Components ──────────────────────────
|
|
318
|
+
function genTableUI(node, c) {
|
|
319
|
+
const data = node.dataSource;
|
|
320
|
+
const lines = [];
|
|
321
|
+
lines.push(`<table style="width: 100%; border-collapse: collapse">`);
|
|
322
|
+
lines.push(`<thead><tr>`);
|
|
323
|
+
for (const col of node.columns) {
|
|
324
|
+
if (col.kind === 'select') {
|
|
325
|
+
lines.push(`<th style="padding: 12px 8px; border-bottom: 2px solid #e2e8f0"><input type="checkbox" /></th>`);
|
|
326
|
+
}
|
|
327
|
+
else if (col.kind === 'field') {
|
|
328
|
+
lines.push(`<th style="padding: 12px 8px; border-bottom: 2px solid #e2e8f0; text-align: left; font-weight: 600">${col.label || col.field}</th>`);
|
|
329
|
+
}
|
|
330
|
+
else if (col.kind === 'actions') {
|
|
331
|
+
lines.push(`<th style="padding: 12px 8px; border-bottom: 2px solid #e2e8f0; text-align: right; font-weight: 600">Actions</th>`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
lines.push(`</tr></thead>`);
|
|
335
|
+
lines.push(`<tbody>`);
|
|
336
|
+
lines.push(`<tr v-for="(row, idx) in ${data}" :key="idx" style="border-bottom: 1px solid #e2e8f0">`);
|
|
337
|
+
for (const col of node.columns) {
|
|
338
|
+
if (col.kind === 'select') {
|
|
339
|
+
lines.push(`<td style="padding: 12px 8px"><input type="checkbox" /></td>`);
|
|
340
|
+
}
|
|
341
|
+
else if (col.kind === 'field') {
|
|
342
|
+
let content = `{{ row.${col.field} }}`;
|
|
343
|
+
if (col.format === 'date')
|
|
344
|
+
content = `{{ new Date(row.${col.field}).toLocaleDateString() }}`;
|
|
345
|
+
lines.push(`<td style="padding: 12px 8px">${content}</td>`);
|
|
346
|
+
}
|
|
347
|
+
else if (col.kind === 'actions') {
|
|
348
|
+
lines.push(`<td style="padding: 12px 8px; text-align: right">`);
|
|
349
|
+
for (const action of (col.actions || [])) {
|
|
350
|
+
if (action === 'edit')
|
|
351
|
+
lines.push(`<button @click="onEdit(row)" style="margin-right: 4px; padding: 4px 8px; font-size: 13px; cursor: pointer">Edit</button>`);
|
|
352
|
+
if (action === 'delete')
|
|
353
|
+
lines.push(`<button @click="onDelete(row)" style="padding: 4px 8px; font-size: 13px; cursor: pointer; color: #e53e3e">Delete</button>`);
|
|
354
|
+
}
|
|
355
|
+
lines.push(`</td>`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
lines.push(`</tr>`);
|
|
359
|
+
lines.push(`</tbody></table>`);
|
|
360
|
+
return lines.join('\n');
|
|
361
|
+
}
|
|
362
|
+
function genChartUI(node, c) {
|
|
363
|
+
return `<div style="padding: 24px; border: 1px solid #e2e8f0; border-radius: 8px"><!-- Chart: ${node.chartType} --><canvas ref="${node.name}Chart"></canvas></div>`;
|
|
364
|
+
}
|
|
365
|
+
function genStatUI(node, c) {
|
|
366
|
+
const value = genExpr(node.value, c);
|
|
367
|
+
return `<div style="padding: 20px; border-radius: 12px; background: #f7fafc; border: 1px solid #e2e8f0"><div style="font-size: 14px; color: #718096">${node.label}</div><div style="font-size: 32px; font-weight: bold; margin-top: 4px">{{ ${value} }}</div></div>`;
|
|
368
|
+
}
|
|
369
|
+
function genNavUI(node, c) {
|
|
370
|
+
const lines = [];
|
|
371
|
+
lines.push(`<nav style="display: flex; gap: 16px; padding: 12px 24px; background-color: #fff; border-bottom: 1px solid #e2e8f0; align-items: center">`);
|
|
372
|
+
for (const item of node.items) {
|
|
373
|
+
const iconPart = item.icon ? `<span style="margin-right: 6px">${item.icon}</span>` : '';
|
|
374
|
+
lines.push(`<a href="${item.href}" style="text-decoration: none; color: #4a5568; font-weight: 500; padding: 8px 12px; border-radius: 6px">${iconPart}${item.label}</a>`);
|
|
375
|
+
}
|
|
376
|
+
lines.push(`</nav>`);
|
|
377
|
+
return lines.join('\n');
|
|
378
|
+
}
|
|
379
|
+
function genUploadUI(node, c) {
|
|
380
|
+
c.imports.add('ref');
|
|
381
|
+
const name = node.name;
|
|
382
|
+
return `<div style="border: 2px dashed #cbd5e0; border-radius: 12px; padding: 24px; text-align: center; cursor: pointer" @click="$refs.${name}Input?.click()"><input ref="${name}Input" type="file"${node.accept ? ` accept="${node.accept}"` : ''} style="display: none" /><span style="color: #a0aec0">Click to select file</span></div>`;
|
|
383
|
+
}
|
|
384
|
+
function genModalUI(node, c) {
|
|
385
|
+
c.imports.add('ref');
|
|
386
|
+
const showVar = `show${capitalize(node.name)}`;
|
|
387
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
388
|
+
const lines = [];
|
|
389
|
+
if (node.trigger) {
|
|
390
|
+
lines.push(`<button @click="${showVar} = true" style="padding: 8px 16px; border-radius: 6px; border: 1px solid #e2e8f0; cursor: pointer">${node.trigger}</button>`);
|
|
391
|
+
}
|
|
392
|
+
lines.push(`<Teleport to="body">`);
|
|
393
|
+
lines.push(`<div v-if="${showVar}" style="position: fixed; inset: 0; background-color: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000" @click="${showVar} = false">`);
|
|
394
|
+
lines.push(` <div style="background-color: #fff; border-radius: 12px; padding: 24px; min-width: 400px; max-width: 90vw; box-shadow: 0 20px 60px rgba(0,0,0,0.15)" @click.stop>`);
|
|
395
|
+
lines.push(` <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px">`);
|
|
396
|
+
lines.push(` <h2 style="margin: 0; font-size: 20px">${node.title}</h2>`);
|
|
397
|
+
lines.push(` <button @click="${showVar} = false" style="border: none; background: none; font-size: 20px; cursor: pointer; padding: 4px">×</button>`);
|
|
398
|
+
lines.push(` </div>`);
|
|
399
|
+
lines.push(` ${body}`);
|
|
400
|
+
lines.push(` </div>`);
|
|
401
|
+
lines.push(`</div>`);
|
|
402
|
+
lines.push(`</Teleport>`);
|
|
403
|
+
return lines.join('\n');
|
|
404
|
+
}
|
|
405
|
+
function genToastUI(node, c) {
|
|
406
|
+
return `<div v-if="toast.visible" style="position: fixed; top: 16px; right: 16px; padding: 12px 20px; border-radius: 8px; background-color: #333; color: #fff; z-index: 2000; box-shadow: 0 4px 12px rgba(0,0,0,0.15)">{{ toast.message }}</div>`;
|
|
407
|
+
}
|
|
408
|
+
function genHeroUI(node, c) {
|
|
409
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
410
|
+
return `<section style="text-align: center; padding: 80px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff">\n ${body}\n</section>`;
|
|
411
|
+
}
|
|
412
|
+
function genCrudUI(node, c) {
|
|
413
|
+
return `<div class="crud-${node.model.toLowerCase()}"><!-- CRUD: ${node.model} --><h2>${node.model} Management</h2></div>`;
|
|
414
|
+
}
|
|
415
|
+
function genListUI(node, c) {
|
|
416
|
+
const data = genExpr(node.dataSource, c);
|
|
417
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
418
|
+
return `<div style="display: flex; flex-direction: column; gap: 8px"><div v-for="item in ${data}" :key="item.id" style="padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px">\n ${body}\n </div></div>`;
|
|
419
|
+
}
|
|
420
|
+
function genDrawerUI(node, c) {
|
|
421
|
+
c.imports.add('ref');
|
|
422
|
+
const showVar = `show${capitalize(node.name)}`;
|
|
423
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
424
|
+
return `<Teleport to="body"><div v-if="${showVar}" style="position: fixed; inset: 0; background-color: rgba(0,0,0,0.5); z-index: 1000" @click="${showVar} = false"><div style="position: fixed; top: 0; right: 0; bottom: 0; width: 320px; background-color: #fff; padding: 24px; box-shadow: -2px 0 8px rgba(0,0,0,0.1); overflow-y: auto" @click.stop>\n ${body}\n </div></div></Teleport>`;
|
|
425
|
+
}
|
|
426
|
+
function genCommandUI(node, c) {
|
|
427
|
+
return `<div style="position: fixed; top: 20%; left: 50%; transform: translateX(-50%); width: 500px; background: #fff; border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,0.2); z-index: 1000; padding: 8px"><input placeholder="Type a command..." style="width: 100%; padding: 12px 16px; border: none; font-size: 16px; outline: none" /></div>`;
|
|
428
|
+
}
|
|
429
|
+
function genConfirmUI(node, c) {
|
|
430
|
+
const desc = node.description ? `<p style="color: #718096; margin: 8px 0 0 0">${node.description}</p>` : '';
|
|
431
|
+
const dangerStyle = node.danger ? 'background-color: #e53e3e' : 'background-color: #3182ce';
|
|
432
|
+
return `<div style="position: fixed; inset: 0; background-color: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000"><div style="background: #fff; border-radius: 12px; padding: 24px; max-width: 400px"><p style="font-weight: 600; margin: 0">${node.message}</p>${desc}<div style="display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px"><button @click="onCancel" style="padding: 8px 16px; border-radius: 6px; border: 1px solid #e2e8f0; cursor: pointer">${node.cancelLabel}</button><button @click="onConfirm" style="padding: 8px 16px; border-radius: 6px; ${dangerStyle}; color: #fff; border: none; cursor: pointer">${node.confirmLabel}</button></div></div></div>`;
|
|
433
|
+
}
|
|
434
|
+
function genPayUI(node, c) {
|
|
435
|
+
return `<div style="padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px"><!-- Payment: ${node.provider} --><button style="width: 100%; padding: 12px; background: #635bff; color: #fff; border: none; border-radius: 8px; font-size: 16px; cursor: pointer">Pay Now</button></div>`;
|
|
436
|
+
}
|
|
437
|
+
function genCartUI(node, c) {
|
|
438
|
+
return `<div style="padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px"><!-- Cart --><h3 style="margin: 0 0 12px 0">Shopping Cart</h3><div v-for="item in cartItems" :key="item.id" style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f0f0f0"><span>{{ item.name }}</span><span>{{ item.price }}</span></div></div>`;
|
|
439
|
+
}
|
|
440
|
+
function genMediaUI(node, c) {
|
|
441
|
+
return `<div style="border-radius: 12px; overflow: hidden"><!-- Media: ${node.mediaType} --></div>`;
|
|
442
|
+
}
|
|
443
|
+
function genNotificationUI(node, c) {
|
|
444
|
+
return `<div style="position: fixed; top: 16px; right: 16px; z-index: 2000; display: flex; flex-direction: column; gap: 8px"><div v-for="n in notifications" :key="n.id" style="padding: 12px 20px; border-radius: 8px; background-color: #333; color: #fff; box-shadow: 0 4px 12px rgba(0,0,0,0.15)">{{ n.message }}</div></div>`;
|
|
445
|
+
}
|
|
446
|
+
function genSearchUI(node, c) {
|
|
447
|
+
c.imports.add('ref');
|
|
448
|
+
return `<div style="position: relative"><input v-model="searchQuery" placeholder="Search..." style="width: 100%; padding: 10px 16px; border-radius: 8px; border: 1px solid #e2e8f0; font-size: 14px" /></div>`;
|
|
449
|
+
}
|
|
450
|
+
function genFilterUI(node, c) {
|
|
451
|
+
return `<div style="display: flex; gap: 8px; flex-wrap: wrap"><!-- Filter: ${node.target} --></div>`;
|
|
452
|
+
}
|
|
453
|
+
function genSocialUI(node, c) {
|
|
454
|
+
return `<div style="display: flex; gap: 12px; align-items: center"><!-- Social: ${node.socialType} --><button style="padding: 8px 16px; border-radius: 20px; border: 1px solid #e2e8f0; cursor: pointer">Like</button></div>`;
|
|
455
|
+
}
|
|
456
|
+
function genProfileUI(node, c) {
|
|
457
|
+
return `<div style="display: flex; gap: 16px; align-items: center; padding: 20px"><!-- Profile --><div style="width: 64px; height: 64px; border-radius: 50%; background-color: #e2e8f0"></div><div><div style="font-weight: 600">User Profile</div></div></div>`;
|
|
458
|
+
}
|
|
459
|
+
function genFeaturesUI(node, c) {
|
|
460
|
+
const items = node.items.map(item => `<div style="padding: 24px; border-radius: 12px; border: 1px solid #e2e8f0"><div style="font-size: 24px; margin-bottom: 12px">${item.icon}</div><h3 style="margin: 0 0 8px 0">${item.title}</h3><p style="color: #718096; margin: 0">${item.description}</p></div>`).join('\n ');
|
|
461
|
+
return `<section style="padding: 60px 20px"><h2 style="text-align: center; margin-bottom: 40px">Features</h2><div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px">\n ${items}\n</div></section>`;
|
|
462
|
+
}
|
|
463
|
+
function genPricingUI(node, c) {
|
|
464
|
+
const plans = node.plans.map(plan => {
|
|
465
|
+
const features = plan.features.map(f => `<li style="padding: 4px 0">${f}</li>`).join('');
|
|
466
|
+
const hl = plan.highlighted ? 'border: 2px solid #3182ce; transform: scale(1.05)' : 'border: 1px solid #e2e8f0';
|
|
467
|
+
return `<div style="padding: 32px; border-radius: 12px; ${hl}"><h3 style="margin: 0">${plan.name}</h3><div style="font-size: 32px; font-weight: bold; margin: 16px 0">${genExpr(plan.price, c)}</div><ul style="list-style: none; padding: 0; margin: 16px 0">${features}</ul><button style="width: 100%; padding: 10px; border-radius: 6px; background-color: #3182ce; color: #fff; border: none; cursor: pointer">${plan.cta}</button></div>`;
|
|
468
|
+
}).join('\n ');
|
|
469
|
+
return `<section style="padding: 60px 20px"><h2 style="text-align: center; margin-bottom: 40px">Pricing</h2><div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; max-width: 1000px; margin: 0 auto">\n ${plans}\n</div></section>`;
|
|
470
|
+
}
|
|
471
|
+
function genFaqUI(node, c) {
|
|
472
|
+
const items = node.items.map(item => `<details style="border-bottom: 1px solid #e2e8f0; padding: 16px 0"><summary style="cursor: pointer; font-weight: 600">${item.question}</summary><p style="color: #718096; margin: 8px 0 0 0">${item.answer}</p></details>`).join('\n ');
|
|
473
|
+
return `<section style="padding: 60px 20px; max-width: 700px; margin: 0 auto"><h2 style="text-align: center; margin-bottom: 40px">FAQ</h2>\n ${items}\n</section>`;
|
|
474
|
+
}
|
|
475
|
+
function genTestimonialUI(node, c) {
|
|
476
|
+
const items = node.items.map(item => `<div style="padding: 24px; border-radius: 12px; border: 1px solid #e2e8f0"><p style="margin: 0 0 16px 0; font-style: italic">"${item.text}"</p><div style="display: flex; align-items: center; gap: 12px"><div style="width: 40px; height: 40px; border-radius: 50%; background-color: #e2e8f0"></div><div><div style="font-weight: 600">${item.name}</div><div style="font-size: 14px; color: #718096">${item.role}</div></div></div></div>`).join('\n ');
|
|
477
|
+
return `<section style="padding: 60px 20px"><h2 style="text-align: center; margin-bottom: 40px">Testimonials</h2><div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px">\n ${items}\n</div></section>`;
|
|
478
|
+
}
|
|
479
|
+
function genFooterUI(node, c) {
|
|
480
|
+
const cols = node.columns.map(col => {
|
|
481
|
+
const links = col.links.map(l => `<a href="${l.href}" style="display: block; color: #a0aec0; text-decoration: none; padding: 4px 0">${l.label}</a>`).join('');
|
|
482
|
+
return `<div><h4 style="color: #fff; margin: 0 0 12px 0">${col.title}</h4>${links}</div>`;
|
|
483
|
+
}).join('\n ');
|
|
484
|
+
return `<footer style="padding: 40px 20px; background-color: #1a202c; color: #a0aec0"><div style="display: grid; grid-template-columns: repeat(${node.columns.length}, 1fr); gap: 32px; max-width: 1000px; margin: 0 auto">\n ${cols}\n</div></footer>`;
|
|
485
|
+
}
|
|
486
|
+
function genAdminUI(node, c) {
|
|
487
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
488
|
+
return `<div style="display: grid; grid-template-columns: 240px 1fr; min-height: 100vh"><aside style="background-color: #1a202c; color: #fff; padding: 20px"><h3 style="margin: 0 0 20px 0">Admin</h3></aside><main style="padding: 24px">\n ${body}\n </main></div>`;
|
|
489
|
+
}
|
|
490
|
+
function genAnimateUI(node, c) {
|
|
491
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
492
|
+
return `<Transition name="${node.animType}">\n ${body}\n</Transition>`;
|
|
493
|
+
}
|
|
494
|
+
function genAiUI(node, c) {
|
|
495
|
+
return `<div style="display: flex; flex-direction: column; height: 100%"><!-- AI: ${node.aiType} --><div style="flex: 1; overflow-y: auto; padding: 16px"></div><div style="padding: 16px; border-top: 1px solid #e2e8f0"><input placeholder="Type a message..." style="width: 100%; padding: 10px 16px; border-radius: 8px; border: 1px solid #e2e8f0" /></div></div>`;
|
|
496
|
+
}
|
|
497
|
+
function genResponsiveUI(node, c) {
|
|
498
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
499
|
+
return `<div class="responsive-container">\n ${body}\n</div>`;
|
|
500
|
+
}
|
|
501
|
+
function genBreadcrumbUI(node, c) {
|
|
502
|
+
return `<nav style="display: flex; align-items: center; padding: 12px 0; font-size: 14px"><!-- Breadcrumb --></nav>`;
|
|
503
|
+
}
|
|
504
|
+
function genStatsGridUI(node, c) {
|
|
505
|
+
const stats = node.stats.map(s => genStatUI(s, c)).join('\n ');
|
|
506
|
+
return `<div style="display: grid; grid-template-columns: repeat(${node.cols || 4}, 1fr); gap: 16px">\n ${stats}\n</div>`;
|
|
507
|
+
}
|
|
508
|
+
function genErrorUI(node, c) {
|
|
509
|
+
const fallback = node.fallback.map(ch => genUINode(ch, c)).join('\n ');
|
|
510
|
+
if (node.errorType === 'boundary') {
|
|
511
|
+
return `<div v-if="hasError" style="padding: 24px; text-align: center; color: #e53e3e">\n ${fallback || '<p>An error occurred</p>'}\n </div>`;
|
|
512
|
+
}
|
|
513
|
+
return `<!-- Error handler: ${node.errorType} -->`;
|
|
514
|
+
}
|
|
515
|
+
function genLoadingUI(node, c) {
|
|
516
|
+
const body = node.body.map(ch => genUINode(ch, c)).join('\n ');
|
|
517
|
+
if (node.loadingType === 'skeleton') {
|
|
518
|
+
return `<div style="animation: pulse 1.5s infinite; background-color: #e2e8f0; border-radius: 8px">\n ${body || '<div style="height: 20px; margin-bottom: 8px"></div><div style="height: 20px; width: 60%"></div>'}\n</div>`;
|
|
519
|
+
}
|
|
520
|
+
if (node.loadingType === 'spinner') {
|
|
521
|
+
return `<div style="display: flex; justify-content: center; align-items: center; padding: 40px"><div style="width: 32px; height: 32px; border: 3px solid #e2e8f0; border-top: 3px solid #3182ce; border-radius: 50%; animation: spin 0.8s linear infinite"></div></div>`;
|
|
522
|
+
}
|
|
523
|
+
if (node.loadingType === 'shimmer') {
|
|
524
|
+
return `<div style="background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; border-radius: 8px; height: 100px"></div>`;
|
|
525
|
+
}
|
|
526
|
+
return `<div style="text-align: center; padding: 20px; color: #718096">Loading...</div>`;
|
|
527
|
+
}
|
|
528
|
+
function genOfflineUI(node, c) {
|
|
529
|
+
return `<div v-if="!navigator.onLine" style="padding: 24px; text-align: center; background-color: #fff3cd; border-radius: 8px"><p style="margin: 0; color: #856404">You are currently offline</p></div>`;
|
|
530
|
+
}
|
|
531
|
+
// ── Helpers ─────────────────────────────────────────
|
|
532
|
+
function genExpr(expr, c, useValue = false) {
|
|
533
|
+
const suffix = useValue ? '.value' : '';
|
|
534
|
+
switch (expr.kind) {
|
|
535
|
+
case 'number': return String(expr.value);
|
|
536
|
+
case 'string': return `'${expr.value}'`;
|
|
537
|
+
case 'boolean': return String(expr.value);
|
|
538
|
+
case 'null': return 'null';
|
|
539
|
+
case 'identifier':
|
|
540
|
+
if (useValue && c.states.has(expr.name))
|
|
541
|
+
return `${expr.name}.value`;
|
|
542
|
+
return expr.name;
|
|
543
|
+
case 'member': return `${genExpr(expr.object, c, useValue)}.${expr.property}`;
|
|
544
|
+
case 'index': return `${genExpr(expr.object, c, useValue)}[${genExpr(expr.index, c, useValue)}]`;
|
|
545
|
+
case 'call': {
|
|
546
|
+
const callee = genExpr(expr.callee, c, useValue);
|
|
547
|
+
const args = expr.args.map(a => genExpr(a, c, useValue)).join(', ');
|
|
548
|
+
return `${callee}(${args})`;
|
|
549
|
+
}
|
|
550
|
+
case 'binary': return `${genExpr(expr.left, c, useValue)} ${expr.op} ${genExpr(expr.right, c, useValue)}`;
|
|
551
|
+
case 'unary': return `${expr.op}${genExpr(expr.operand, c, useValue)}`;
|
|
552
|
+
case 'ternary': return `${genExpr(expr.condition, c, useValue)} ? ${genExpr(expr.consequent, c, useValue)} : ${genExpr(expr.alternate, c, useValue)}`;
|
|
553
|
+
case 'arrow': {
|
|
554
|
+
const params = expr.params.join(', ');
|
|
555
|
+
if (!Array.isArray(expr.body))
|
|
556
|
+
return `${params} => ${genExpr(expr.body, c, useValue)}`;
|
|
557
|
+
return `(${params}) => { }`;
|
|
558
|
+
}
|
|
559
|
+
case 'array': return `[${expr.elements.map(e => genExpr(e, c, useValue)).join(', ')}]`;
|
|
560
|
+
case 'object_expr': {
|
|
561
|
+
const ps = expr.properties.map(p => `${p.key}: ${genExpr(p.value, c, useValue)}`).join(', ');
|
|
562
|
+
return `{ ${ps} }`;
|
|
563
|
+
}
|
|
564
|
+
case 'template': {
|
|
565
|
+
return expr.parts.map(p => typeof p === 'string' ? p : `\${${genExpr(p, c, useValue)}}`).join('');
|
|
566
|
+
}
|
|
567
|
+
case 'assignment': {
|
|
568
|
+
const target = genExpr(expr.target, c);
|
|
569
|
+
const value = genExpr(expr.value, c);
|
|
570
|
+
if (c.states.has(extractName(expr.target))) {
|
|
571
|
+
return `${extractName(expr.target)}.value ${expr.op} ${value}`;
|
|
572
|
+
}
|
|
573
|
+
return `${target} ${expr.op} ${value}`;
|
|
574
|
+
}
|
|
575
|
+
case 'await': return `await ${genExpr(expr.expression, c, useValue)}`;
|
|
576
|
+
case 'old': return genExpr(expr.expression, c, useValue);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function genStmt(stmt, c) {
|
|
580
|
+
switch (stmt.kind) {
|
|
581
|
+
case 'expr_stmt': return genExpr(stmt.expression, c, true) + ';';
|
|
582
|
+
case 'return': return stmt.value ? `return ${genExpr(stmt.value, c, true)};` : 'return;';
|
|
583
|
+
case 'assignment_stmt': {
|
|
584
|
+
const name = extractName(stmt.target);
|
|
585
|
+
if (c.states.has(name)) {
|
|
586
|
+
const value = genExpr(stmt.value, c, true);
|
|
587
|
+
return `${name}.value ${stmt.op} ${value};`;
|
|
588
|
+
}
|
|
589
|
+
return `${genExpr(stmt.target, c)} ${stmt.op} ${genExpr(stmt.value, c)};`;
|
|
590
|
+
}
|
|
591
|
+
case 'var_decl': return `const ${stmt.name} = ${genExpr(stmt.value, c)};`;
|
|
592
|
+
case 'if_stmt': {
|
|
593
|
+
const cond = genExpr(stmt.condition, c, true);
|
|
594
|
+
const body = stmt.body.map(s => genStmt(s, c)).join('\n ');
|
|
595
|
+
return `if (${cond}) {\n ${body}\n}`;
|
|
596
|
+
}
|
|
597
|
+
case 'for_stmt': {
|
|
598
|
+
const iter = genExpr(stmt.iterable, c, true);
|
|
599
|
+
const body = stmt.body.map(s => genStmt(s, c)).join('\n ');
|
|
600
|
+
return `for (const ${stmt.item} of ${iter}) {\n ${body}\n}`;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function genActionExpr(expr, c) {
|
|
605
|
+
if (Array.isArray(expr))
|
|
606
|
+
return '';
|
|
607
|
+
if (expr.kind === 'assignment') {
|
|
608
|
+
const name = extractName(expr.target);
|
|
609
|
+
if (c.states.has(name)) {
|
|
610
|
+
return `${name} ${expr.op} ${genExpr(expr.value, c)}`;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (expr.kind === 'call')
|
|
614
|
+
return genExpr(expr, c);
|
|
615
|
+
return genExpr(expr, c);
|
|
616
|
+
}
|
|
617
|
+
function genTextContent(expr, c) {
|
|
618
|
+
if (expr.kind === 'string')
|
|
619
|
+
return expr.value;
|
|
620
|
+
if (expr.kind === 'template') {
|
|
621
|
+
return expr.parts.map(p => typeof p === 'string' ? p : `{{ ${genExpr(p, c)} }}`).join('');
|
|
622
|
+
}
|
|
623
|
+
return `{{ ${genExpr(expr, c)} }}`;
|
|
624
|
+
}
|
|
625
|
+
function genTextStyle(node, c) {
|
|
626
|
+
const parts = [];
|
|
627
|
+
for (const [key, val] of Object.entries(node.props)) {
|
|
628
|
+
const v = genExpr(val, c);
|
|
629
|
+
if (key === 'size') {
|
|
630
|
+
const uv = unquote(v);
|
|
631
|
+
parts.push(`font-size: ${SIZE_MAP[uv] || uv}`);
|
|
632
|
+
}
|
|
633
|
+
if (key === 'bold')
|
|
634
|
+
parts.push('font-weight: bold');
|
|
635
|
+
if (key === 'color')
|
|
636
|
+
parts.push(`color: ${unquote(v)}`);
|
|
637
|
+
if (key === 'center')
|
|
638
|
+
parts.push('text-align: center');
|
|
639
|
+
}
|
|
640
|
+
return parts.join('; ');
|
|
641
|
+
}
|
|
642
|
+
function mapType(t) {
|
|
643
|
+
if (t.kind === 'primitive') {
|
|
644
|
+
switch (t.name) {
|
|
645
|
+
case 'str': return 'String';
|
|
646
|
+
case 'int':
|
|
647
|
+
case 'float': return 'Number';
|
|
648
|
+
case 'bool': return 'Boolean';
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return 'Object';
|
|
652
|
+
}
|
|
653
|
+
function extractName(expr) {
|
|
654
|
+
if (expr.kind === 'identifier')
|
|
655
|
+
return expr.name;
|
|
656
|
+
if (expr.kind === 'member')
|
|
657
|
+
return extractName(expr.object);
|
|
658
|
+
return '';
|
|
659
|
+
}
|
|
660
|
+
//# sourceMappingURL=vue.js.map
|