@lokascript/compilation-service 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-CY26COTT.js +77 -0
- package/dist/chunk-CY26COTT.js.map +1 -0
- package/dist/chunk-HMLY7DHA.js +16 -0
- package/dist/chunk-HMLY7DHA.js.map +1 -0
- package/dist/chunk-JDOSUTCM.js +49 -0
- package/dist/chunk-JDOSUTCM.js.map +1 -0
- package/dist/chunk-ODJIDVMN.js +3322 -0
- package/dist/chunk-ODJIDVMN.js.map +1 -0
- package/dist/core-parser-adapter-VCJB52SO-MDHRIVZ7.js +12 -0
- package/dist/core-parser-adapter-VCJB52SO-MDHRIVZ7.js.map +1 -0
- package/dist/dist-ICUX26U7.js +3790 -0
- package/dist/dist-ICUX26U7.js.map +1 -0
- package/dist/dist-JOIDNRL3.js +65969 -0
- package/dist/dist-JOIDNRL3.js.map +1 -0
- package/dist/http.cjs +73093 -0
- package/dist/http.cjs.map +1 -0
- package/dist/http.d.cts +46 -0
- package/dist/http.d.ts +46 -0
- package/dist/http.js +202 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +72924 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +168 -0
- package/dist/index.d.ts +168 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/semantic-adapter-7HTMTO75-MFFBR7K3.js +12 -0
- package/dist/semantic-adapter-7HTMTO75-MFFBR7K3.js.map +1 -0
- package/dist/service-891L1MYk.d.cts +595 -0
- package/dist/service-891L1MYk.d.ts +595 -0
- package/package.json +73 -0
|
@@ -0,0 +1,3790 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CoreParserAdapter,
|
|
3
|
+
createCoreParserAdapter
|
|
4
|
+
} from "./chunk-JDOSUTCM.js";
|
|
5
|
+
import {
|
|
6
|
+
SemanticParserAdapter,
|
|
7
|
+
createSemanticAdapter
|
|
8
|
+
} from "./chunk-CY26COTT.js";
|
|
9
|
+
import "./chunk-HMLY7DHA.js";
|
|
10
|
+
|
|
11
|
+
// ../aot-compiler/dist/index.js
|
|
12
|
+
var DEFAULT_OPTIONS = {
|
|
13
|
+
attributeNames: ["_", "data-hs", "data-hyperscript"],
|
|
14
|
+
includeScriptTags: true,
|
|
15
|
+
defaultLanguage: "en"
|
|
16
|
+
};
|
|
17
|
+
function createAttributeRegex(attrName) {
|
|
18
|
+
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
19
|
+
return new RegExp(
|
|
20
|
+
`${escaped}\\s*=\\s*(?:"([^"]*?)"|'([^']*?)'|([^\\s>]+))`,
|
|
21
|
+
"gi"
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
var ELEMENT_REGEX = /<([a-z][a-z0-9-]*)\s+([^>]*?)>/gi;
|
|
25
|
+
var SCRIPT_TAG_REGEX = /<script\s+[^>]*type\s*=\s*["']text\/hyperscript["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
26
|
+
var ID_REGEX = /\bid\s*=\s*["']([^"']+)["']/i;
|
|
27
|
+
var CLASS_REGEX = /\bclass\s*=\s*["']([^"']+)["']/i;
|
|
28
|
+
var LANG_REGEX = /\blang\s*=\s*["']([^"']+)["']/i;
|
|
29
|
+
var HTMLScanner = class {
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
32
|
+
this.attributeRegexes = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const attr of this.options.attributeNames) {
|
|
34
|
+
this.attributeRegexes.set(attr, createAttributeRegex(attr));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract hyperscript from HTML source code.
|
|
39
|
+
*/
|
|
40
|
+
extract(source, filename) {
|
|
41
|
+
const scripts = [];
|
|
42
|
+
scripts.push(...this.extractFromAttributes(source, filename));
|
|
43
|
+
if (this.options.includeScriptTags) {
|
|
44
|
+
scripts.push(...this.extractFromScriptTags(source, filename));
|
|
45
|
+
}
|
|
46
|
+
return scripts;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extract hyperscript from element attributes.
|
|
50
|
+
*/
|
|
51
|
+
extractFromAttributes(source, filename) {
|
|
52
|
+
const scripts = [];
|
|
53
|
+
let elementMatch;
|
|
54
|
+
ELEMENT_REGEX.lastIndex = 0;
|
|
55
|
+
while ((elementMatch = ELEMENT_REGEX.exec(source)) !== null) {
|
|
56
|
+
const tagName = elementMatch[1];
|
|
57
|
+
const attributes = elementMatch[2];
|
|
58
|
+
const elementStart = elementMatch.index;
|
|
59
|
+
const location = this.calculateLocation(source, elementStart, filename);
|
|
60
|
+
for (const [attrName, regex] of this.attributeRegexes) {
|
|
61
|
+
regex.lastIndex = 0;
|
|
62
|
+
const attrMatch = regex.exec(attributes);
|
|
63
|
+
if (attrMatch) {
|
|
64
|
+
const code = attrMatch[1] ?? attrMatch[2] ?? attrMatch[3];
|
|
65
|
+
if (code && code.trim()) {
|
|
66
|
+
const elementId = this.extractId(attributes);
|
|
67
|
+
const elementSelector = this.buildSelector(tagName, attributes, elementId);
|
|
68
|
+
const language = this.extractLanguage(attributes) ?? this.options.defaultLanguage;
|
|
69
|
+
const decodedCode = this.decodeHTMLEntities(code);
|
|
70
|
+
scripts.push({
|
|
71
|
+
code: decodedCode,
|
|
72
|
+
location: {
|
|
73
|
+
...location,
|
|
74
|
+
column: location.column + attrMatch.index
|
|
75
|
+
},
|
|
76
|
+
elementId,
|
|
77
|
+
elementSelector,
|
|
78
|
+
language,
|
|
79
|
+
attributeName: attrName
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return scripts;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Extract hyperscript from script tags.
|
|
89
|
+
*/
|
|
90
|
+
extractFromScriptTags(source, filename) {
|
|
91
|
+
const scripts = [];
|
|
92
|
+
let match;
|
|
93
|
+
SCRIPT_TAG_REGEX.lastIndex = 0;
|
|
94
|
+
while ((match = SCRIPT_TAG_REGEX.exec(source)) !== null) {
|
|
95
|
+
const code = match[1].trim();
|
|
96
|
+
if (code) {
|
|
97
|
+
const location = this.calculateLocation(source, match.index, filename);
|
|
98
|
+
scripts.push({
|
|
99
|
+
code,
|
|
100
|
+
location,
|
|
101
|
+
language: this.options.defaultLanguage,
|
|
102
|
+
attributeName: "script"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return scripts;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Calculate line and column from character offset.
|
|
110
|
+
*/
|
|
111
|
+
calculateLocation(source, offset, filename) {
|
|
112
|
+
const before = source.slice(0, offset);
|
|
113
|
+
const lines = before.split("\n");
|
|
114
|
+
const line = lines.length;
|
|
115
|
+
const column = (lines[lines.length - 1]?.length ?? 0) + 1;
|
|
116
|
+
return { file: filename, line, column };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract ID from attributes string.
|
|
120
|
+
*/
|
|
121
|
+
extractId(attributes) {
|
|
122
|
+
const match = ID_REGEX.exec(attributes);
|
|
123
|
+
return match?.[1];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Extract language from attributes string.
|
|
127
|
+
*/
|
|
128
|
+
extractLanguage(attributes) {
|
|
129
|
+
const match = LANG_REGEX.exec(attributes);
|
|
130
|
+
return match?.[1];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Build a CSS selector for an element.
|
|
134
|
+
*/
|
|
135
|
+
buildSelector(tagName, attributes, elementId) {
|
|
136
|
+
if (elementId) {
|
|
137
|
+
return `#${elementId}`;
|
|
138
|
+
}
|
|
139
|
+
let selector = tagName;
|
|
140
|
+
const classMatch = CLASS_REGEX.exec(attributes);
|
|
141
|
+
if (classMatch) {
|
|
142
|
+
const classes = classMatch[1].split(/\s+/).filter(Boolean);
|
|
143
|
+
if (classes.length > 0) {
|
|
144
|
+
selector += "." + classes.join(".");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const attrName of this.options.attributeNames) {
|
|
148
|
+
if (attributes.includes(attrName + "=")) {
|
|
149
|
+
selector += `[${attrName}]`;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return selector;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Decode HTML entities in attribute values.
|
|
157
|
+
*/
|
|
158
|
+
decodeHTMLEntities(text) {
|
|
159
|
+
return text.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCharCode(parseInt(code, 16)));
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var VueScanner = class extends HTMLScanner {
|
|
163
|
+
/**
|
|
164
|
+
* Extract hyperscript from Vue SFC.
|
|
165
|
+
*/
|
|
166
|
+
extract(source, filename) {
|
|
167
|
+
const templateMatch = /<template[^>]*>([\s\S]*?)<\/template>/i.exec(source);
|
|
168
|
+
if (!templateMatch) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
const template = templateMatch[1];
|
|
172
|
+
const templateOffset = templateMatch.index + templateMatch[0].indexOf(">") + 1;
|
|
173
|
+
const scripts = super.extract(template, filename);
|
|
174
|
+
const linesBefore = source.slice(0, templateOffset).split("\n").length - 1;
|
|
175
|
+
for (const script of scripts) {
|
|
176
|
+
script.location.line += linesBefore;
|
|
177
|
+
}
|
|
178
|
+
return scripts;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
var SvelteScanner = class extends HTMLScanner {
|
|
182
|
+
/**
|
|
183
|
+
* Extract hyperscript from Svelte component.
|
|
184
|
+
* Svelte components are HTML-like, so we can use the base scanner
|
|
185
|
+
* but need to exclude script and style blocks.
|
|
186
|
+
*/
|
|
187
|
+
extract(source, filename) {
|
|
188
|
+
let cleanedSource = source.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, (match) => " ".repeat(match.length)).replace(/<style[^>]*>[\s\S]*?<\/style>/gi, (match) => " ".repeat(match.length));
|
|
189
|
+
return super.extract(cleanedSource, filename);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
var JSXScanner = class {
|
|
193
|
+
constructor(options = {}) {
|
|
194
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Extract hyperscript from JSX source.
|
|
198
|
+
* Handles both regular attributes and spread attributes.
|
|
199
|
+
*/
|
|
200
|
+
extract(source, filename) {
|
|
201
|
+
const scripts = [];
|
|
202
|
+
for (const attrName of this.options.attributeNames) {
|
|
203
|
+
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
204
|
+
const stringRegex = new RegExp(`${escaped}\\s*=\\s*["']([^"']+)["']`, "g");
|
|
205
|
+
let match;
|
|
206
|
+
while ((match = stringRegex.exec(source)) !== null) {
|
|
207
|
+
const code = match[1];
|
|
208
|
+
if (code && code.trim()) {
|
|
209
|
+
scripts.push({
|
|
210
|
+
code,
|
|
211
|
+
location: this.calculateLocation(source, match.index, filename),
|
|
212
|
+
language: this.options.defaultLanguage,
|
|
213
|
+
attributeName: attrName
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const templateRegex = new RegExp(`${escaped}\\s*=\\s*\\{\\s*\`([^\`]+)\`\\s*\\}`, "g");
|
|
218
|
+
while ((match = templateRegex.exec(source)) !== null) {
|
|
219
|
+
const code = match[1];
|
|
220
|
+
if (code && code.trim()) {
|
|
221
|
+
scripts.push({
|
|
222
|
+
code,
|
|
223
|
+
location: this.calculateLocation(source, match.index, filename),
|
|
224
|
+
language: this.options.defaultLanguage,
|
|
225
|
+
attributeName: attrName
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const exprRegex = new RegExp(`${escaped}\\s*=\\s*\\{\\s*["']([^"']+)["']\\s*\\}`, "g");
|
|
230
|
+
while ((match = exprRegex.exec(source)) !== null) {
|
|
231
|
+
const code = match[1];
|
|
232
|
+
if (code && code.trim()) {
|
|
233
|
+
scripts.push({
|
|
234
|
+
code,
|
|
235
|
+
location: this.calculateLocation(source, match.index, filename),
|
|
236
|
+
language: this.options.defaultLanguage,
|
|
237
|
+
attributeName: attrName
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return scripts;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Calculate line and column from character offset.
|
|
246
|
+
*/
|
|
247
|
+
calculateLocation(source, offset, filename) {
|
|
248
|
+
const before = source.slice(0, offset);
|
|
249
|
+
const lines = before.split("\n");
|
|
250
|
+
const line = lines.length;
|
|
251
|
+
const column = (lines[lines.length - 1]?.length ?? 0) + 1;
|
|
252
|
+
return { file: filename, line, column };
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
async function scanFiles(files, readFile, options = {}) {
|
|
256
|
+
const result = {
|
|
257
|
+
scripts: [],
|
|
258
|
+
files: [],
|
|
259
|
+
errors: []
|
|
260
|
+
};
|
|
261
|
+
const htmlScanner = new HTMLScanner(options);
|
|
262
|
+
const vueScanner = new VueScanner(options);
|
|
263
|
+
const svelteScanner = new SvelteScanner(options);
|
|
264
|
+
const jsxScanner = new JSXScanner(options);
|
|
265
|
+
for (const file of files) {
|
|
266
|
+
try {
|
|
267
|
+
const content = await readFile(file);
|
|
268
|
+
let scripts = [];
|
|
269
|
+
if (file.endsWith(".vue")) {
|
|
270
|
+
scripts = vueScanner.extract(content, file);
|
|
271
|
+
} else if (file.endsWith(".svelte")) {
|
|
272
|
+
scripts = svelteScanner.extract(content, file);
|
|
273
|
+
} else if (file.match(/\.(jsx|tsx)$/)) {
|
|
274
|
+
scripts = jsxScanner.extract(content, file);
|
|
275
|
+
} else if (file.match(/\.(html|htm)$/)) {
|
|
276
|
+
scripts = htmlScanner.extract(content, file);
|
|
277
|
+
}
|
|
278
|
+
result.scripts.push(...scripts);
|
|
279
|
+
result.files.push(file);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
result.errors.push({
|
|
282
|
+
file,
|
|
283
|
+
error: error instanceof Error ? error.message : String(error)
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
function createScanner(filename, options) {
|
|
290
|
+
if (filename.endsWith(".vue")) {
|
|
291
|
+
return new VueScanner(options);
|
|
292
|
+
}
|
|
293
|
+
if (filename.endsWith(".svelte")) {
|
|
294
|
+
return new SvelteScanner(options);
|
|
295
|
+
}
|
|
296
|
+
if (filename.match(/\.(jsx|tsx)$/)) {
|
|
297
|
+
return new JSXScanner(options);
|
|
298
|
+
}
|
|
299
|
+
return new HTMLScanner(options);
|
|
300
|
+
}
|
|
301
|
+
var Analyzer = class {
|
|
302
|
+
/**
|
|
303
|
+
* Analyze an AST and return analysis results.
|
|
304
|
+
*/
|
|
305
|
+
analyze(ast) {
|
|
306
|
+
const visitor = new AnalysisVisitor();
|
|
307
|
+
visitor.visit(ast);
|
|
308
|
+
return visitor.getResult();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
var AnalysisVisitor = class {
|
|
312
|
+
constructor() {
|
|
313
|
+
this.commandsUsed = /* @__PURE__ */ new Set();
|
|
314
|
+
this.localVars = /* @__PURE__ */ new Map();
|
|
315
|
+
this.globalVars = /* @__PURE__ */ new Map();
|
|
316
|
+
this.contextVars = /* @__PURE__ */ new Set();
|
|
317
|
+
this.pureExpressions = [];
|
|
318
|
+
this.dynamicExpressions = [];
|
|
319
|
+
this.selectors = [];
|
|
320
|
+
this.selectorMap = /* @__PURE__ */ new Map();
|
|
321
|
+
this.domQueries = [];
|
|
322
|
+
this.eventTypes = [];
|
|
323
|
+
this.behaviors = [];
|
|
324
|
+
this.runtimeHelpers = /* @__PURE__ */ new Set();
|
|
325
|
+
this.warnings = [];
|
|
326
|
+
this.hasAsync = false;
|
|
327
|
+
this.hasLoops = false;
|
|
328
|
+
this.hasConditionals = false;
|
|
329
|
+
this.canThrow = false;
|
|
330
|
+
this.currentNestingDepth = 0;
|
|
331
|
+
this.maxNestingDepth = 0;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Visit an AST node and its children.
|
|
335
|
+
*/
|
|
336
|
+
visit(node) {
|
|
337
|
+
if (!node) return;
|
|
338
|
+
switch (node.type) {
|
|
339
|
+
case "event":
|
|
340
|
+
this.visitEvent(node);
|
|
341
|
+
break;
|
|
342
|
+
case "command":
|
|
343
|
+
this.visitCommand(node);
|
|
344
|
+
break;
|
|
345
|
+
case "if":
|
|
346
|
+
this.visitIf(node);
|
|
347
|
+
break;
|
|
348
|
+
case "repeat":
|
|
349
|
+
this.visitRepeat(node);
|
|
350
|
+
break;
|
|
351
|
+
case "foreach":
|
|
352
|
+
this.visitForEach(node);
|
|
353
|
+
break;
|
|
354
|
+
case "while":
|
|
355
|
+
this.visitWhile(node);
|
|
356
|
+
break;
|
|
357
|
+
case "variable":
|
|
358
|
+
this.visitVariable(node, "read");
|
|
359
|
+
break;
|
|
360
|
+
case "selector":
|
|
361
|
+
this.visitSelector(node);
|
|
362
|
+
break;
|
|
363
|
+
case "binary":
|
|
364
|
+
this.visitBinary(node);
|
|
365
|
+
break;
|
|
366
|
+
case "call":
|
|
367
|
+
this.visitCall(node);
|
|
368
|
+
break;
|
|
369
|
+
case "identifier":
|
|
370
|
+
this.visitIdentifier(node);
|
|
371
|
+
break;
|
|
372
|
+
default:
|
|
373
|
+
this.visitChildren(node);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Get the analysis result.
|
|
378
|
+
*/
|
|
379
|
+
getResult() {
|
|
380
|
+
return {
|
|
381
|
+
commandsUsed: this.commandsUsed,
|
|
382
|
+
variables: {
|
|
383
|
+
locals: this.localVars,
|
|
384
|
+
globals: this.globalVars,
|
|
385
|
+
contextVars: this.contextVars
|
|
386
|
+
},
|
|
387
|
+
expressions: {
|
|
388
|
+
pure: this.pureExpressions,
|
|
389
|
+
dynamic: this.dynamicExpressions,
|
|
390
|
+
selectors: this.selectors
|
|
391
|
+
},
|
|
392
|
+
controlFlow: {
|
|
393
|
+
hasAsync: this.hasAsync,
|
|
394
|
+
hasLoops: this.hasLoops,
|
|
395
|
+
hasConditionals: this.hasConditionals,
|
|
396
|
+
canThrow: this.canThrow,
|
|
397
|
+
maxNestingDepth: this.maxNestingDepth
|
|
398
|
+
},
|
|
399
|
+
dependencies: {
|
|
400
|
+
domQueries: [...new Set(this.domQueries)],
|
|
401
|
+
eventTypes: [...new Set(this.eventTypes)],
|
|
402
|
+
behaviors: [...new Set(this.behaviors)],
|
|
403
|
+
runtimeHelpers: Array.from(this.runtimeHelpers)
|
|
404
|
+
},
|
|
405
|
+
warnings: this.warnings
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
// ===========================================================================
|
|
409
|
+
// VISITOR METHODS
|
|
410
|
+
// ===========================================================================
|
|
411
|
+
visitEvent(node) {
|
|
412
|
+
this.eventTypes.push(node.event);
|
|
413
|
+
if (node.modifiers?.debounce) {
|
|
414
|
+
this.runtimeHelpers.add("debounce");
|
|
415
|
+
}
|
|
416
|
+
if (node.modifiers?.throttle) {
|
|
417
|
+
this.runtimeHelpers.add("throttle");
|
|
418
|
+
}
|
|
419
|
+
if (node.body) {
|
|
420
|
+
for (const child of node.body) {
|
|
421
|
+
this.visit(child);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
visitCommand(node) {
|
|
426
|
+
this.commandsUsed.add(node.name);
|
|
427
|
+
switch (node.name) {
|
|
428
|
+
case "fetch":
|
|
429
|
+
this.hasAsync = true;
|
|
430
|
+
this.runtimeHelpers.add("fetchJSON");
|
|
431
|
+
this.runtimeHelpers.add("fetchText");
|
|
432
|
+
break;
|
|
433
|
+
case "wait":
|
|
434
|
+
case "settle":
|
|
435
|
+
this.hasAsync = true;
|
|
436
|
+
this.runtimeHelpers.add("wait");
|
|
437
|
+
break;
|
|
438
|
+
case "toggle":
|
|
439
|
+
this.runtimeHelpers.add("toggle");
|
|
440
|
+
break;
|
|
441
|
+
case "set":
|
|
442
|
+
this.runtimeHelpers.add("setProp");
|
|
443
|
+
break;
|
|
444
|
+
case "put":
|
|
445
|
+
this.runtimeHelpers.add("put");
|
|
446
|
+
break;
|
|
447
|
+
case "increment":
|
|
448
|
+
case "decrement":
|
|
449
|
+
this.runtimeHelpers.add("setProp");
|
|
450
|
+
break;
|
|
451
|
+
case "send":
|
|
452
|
+
case "trigger":
|
|
453
|
+
this.runtimeHelpers.add("send");
|
|
454
|
+
break;
|
|
455
|
+
case "halt":
|
|
456
|
+
case "exit":
|
|
457
|
+
this.canThrow = true;
|
|
458
|
+
break;
|
|
459
|
+
case "throw":
|
|
460
|
+
this.canThrow = true;
|
|
461
|
+
break;
|
|
462
|
+
case "call":
|
|
463
|
+
if (node.args?.[0]?.type === "identifier") {
|
|
464
|
+
this.behaviors.push(node.args[0].value);
|
|
465
|
+
}
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
if (node.args) {
|
|
469
|
+
for (const arg of node.args) {
|
|
470
|
+
this.visit(arg);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (node.target) {
|
|
474
|
+
this.visit(node.target);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
visitIf(node) {
|
|
478
|
+
this.hasConditionals = true;
|
|
479
|
+
this.enterNesting();
|
|
480
|
+
this.visit(node.condition);
|
|
481
|
+
for (const child of node.thenBranch) {
|
|
482
|
+
this.visit(child);
|
|
483
|
+
}
|
|
484
|
+
if (node.elseIfBranches) {
|
|
485
|
+
for (const branch of node.elseIfBranches) {
|
|
486
|
+
this.visit(branch.condition);
|
|
487
|
+
for (const child of branch.body) {
|
|
488
|
+
this.visit(child);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (node.elseBranch) {
|
|
493
|
+
for (const child of node.elseBranch) {
|
|
494
|
+
this.visit(child);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
this.exitNesting();
|
|
498
|
+
}
|
|
499
|
+
visitRepeat(node) {
|
|
500
|
+
this.hasLoops = true;
|
|
501
|
+
this.enterNesting();
|
|
502
|
+
if (node.count && typeof node.count !== "number") {
|
|
503
|
+
this.visit(node.count);
|
|
504
|
+
}
|
|
505
|
+
if (node.whileCondition) {
|
|
506
|
+
this.visit(node.whileCondition);
|
|
507
|
+
}
|
|
508
|
+
for (const child of node.body) {
|
|
509
|
+
this.visit(child);
|
|
510
|
+
}
|
|
511
|
+
this.exitNesting();
|
|
512
|
+
}
|
|
513
|
+
visitForEach(node) {
|
|
514
|
+
this.hasLoops = true;
|
|
515
|
+
this.enterNesting();
|
|
516
|
+
this.registerVariable(node.itemName, "local", "write");
|
|
517
|
+
if (node.indexName) {
|
|
518
|
+
this.registerVariable(node.indexName, "local", "write");
|
|
519
|
+
}
|
|
520
|
+
this.visit(node.collection);
|
|
521
|
+
for (const child of node.body) {
|
|
522
|
+
this.visit(child);
|
|
523
|
+
}
|
|
524
|
+
this.exitNesting();
|
|
525
|
+
}
|
|
526
|
+
visitWhile(node) {
|
|
527
|
+
this.hasLoops = true;
|
|
528
|
+
this.enterNesting();
|
|
529
|
+
this.visit(node.condition);
|
|
530
|
+
for (const child of node.body) {
|
|
531
|
+
this.visit(child);
|
|
532
|
+
}
|
|
533
|
+
this.exitNesting();
|
|
534
|
+
}
|
|
535
|
+
visitVariable(node, access) {
|
|
536
|
+
const name = node.name.startsWith(":") || node.name.startsWith("$") ? node.name.slice(1) : node.name;
|
|
537
|
+
this.registerVariable(name, node.scope, access);
|
|
538
|
+
}
|
|
539
|
+
visitSelector(node) {
|
|
540
|
+
const selector = node.value;
|
|
541
|
+
if (!this.selectorMap.has(selector)) {
|
|
542
|
+
const info = {
|
|
543
|
+
selector,
|
|
544
|
+
usages: [],
|
|
545
|
+
isId: selector.startsWith("#") && !selector.includes(" "),
|
|
546
|
+
canCache: this.canCacheSelector(selector)
|
|
547
|
+
};
|
|
548
|
+
this.selectorMap.set(selector, info);
|
|
549
|
+
this.selectors.push(info);
|
|
550
|
+
}
|
|
551
|
+
this.selectorMap.get(selector).usages.push(this.currentLocation ?? { file: "", line: 0, column: 0 });
|
|
552
|
+
this.domQueries.push(selector);
|
|
553
|
+
if (this.isPureSelector(selector)) {
|
|
554
|
+
this.pureExpressions.push(node);
|
|
555
|
+
} else {
|
|
556
|
+
this.dynamicExpressions.push(node);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
visitBinary(node) {
|
|
560
|
+
this.visit(node.left);
|
|
561
|
+
this.visit(node.right);
|
|
562
|
+
if (this.isPureExpression(node)) {
|
|
563
|
+
this.pureExpressions.push(node);
|
|
564
|
+
}
|
|
565
|
+
switch (node.operator) {
|
|
566
|
+
case "contains":
|
|
567
|
+
this.runtimeHelpers.add("contains");
|
|
568
|
+
break;
|
|
569
|
+
case "matches":
|
|
570
|
+
this.runtimeHelpers.add("matches");
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
visitCall(node) {
|
|
575
|
+
this.visit(node.callee);
|
|
576
|
+
if (node.args) {
|
|
577
|
+
for (const arg of node.args) {
|
|
578
|
+
this.visit(arg);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
this.dynamicExpressions.push(node);
|
|
582
|
+
}
|
|
583
|
+
visitIdentifier(node) {
|
|
584
|
+
const value = node.value ?? node.name;
|
|
585
|
+
if (!value) return;
|
|
586
|
+
switch (value) {
|
|
587
|
+
case "me":
|
|
588
|
+
case "my":
|
|
589
|
+
this.contextVars.add("me");
|
|
590
|
+
break;
|
|
591
|
+
case "you":
|
|
592
|
+
case "your":
|
|
593
|
+
this.contextVars.add("you");
|
|
594
|
+
break;
|
|
595
|
+
case "it":
|
|
596
|
+
case "result":
|
|
597
|
+
this.contextVars.add("it");
|
|
598
|
+
break;
|
|
599
|
+
case "event":
|
|
600
|
+
this.contextVars.add("event");
|
|
601
|
+
break;
|
|
602
|
+
case "body":
|
|
603
|
+
case "document":
|
|
604
|
+
case "window":
|
|
605
|
+
break;
|
|
606
|
+
default:
|
|
607
|
+
if (value.charAt(0) === value.charAt(0).toUpperCase()) {
|
|
608
|
+
this.behaviors.push(value);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
// ===========================================================================
|
|
613
|
+
// HELPER METHODS
|
|
614
|
+
// ===========================================================================
|
|
615
|
+
visitChildren(node) {
|
|
616
|
+
for (const key of Object.keys(node)) {
|
|
617
|
+
const value = node[key];
|
|
618
|
+
if (value && typeof value === "object") {
|
|
619
|
+
if (Array.isArray(value)) {
|
|
620
|
+
for (const item of value) {
|
|
621
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
622
|
+
this.visit(item);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
} else if ("type" in value) {
|
|
626
|
+
this.visit(value);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
registerVariable(name, scope, access) {
|
|
632
|
+
const map = scope === "global" ? this.globalVars : this.localVars;
|
|
633
|
+
if (!map.has(name)) {
|
|
634
|
+
map.set(name, {
|
|
635
|
+
name,
|
|
636
|
+
scope,
|
|
637
|
+
reads: [],
|
|
638
|
+
writes: [],
|
|
639
|
+
type: "unknown"
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
const info = map.get(name);
|
|
643
|
+
const location = this.currentLocation ?? { file: "", line: 0, column: 0 };
|
|
644
|
+
if (access === "read") {
|
|
645
|
+
info.reads.push(location);
|
|
646
|
+
} else {
|
|
647
|
+
info.writes.push(location);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
enterNesting() {
|
|
651
|
+
this.currentNestingDepth++;
|
|
652
|
+
this.maxNestingDepth = Math.max(this.maxNestingDepth, this.currentNestingDepth);
|
|
653
|
+
}
|
|
654
|
+
exitNesting() {
|
|
655
|
+
this.currentNestingDepth--;
|
|
656
|
+
}
|
|
657
|
+
canCacheSelector(selector) {
|
|
658
|
+
const dynamicPseudo = /:(not|has|is|where|nth-|first-|last-|only-|empty|focus|hover|active|visited)/i;
|
|
659
|
+
return !dynamicPseudo.test(selector);
|
|
660
|
+
}
|
|
661
|
+
isPureSelector(selector) {
|
|
662
|
+
return /^[#.][a-zA-Z_][a-zA-Z0-9_-]*$/.test(selector);
|
|
663
|
+
}
|
|
664
|
+
isPureExpression(node) {
|
|
665
|
+
if (!node) return true;
|
|
666
|
+
switch (node.type) {
|
|
667
|
+
case "literal":
|
|
668
|
+
return true;
|
|
669
|
+
case "identifier": {
|
|
670
|
+
const value = node.value;
|
|
671
|
+
return !["me", "you", "it", "result", "event"].includes(value ?? "");
|
|
672
|
+
}
|
|
673
|
+
case "binary": {
|
|
674
|
+
const bin = node;
|
|
675
|
+
return this.isPureExpression(bin.left) && this.isPureExpression(bin.right);
|
|
676
|
+
}
|
|
677
|
+
default:
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
function analyze(ast) {
|
|
683
|
+
const analyzer = new Analyzer();
|
|
684
|
+
return analyzer.analyze(ast);
|
|
685
|
+
}
|
|
686
|
+
var ConstantFoldingPass = class {
|
|
687
|
+
constructor() {
|
|
688
|
+
this.name = "constant-folding";
|
|
689
|
+
}
|
|
690
|
+
shouldRun(analysis) {
|
|
691
|
+
return analysis.expressions.pure.length > 0;
|
|
692
|
+
}
|
|
693
|
+
transform(ast, _analysis) {
|
|
694
|
+
return this.foldNode(ast);
|
|
695
|
+
}
|
|
696
|
+
foldNode(node) {
|
|
697
|
+
if (!node) return node;
|
|
698
|
+
const processed = this.processChildren(node);
|
|
699
|
+
if (processed.type === "binary") {
|
|
700
|
+
return this.foldBinary(processed);
|
|
701
|
+
}
|
|
702
|
+
return processed;
|
|
703
|
+
}
|
|
704
|
+
processChildren(node) {
|
|
705
|
+
const result = { ...node };
|
|
706
|
+
for (const key of Object.keys(node)) {
|
|
707
|
+
const value = node[key];
|
|
708
|
+
if (Array.isArray(value)) {
|
|
709
|
+
result[key] = value.map(
|
|
710
|
+
(item) => item && typeof item === "object" && "type" in item ? this.foldNode(item) : item
|
|
711
|
+
);
|
|
712
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
713
|
+
result[key] = this.foldNode(value);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return result;
|
|
717
|
+
}
|
|
718
|
+
foldBinary(node) {
|
|
719
|
+
const left = node.left;
|
|
720
|
+
const right = node.right;
|
|
721
|
+
if (left.type !== "literal" || right.type !== "literal") {
|
|
722
|
+
return node;
|
|
723
|
+
}
|
|
724
|
+
const leftVal = left.value;
|
|
725
|
+
const rightVal = right.value;
|
|
726
|
+
let result;
|
|
727
|
+
switch (node.operator) {
|
|
728
|
+
// Arithmetic
|
|
729
|
+
case "+":
|
|
730
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
731
|
+
result = leftVal + rightVal;
|
|
732
|
+
}
|
|
733
|
+
break;
|
|
734
|
+
case "-":
|
|
735
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
736
|
+
result = leftVal - rightVal;
|
|
737
|
+
}
|
|
738
|
+
break;
|
|
739
|
+
case "*":
|
|
740
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
741
|
+
result = leftVal * rightVal;
|
|
742
|
+
}
|
|
743
|
+
break;
|
|
744
|
+
case "/":
|
|
745
|
+
if (typeof leftVal === "number" && typeof rightVal === "number" && rightVal !== 0) {
|
|
746
|
+
result = leftVal / rightVal;
|
|
747
|
+
}
|
|
748
|
+
break;
|
|
749
|
+
case "%":
|
|
750
|
+
if (typeof leftVal === "number" && typeof rightVal === "number" && rightVal !== 0) {
|
|
751
|
+
result = leftVal % rightVal;
|
|
752
|
+
}
|
|
753
|
+
break;
|
|
754
|
+
// String concatenation
|
|
755
|
+
case "&":
|
|
756
|
+
result = String(leftVal) + String(rightVal);
|
|
757
|
+
break;
|
|
758
|
+
// Logical
|
|
759
|
+
case "and":
|
|
760
|
+
case "&&":
|
|
761
|
+
result = leftVal && rightVal;
|
|
762
|
+
break;
|
|
763
|
+
case "or":
|
|
764
|
+
case "||":
|
|
765
|
+
result = leftVal || rightVal;
|
|
766
|
+
break;
|
|
767
|
+
// Comparison
|
|
768
|
+
case "is":
|
|
769
|
+
case "==":
|
|
770
|
+
result = leftVal === rightVal;
|
|
771
|
+
break;
|
|
772
|
+
case "is not":
|
|
773
|
+
case "!=":
|
|
774
|
+
result = leftVal !== rightVal;
|
|
775
|
+
break;
|
|
776
|
+
case "<":
|
|
777
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
778
|
+
result = leftVal < rightVal;
|
|
779
|
+
}
|
|
780
|
+
break;
|
|
781
|
+
case ">":
|
|
782
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
783
|
+
result = leftVal > rightVal;
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
case "<=":
|
|
787
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
788
|
+
result = leftVal <= rightVal;
|
|
789
|
+
}
|
|
790
|
+
break;
|
|
791
|
+
case ">=":
|
|
792
|
+
if (typeof leftVal === "number" && typeof rightVal === "number") {
|
|
793
|
+
result = leftVal >= rightVal;
|
|
794
|
+
}
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
if (result !== void 0) {
|
|
798
|
+
return {
|
|
799
|
+
type: "literal",
|
|
800
|
+
value: result
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
return node;
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
var SelectorCachingPass = class {
|
|
807
|
+
constructor() {
|
|
808
|
+
this.name = "selector-caching";
|
|
809
|
+
}
|
|
810
|
+
shouldRun(analysis) {
|
|
811
|
+
return analysis.expressions.selectors.some((s) => s.usages.length > 1 && s.canCache);
|
|
812
|
+
}
|
|
813
|
+
transform(ast, analysis) {
|
|
814
|
+
const selectorsToCache = /* @__PURE__ */ new Set();
|
|
815
|
+
for (const info of analysis.expressions.selectors) {
|
|
816
|
+
if (info.usages.length > 1 && info.canCache) {
|
|
817
|
+
selectorsToCache.add(info.selector);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (selectorsToCache.size === 0) {
|
|
821
|
+
return ast;
|
|
822
|
+
}
|
|
823
|
+
return this.markSelectors(ast, selectorsToCache);
|
|
824
|
+
}
|
|
825
|
+
markSelectors(node, toCache) {
|
|
826
|
+
if (!node) return node;
|
|
827
|
+
if (node.type === "selector") {
|
|
828
|
+
const selector = node.value;
|
|
829
|
+
if (toCache.has(selector)) {
|
|
830
|
+
return {
|
|
831
|
+
...node,
|
|
832
|
+
_cached: true,
|
|
833
|
+
_cacheKey: this.generateCacheKey(selector)
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
return node;
|
|
837
|
+
}
|
|
838
|
+
const result = { ...node };
|
|
839
|
+
for (const key of Object.keys(node)) {
|
|
840
|
+
const value = node[key];
|
|
841
|
+
if (Array.isArray(value)) {
|
|
842
|
+
result[key] = value.map(
|
|
843
|
+
(item) => item && typeof item === "object" && "type" in item ? this.markSelectors(item, toCache) : item
|
|
844
|
+
);
|
|
845
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
846
|
+
result[key] = this.markSelectors(value, toCache);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return result;
|
|
850
|
+
}
|
|
851
|
+
generateCacheKey(selector) {
|
|
852
|
+
return "_sel_" + selector.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").slice(0, 20);
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
var DeadCodeEliminationPass = class {
|
|
856
|
+
constructor() {
|
|
857
|
+
this.name = "dead-code-elimination";
|
|
858
|
+
}
|
|
859
|
+
shouldRun(analysis) {
|
|
860
|
+
return analysis.controlFlow.canThrow;
|
|
861
|
+
}
|
|
862
|
+
transform(ast, _analysis) {
|
|
863
|
+
return this.eliminateDeadCode(ast);
|
|
864
|
+
}
|
|
865
|
+
eliminateDeadCode(node) {
|
|
866
|
+
if (!node) return node;
|
|
867
|
+
if (Array.isArray(node)) {
|
|
868
|
+
return this.eliminateFromArray(node);
|
|
869
|
+
}
|
|
870
|
+
const result = { ...node };
|
|
871
|
+
for (const key of ["body", "thenBranch", "elseBranch"]) {
|
|
872
|
+
const value = node[key];
|
|
873
|
+
if (Array.isArray(value)) {
|
|
874
|
+
result[key] = this.eliminateFromArray(value);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
for (const key of Object.keys(node)) {
|
|
878
|
+
if (["body", "thenBranch", "elseBranch"].includes(key)) continue;
|
|
879
|
+
const value = node[key];
|
|
880
|
+
if (value && typeof value === "object" && "type" in value) {
|
|
881
|
+
result[key] = this.eliminateDeadCode(value);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
}
|
|
886
|
+
eliminateFromArray(nodes) {
|
|
887
|
+
const result = [];
|
|
888
|
+
for (const node of nodes) {
|
|
889
|
+
const processed = this.eliminateDeadCode(node);
|
|
890
|
+
result.push(processed);
|
|
891
|
+
if (this.isTerminator(node)) {
|
|
892
|
+
break;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
897
|
+
isTerminator(node) {
|
|
898
|
+
if (node.type === "command") {
|
|
899
|
+
return ["halt", "exit", "return", "throw"].includes(node.name);
|
|
900
|
+
}
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
var LoopUnrollingPass = class {
|
|
905
|
+
constructor() {
|
|
906
|
+
this.name = "loop-unrolling";
|
|
907
|
+
this.maxUnrollCount = 5;
|
|
908
|
+
}
|
|
909
|
+
shouldRun(analysis) {
|
|
910
|
+
return analysis.controlFlow.hasLoops;
|
|
911
|
+
}
|
|
912
|
+
transform(ast, _analysis) {
|
|
913
|
+
return this.unrollLoops(ast);
|
|
914
|
+
}
|
|
915
|
+
unrollLoops(node) {
|
|
916
|
+
if (!node) return node;
|
|
917
|
+
if (node.type === "repeat") {
|
|
918
|
+
const unrolled = this.tryUnroll(node);
|
|
919
|
+
if (unrolled) {
|
|
920
|
+
return unrolled;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const result = { ...node };
|
|
924
|
+
for (const key of Object.keys(node)) {
|
|
925
|
+
const value = node[key];
|
|
926
|
+
if (Array.isArray(value)) {
|
|
927
|
+
result[key] = value.map(
|
|
928
|
+
(item) => item && typeof item === "object" && "type" in item ? this.unrollLoops(item) : item
|
|
929
|
+
);
|
|
930
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
931
|
+
result[key] = this.unrollLoops(value);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return result;
|
|
935
|
+
}
|
|
936
|
+
tryUnroll(node) {
|
|
937
|
+
const repeatNode = node;
|
|
938
|
+
if (typeof repeatNode.count !== "number") {
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
const count = repeatNode.count;
|
|
942
|
+
const body = repeatNode.body ?? [];
|
|
943
|
+
if (count > this.maxUnrollCount) {
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
if (body.length > 3) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
if (this.usesIndex(body)) {
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
const unrolled = [];
|
|
953
|
+
for (let i = 0; i < count; i++) {
|
|
954
|
+
unrolled.push(...this.cloneBody(body));
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
type: "sequence",
|
|
958
|
+
commands: unrolled,
|
|
959
|
+
_unrolled: true
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
usesIndex(nodes) {
|
|
963
|
+
for (const node of nodes) {
|
|
964
|
+
if (this.nodeUsesIndex(node)) {
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
nodeUsesIndex(node) {
|
|
971
|
+
if (!node) return false;
|
|
972
|
+
if (node.type === "identifier") {
|
|
973
|
+
if (node.value === "index" || node.value === ":index") {
|
|
974
|
+
return true;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (node.type === "variable") {
|
|
978
|
+
if (node.name === "index" || node.name === ":index") {
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
for (const key of Object.keys(node)) {
|
|
983
|
+
const value = node[key];
|
|
984
|
+
if (Array.isArray(value)) {
|
|
985
|
+
for (const item of value) {
|
|
986
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
987
|
+
if (this.nodeUsesIndex(item)) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
993
|
+
if (this.nodeUsesIndex(value)) {
|
|
994
|
+
return true;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
cloneBody(nodes) {
|
|
1001
|
+
return JSON.parse(JSON.stringify(nodes));
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
var OptimizationPipeline = class {
|
|
1005
|
+
constructor() {
|
|
1006
|
+
this.passes = [
|
|
1007
|
+
new ConstantFoldingPass(),
|
|
1008
|
+
new SelectorCachingPass(),
|
|
1009
|
+
new DeadCodeEliminationPass(),
|
|
1010
|
+
new LoopUnrollingPass()
|
|
1011
|
+
];
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Run all applicable optimizations on the AST.
|
|
1015
|
+
*/
|
|
1016
|
+
optimize(ast, analysis, level = 2) {
|
|
1017
|
+
if (level === 0) {
|
|
1018
|
+
return ast;
|
|
1019
|
+
}
|
|
1020
|
+
let current = ast;
|
|
1021
|
+
const appliedOptimizations = [];
|
|
1022
|
+
const passesToRun = level === 1 ? this.passes.slice(0, 2) : this.passes;
|
|
1023
|
+
for (const pass of passesToRun) {
|
|
1024
|
+
if (pass.shouldRun(analysis)) {
|
|
1025
|
+
current = pass.transform(current, analysis);
|
|
1026
|
+
appliedOptimizations.push(pass.name);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
...current,
|
|
1031
|
+
_optimizations: appliedOptimizations
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Add a custom optimization pass.
|
|
1036
|
+
*/
|
|
1037
|
+
addPass(pass) {
|
|
1038
|
+
this.passes.push(pass);
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
function createOptimizer() {
|
|
1042
|
+
return new OptimizationPipeline();
|
|
1043
|
+
}
|
|
1044
|
+
function optimize(ast, analysis, level = 2) {
|
|
1045
|
+
const pipeline = new OptimizationPipeline();
|
|
1046
|
+
return pipeline.optimize(ast, analysis, level);
|
|
1047
|
+
}
|
|
1048
|
+
function sanitizeClassName(name) {
|
|
1049
|
+
if (!/^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) {
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
return name;
|
|
1053
|
+
}
|
|
1054
|
+
function sanitizeSelector(selector) {
|
|
1055
|
+
return selector.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\0/g, "");
|
|
1056
|
+
}
|
|
1057
|
+
function sanitizeIdentifier(name) {
|
|
1058
|
+
return name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
1059
|
+
}
|
|
1060
|
+
var ExpressionCodegen = class {
|
|
1061
|
+
constructor(ctx) {
|
|
1062
|
+
this.ctx = ctx;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Generate JavaScript code for an expression.
|
|
1066
|
+
*/
|
|
1067
|
+
generate(node) {
|
|
1068
|
+
if (!node) {
|
|
1069
|
+
return "undefined";
|
|
1070
|
+
}
|
|
1071
|
+
switch (node.type) {
|
|
1072
|
+
case "literal":
|
|
1073
|
+
return this.generateLiteral(node);
|
|
1074
|
+
case "identifier":
|
|
1075
|
+
return this.generateIdentifier(node);
|
|
1076
|
+
case "selector":
|
|
1077
|
+
return this.generateSelector(node);
|
|
1078
|
+
case "variable":
|
|
1079
|
+
return this.generateVariable(node);
|
|
1080
|
+
case "binary":
|
|
1081
|
+
return this.generateBinary(node);
|
|
1082
|
+
case "member":
|
|
1083
|
+
return this.generateMember(node);
|
|
1084
|
+
case "possessive":
|
|
1085
|
+
return this.generatePossessive(node);
|
|
1086
|
+
case "call":
|
|
1087
|
+
return this.generateCall(node);
|
|
1088
|
+
case "positional":
|
|
1089
|
+
return this.generatePositional(node);
|
|
1090
|
+
case "array":
|
|
1091
|
+
return this.generateArray(node);
|
|
1092
|
+
case "object":
|
|
1093
|
+
return this.generateObject(node);
|
|
1094
|
+
case "template":
|
|
1095
|
+
return this.generateTemplate(node);
|
|
1096
|
+
case "unary":
|
|
1097
|
+
return this.generateUnary(node);
|
|
1098
|
+
case "conditional":
|
|
1099
|
+
return this.generateConditional(node);
|
|
1100
|
+
case "sequence":
|
|
1101
|
+
return "undefined";
|
|
1102
|
+
default:
|
|
1103
|
+
if ("value" in node) {
|
|
1104
|
+
return JSON.stringify(node.value);
|
|
1105
|
+
}
|
|
1106
|
+
throw new Error(`Unknown expression type: ${node.type}`);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
// ===========================================================================
|
|
1110
|
+
// LITERAL EXPRESSIONS
|
|
1111
|
+
// ===========================================================================
|
|
1112
|
+
generateLiteral(node) {
|
|
1113
|
+
const value = node.value;
|
|
1114
|
+
if (value === null) {
|
|
1115
|
+
return "null";
|
|
1116
|
+
}
|
|
1117
|
+
if (value === void 0) {
|
|
1118
|
+
return "undefined";
|
|
1119
|
+
}
|
|
1120
|
+
if (typeof value === "string") {
|
|
1121
|
+
return JSON.stringify(value);
|
|
1122
|
+
}
|
|
1123
|
+
if (typeof value === "number") {
|
|
1124
|
+
return String(value);
|
|
1125
|
+
}
|
|
1126
|
+
if (typeof value === "boolean") {
|
|
1127
|
+
return String(value);
|
|
1128
|
+
}
|
|
1129
|
+
return JSON.stringify(value);
|
|
1130
|
+
}
|
|
1131
|
+
// ===========================================================================
|
|
1132
|
+
// IDENTIFIER EXPRESSIONS
|
|
1133
|
+
// ===========================================================================
|
|
1134
|
+
generateIdentifier(node) {
|
|
1135
|
+
const name = node.value ?? node.name ?? "";
|
|
1136
|
+
switch (name) {
|
|
1137
|
+
case "me":
|
|
1138
|
+
case "my":
|
|
1139
|
+
return "_ctx.me";
|
|
1140
|
+
case "you":
|
|
1141
|
+
case "your":
|
|
1142
|
+
return "_ctx.you";
|
|
1143
|
+
case "it":
|
|
1144
|
+
case "result":
|
|
1145
|
+
return "_ctx.it";
|
|
1146
|
+
case "event":
|
|
1147
|
+
return "_ctx.event";
|
|
1148
|
+
case "body":
|
|
1149
|
+
return "document.body";
|
|
1150
|
+
case "document":
|
|
1151
|
+
return "document";
|
|
1152
|
+
case "window":
|
|
1153
|
+
return "window";
|
|
1154
|
+
case "true":
|
|
1155
|
+
return "true";
|
|
1156
|
+
case "false":
|
|
1157
|
+
return "false";
|
|
1158
|
+
case "null":
|
|
1159
|
+
return "null";
|
|
1160
|
+
case "undefined":
|
|
1161
|
+
return "undefined";
|
|
1162
|
+
default:
|
|
1163
|
+
if (name.startsWith(":")) {
|
|
1164
|
+
const varName = sanitizeIdentifier(name.slice(1));
|
|
1165
|
+
return `_ctx.locals.get('${varName}')`;
|
|
1166
|
+
}
|
|
1167
|
+
if (name.startsWith("$") || name.startsWith("::")) {
|
|
1168
|
+
const varName = sanitizeIdentifier(name.replace(/^(\$|::)/, ""));
|
|
1169
|
+
this.ctx.requireHelper("globals");
|
|
1170
|
+
return `_rt.globals.get('${varName}')`;
|
|
1171
|
+
}
|
|
1172
|
+
return sanitizeIdentifier(name);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
// ===========================================================================
|
|
1176
|
+
// SELECTOR EXPRESSIONS
|
|
1177
|
+
// ===========================================================================
|
|
1178
|
+
generateSelector(node) {
|
|
1179
|
+
const selector = node.value;
|
|
1180
|
+
const sanitized = sanitizeSelector(selector);
|
|
1181
|
+
if (this.ctx.canCacheSelector(selector)) {
|
|
1182
|
+
return this.ctx.getCachedSelector(selector);
|
|
1183
|
+
}
|
|
1184
|
+
if (selector.startsWith("#") && !selector.includes(" ") && !selector.includes(".")) {
|
|
1185
|
+
const id = selector.slice(1);
|
|
1186
|
+
return `document.getElementById('${sanitizeSelector(id)}')`;
|
|
1187
|
+
}
|
|
1188
|
+
return `document.querySelector('${sanitized}')`;
|
|
1189
|
+
}
|
|
1190
|
+
// ===========================================================================
|
|
1191
|
+
// VARIABLE EXPRESSIONS
|
|
1192
|
+
// ===========================================================================
|
|
1193
|
+
generateVariable(node) {
|
|
1194
|
+
const name = node.name;
|
|
1195
|
+
const varName = sanitizeIdentifier(
|
|
1196
|
+
name.startsWith(":") || name.startsWith("$") ? name.slice(1) : name
|
|
1197
|
+
);
|
|
1198
|
+
switch (node.scope) {
|
|
1199
|
+
case "local":
|
|
1200
|
+
return `_ctx.locals.get('${varName}')`;
|
|
1201
|
+
case "global":
|
|
1202
|
+
this.ctx.requireHelper("globals");
|
|
1203
|
+
return `_rt.globals.get('${varName}')`;
|
|
1204
|
+
case "element":
|
|
1205
|
+
return `_ctx.me.${varName}`;
|
|
1206
|
+
default:
|
|
1207
|
+
return `_ctx.locals.get('${varName}')`;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
// ===========================================================================
|
|
1211
|
+
// BINARY EXPRESSIONS
|
|
1212
|
+
// ===========================================================================
|
|
1213
|
+
generateBinary(node) {
|
|
1214
|
+
const left = this.generate(node.left);
|
|
1215
|
+
const right = this.generate(node.right);
|
|
1216
|
+
const op = node.operator;
|
|
1217
|
+
switch (op) {
|
|
1218
|
+
// Equality
|
|
1219
|
+
case "is":
|
|
1220
|
+
case "==":
|
|
1221
|
+
return `(${left} === ${right})`;
|
|
1222
|
+
case "is not":
|
|
1223
|
+
case "!=":
|
|
1224
|
+
return `(${left} !== ${right})`;
|
|
1225
|
+
// Comparison
|
|
1226
|
+
case "<":
|
|
1227
|
+
case ">":
|
|
1228
|
+
case "<=":
|
|
1229
|
+
case ">=":
|
|
1230
|
+
return `(${left} ${op} ${right})`;
|
|
1231
|
+
// Arithmetic
|
|
1232
|
+
case "+":
|
|
1233
|
+
case "-":
|
|
1234
|
+
case "*":
|
|
1235
|
+
case "/":
|
|
1236
|
+
case "%":
|
|
1237
|
+
return `(${left} ${op} ${right})`;
|
|
1238
|
+
// Logical
|
|
1239
|
+
case "and":
|
|
1240
|
+
case "&&":
|
|
1241
|
+
return `(${left} && ${right})`;
|
|
1242
|
+
case "or":
|
|
1243
|
+
case "||":
|
|
1244
|
+
return `(${left} || ${right})`;
|
|
1245
|
+
// String/collection operators
|
|
1246
|
+
case "contains":
|
|
1247
|
+
this.ctx.requireHelper("contains");
|
|
1248
|
+
return `_rt.contains(${left}, ${right})`;
|
|
1249
|
+
case "matches":
|
|
1250
|
+
this.ctx.requireHelper("matches");
|
|
1251
|
+
return `_rt.matches(${left}, ${right})`;
|
|
1252
|
+
case "starts with":
|
|
1253
|
+
return `${left}.startsWith(${right})`;
|
|
1254
|
+
case "ends with":
|
|
1255
|
+
return `${left}.endsWith(${right})`;
|
|
1256
|
+
// Class check
|
|
1257
|
+
case "has":
|
|
1258
|
+
if (node.right.type === "selector") {
|
|
1259
|
+
const sel = node.right.value;
|
|
1260
|
+
if (sel.startsWith(".")) {
|
|
1261
|
+
const className = sanitizeClassName(sel.slice(1));
|
|
1262
|
+
if (className) {
|
|
1263
|
+
return `${left}.classList.contains('${className}')`;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
this.ctx.requireHelper("contains");
|
|
1268
|
+
return `_rt.contains(${left}, ${right})`;
|
|
1269
|
+
// Type check
|
|
1270
|
+
case "is a":
|
|
1271
|
+
case "is an":
|
|
1272
|
+
return `(typeof ${left} === ${right} || ${left} instanceof ${right})`;
|
|
1273
|
+
case "is not a":
|
|
1274
|
+
case "is not an":
|
|
1275
|
+
return `(typeof ${left} !== ${right} && !(${left} instanceof ${right}))`;
|
|
1276
|
+
// Concatenation
|
|
1277
|
+
case "&":
|
|
1278
|
+
return `(String(${left}) + String(${right}))`;
|
|
1279
|
+
default:
|
|
1280
|
+
return `(${left} ${op} ${right})`;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
// ===========================================================================
|
|
1284
|
+
// MEMBER EXPRESSIONS
|
|
1285
|
+
// ===========================================================================
|
|
1286
|
+
generateMember(node) {
|
|
1287
|
+
const object = this.generate(node.object);
|
|
1288
|
+
if (typeof node.property === "string") {
|
|
1289
|
+
const prop = node.property;
|
|
1290
|
+
if (prop.startsWith("*")) {
|
|
1291
|
+
const styleProp = prop.slice(1);
|
|
1292
|
+
return `${object}.style.${styleProp}`;
|
|
1293
|
+
}
|
|
1294
|
+
if (prop.startsWith("@")) {
|
|
1295
|
+
const attrName = prop.slice(1);
|
|
1296
|
+
return `${object}.getAttribute('${sanitizeSelector(attrName)}')`;
|
|
1297
|
+
}
|
|
1298
|
+
if (node.computed) {
|
|
1299
|
+
return `${object}[${JSON.stringify(prop)}]`;
|
|
1300
|
+
}
|
|
1301
|
+
return `${object}.${sanitizeIdentifier(prop)}`;
|
|
1302
|
+
}
|
|
1303
|
+
const propExpr = this.generate(node.property);
|
|
1304
|
+
return `${object}[${propExpr}]`;
|
|
1305
|
+
}
|
|
1306
|
+
// ===========================================================================
|
|
1307
|
+
// POSSESSIVE EXPRESSIONS
|
|
1308
|
+
// ===========================================================================
|
|
1309
|
+
generatePossessive(node) {
|
|
1310
|
+
const object = this.generate(node.object);
|
|
1311
|
+
const property = node.property;
|
|
1312
|
+
if (property.startsWith("*")) {
|
|
1313
|
+
const styleProp = property.slice(1);
|
|
1314
|
+
return `${object}.style.${styleProp}`;
|
|
1315
|
+
}
|
|
1316
|
+
if (property.startsWith("@")) {
|
|
1317
|
+
const attrName = property.slice(1);
|
|
1318
|
+
return `${object}.getAttribute('${sanitizeSelector(attrName)}')`;
|
|
1319
|
+
}
|
|
1320
|
+
if (property === "values") {
|
|
1321
|
+
this.ctx.requireHelper("getValues");
|
|
1322
|
+
return `_rt.getValues(${object})`;
|
|
1323
|
+
}
|
|
1324
|
+
const domProps = [
|
|
1325
|
+
"value",
|
|
1326
|
+
"textContent",
|
|
1327
|
+
"innerHTML",
|
|
1328
|
+
"innerText",
|
|
1329
|
+
"outerHTML",
|
|
1330
|
+
"checked",
|
|
1331
|
+
"disabled",
|
|
1332
|
+
"selected",
|
|
1333
|
+
"hidden",
|
|
1334
|
+
"src",
|
|
1335
|
+
"href",
|
|
1336
|
+
"id",
|
|
1337
|
+
"className",
|
|
1338
|
+
"classList",
|
|
1339
|
+
"parentElement",
|
|
1340
|
+
"parentNode",
|
|
1341
|
+
"children",
|
|
1342
|
+
"firstChild",
|
|
1343
|
+
"lastChild",
|
|
1344
|
+
"nextSibling",
|
|
1345
|
+
"previousSibling",
|
|
1346
|
+
"nextElementSibling",
|
|
1347
|
+
"previousElementSibling",
|
|
1348
|
+
"offsetWidth",
|
|
1349
|
+
"offsetHeight",
|
|
1350
|
+
"offsetTop",
|
|
1351
|
+
"offsetLeft",
|
|
1352
|
+
"clientWidth",
|
|
1353
|
+
"clientHeight",
|
|
1354
|
+
"scrollWidth",
|
|
1355
|
+
"scrollHeight",
|
|
1356
|
+
"scrollTop",
|
|
1357
|
+
"scrollLeft"
|
|
1358
|
+
];
|
|
1359
|
+
if (domProps.includes(property)) {
|
|
1360
|
+
return `${object}.${property}`;
|
|
1361
|
+
}
|
|
1362
|
+
this.ctx.requireHelper("getProp");
|
|
1363
|
+
return `_rt.getProp(${object}, '${sanitizeSelector(property)}')`;
|
|
1364
|
+
}
|
|
1365
|
+
// ===========================================================================
|
|
1366
|
+
// CALL EXPRESSIONS
|
|
1367
|
+
// ===========================================================================
|
|
1368
|
+
generateCall(node) {
|
|
1369
|
+
const args = (node.args ?? []).map((arg) => this.generate(arg)).join(", ");
|
|
1370
|
+
if (node.callee.type === "member" || node.callee.type === "possessive") {
|
|
1371
|
+
const memberNode = node.callee;
|
|
1372
|
+
const object = this.generate(memberNode.object);
|
|
1373
|
+
const method = typeof memberNode.property === "string" ? memberNode.property : this.generate(memberNode.property);
|
|
1374
|
+
if (typeof memberNode.property === "string") {
|
|
1375
|
+
return `${object}.${sanitizeIdentifier(memberNode.property)}(${args})`;
|
|
1376
|
+
}
|
|
1377
|
+
return `${object}[${method}](${args})`;
|
|
1378
|
+
}
|
|
1379
|
+
const callee = this.generate(node.callee);
|
|
1380
|
+
return `${callee}(${args})`;
|
|
1381
|
+
}
|
|
1382
|
+
// ===========================================================================
|
|
1383
|
+
// POSITIONAL EXPRESSIONS
|
|
1384
|
+
// ===========================================================================
|
|
1385
|
+
generatePositional(node) {
|
|
1386
|
+
const position = node.position;
|
|
1387
|
+
const target = node.target ? this.generate(node.target) : null;
|
|
1388
|
+
switch (position) {
|
|
1389
|
+
case "first":
|
|
1390
|
+
if (target) {
|
|
1391
|
+
if (node.target?.type === "selector") {
|
|
1392
|
+
const sel = node.target.value;
|
|
1393
|
+
return `document.querySelector('${sanitizeSelector(sel)}')`;
|
|
1394
|
+
}
|
|
1395
|
+
this.ctx.requireHelper("first");
|
|
1396
|
+
return `_rt.first(${target})`;
|
|
1397
|
+
}
|
|
1398
|
+
return "_ctx.me";
|
|
1399
|
+
case "last":
|
|
1400
|
+
if (target) {
|
|
1401
|
+
if (node.target?.type === "selector") {
|
|
1402
|
+
const sel = node.target.value;
|
|
1403
|
+
const sanitized = sanitizeSelector(sel);
|
|
1404
|
+
return `(()=>{const _els=document.querySelectorAll('${sanitized}');return _els[_els.length-1]})()`;
|
|
1405
|
+
}
|
|
1406
|
+
this.ctx.requireHelper("last");
|
|
1407
|
+
return `_rt.last(${target})`;
|
|
1408
|
+
}
|
|
1409
|
+
return "_ctx.me";
|
|
1410
|
+
case "random":
|
|
1411
|
+
if (target) {
|
|
1412
|
+
this.ctx.requireHelper("random");
|
|
1413
|
+
return `_rt.random(${target})`;
|
|
1414
|
+
}
|
|
1415
|
+
return "_ctx.me";
|
|
1416
|
+
case "next":
|
|
1417
|
+
return "_ctx.me.nextElementSibling";
|
|
1418
|
+
case "previous":
|
|
1419
|
+
return "_ctx.me.previousElementSibling";
|
|
1420
|
+
case "closest":
|
|
1421
|
+
if (target && node.target?.type === "selector") {
|
|
1422
|
+
const sel = node.target.value;
|
|
1423
|
+
return `_ctx.me.closest('${sanitizeSelector(sel)}')`;
|
|
1424
|
+
}
|
|
1425
|
+
if (target) {
|
|
1426
|
+
return `_ctx.me.closest(${target})`;
|
|
1427
|
+
}
|
|
1428
|
+
return "_ctx.me.parentElement";
|
|
1429
|
+
case "parent":
|
|
1430
|
+
return "_ctx.me.parentElement";
|
|
1431
|
+
default:
|
|
1432
|
+
throw new Error(`Unknown positional: ${position}`);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
// ===========================================================================
|
|
1436
|
+
// ARRAY EXPRESSIONS
|
|
1437
|
+
// ===========================================================================
|
|
1438
|
+
generateArray(node) {
|
|
1439
|
+
const elements = node.elements ?? [];
|
|
1440
|
+
const items = elements.map((el) => this.generate(el)).join(", ");
|
|
1441
|
+
return `[${items}]`;
|
|
1442
|
+
}
|
|
1443
|
+
// ===========================================================================
|
|
1444
|
+
// OBJECT EXPRESSIONS
|
|
1445
|
+
// ===========================================================================
|
|
1446
|
+
generateObject(node) {
|
|
1447
|
+
const properties = node.properties ?? [];
|
|
1448
|
+
const pairs = properties.map((prop) => {
|
|
1449
|
+
const key = typeof prop.key === "string" ? JSON.stringify(prop.key) : this.generate(prop.key);
|
|
1450
|
+
const value = this.generate(prop.value);
|
|
1451
|
+
return `${key}: ${value}`;
|
|
1452
|
+
}).join(", ");
|
|
1453
|
+
return `{${pairs}}`;
|
|
1454
|
+
}
|
|
1455
|
+
// ===========================================================================
|
|
1456
|
+
// TEMPLATE EXPRESSIONS
|
|
1457
|
+
// ===========================================================================
|
|
1458
|
+
generateTemplate(node) {
|
|
1459
|
+
const parts = node.parts ?? [];
|
|
1460
|
+
const segments = parts.map((part) => {
|
|
1461
|
+
if (typeof part === "string") {
|
|
1462
|
+
return part.replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
1463
|
+
}
|
|
1464
|
+
return "${" + this.generate(part) + "}";
|
|
1465
|
+
}).join("");
|
|
1466
|
+
return "`" + segments + "`";
|
|
1467
|
+
}
|
|
1468
|
+
// ===========================================================================
|
|
1469
|
+
// UNARY EXPRESSIONS
|
|
1470
|
+
// ===========================================================================
|
|
1471
|
+
generateUnary(node) {
|
|
1472
|
+
const operand = this.generate(node.operand);
|
|
1473
|
+
switch (node.operator) {
|
|
1474
|
+
case "not":
|
|
1475
|
+
case "!":
|
|
1476
|
+
return `!${operand}`;
|
|
1477
|
+
case "-":
|
|
1478
|
+
return `-${operand}`;
|
|
1479
|
+
case "+":
|
|
1480
|
+
return `+${operand}`;
|
|
1481
|
+
case "no":
|
|
1482
|
+
return `!${operand}`;
|
|
1483
|
+
default:
|
|
1484
|
+
return `${node.operator}${operand}`;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
// ===========================================================================
|
|
1488
|
+
// CONDITIONAL EXPRESSIONS
|
|
1489
|
+
// ===========================================================================
|
|
1490
|
+
generateConditional(node) {
|
|
1491
|
+
const condition = this.generate(node.condition);
|
|
1492
|
+
const consequent = this.generate(node.consequent);
|
|
1493
|
+
const alternate = this.generate(node.alternate);
|
|
1494
|
+
return `(${condition} ? ${consequent} : ${alternate})`;
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
function generateExpression(node, ctx) {
|
|
1498
|
+
const codegen = new ExpressionCodegen(ctx);
|
|
1499
|
+
return codegen.generate(node);
|
|
1500
|
+
}
|
|
1501
|
+
var ToggleCodegen = class {
|
|
1502
|
+
constructor() {
|
|
1503
|
+
this.command = "toggle";
|
|
1504
|
+
}
|
|
1505
|
+
generate(node, ctx) {
|
|
1506
|
+
const args = node.args ?? [];
|
|
1507
|
+
if (args.length === 0) return null;
|
|
1508
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1509
|
+
const arg = args[0];
|
|
1510
|
+
if (arg.type === "selector") {
|
|
1511
|
+
const selector = arg.value;
|
|
1512
|
+
if (selector.startsWith(".")) {
|
|
1513
|
+
const className = sanitizeClassName(selector.slice(1));
|
|
1514
|
+
if (!className) return null;
|
|
1515
|
+
if (target === "_ctx.me") {
|
|
1516
|
+
return {
|
|
1517
|
+
code: `_ctx.me.classList.toggle('${className}')`,
|
|
1518
|
+
async: false,
|
|
1519
|
+
sideEffects: true
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
return {
|
|
1523
|
+
code: `Array.from(document.querySelectorAll('${sanitizeSelector(selector.slice(1))}')).forEach(el => el.classList.toggle('${className}'))`,
|
|
1524
|
+
async: false,
|
|
1525
|
+
sideEffects: true
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
if (arg.type === "identifier") {
|
|
1530
|
+
const value = arg.value ?? "";
|
|
1531
|
+
if (value.startsWith("@")) {
|
|
1532
|
+
const attrName = value.slice(1);
|
|
1533
|
+
ctx.requireHelper("toggleAttr");
|
|
1534
|
+
return {
|
|
1535
|
+
code: `_rt.toggleAttr(${target}, '${sanitizeSelector(attrName)}')`,
|
|
1536
|
+
async: false,
|
|
1537
|
+
sideEffects: true
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
ctx.requireHelper("toggle");
|
|
1542
|
+
return {
|
|
1543
|
+
code: `_rt.toggle(${ctx.generateExpression(arg)}, ${target})`,
|
|
1544
|
+
async: false,
|
|
1545
|
+
sideEffects: true
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
var AddCodegen = class {
|
|
1550
|
+
constructor() {
|
|
1551
|
+
this.command = "add";
|
|
1552
|
+
}
|
|
1553
|
+
generate(node, ctx) {
|
|
1554
|
+
const args = node.args ?? [];
|
|
1555
|
+
if (args.length === 0) return null;
|
|
1556
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1557
|
+
const arg = args[0];
|
|
1558
|
+
if (arg.type === "selector") {
|
|
1559
|
+
const selector = arg.value;
|
|
1560
|
+
if (selector.startsWith(".")) {
|
|
1561
|
+
const className = sanitizeClassName(selector.slice(1));
|
|
1562
|
+
if (!className) return null;
|
|
1563
|
+
if (target === "_ctx.me") {
|
|
1564
|
+
return {
|
|
1565
|
+
code: `_ctx.me.classList.add('${className}')`,
|
|
1566
|
+
async: false,
|
|
1567
|
+
sideEffects: true
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
code: `${target}.classList.add('${className}')`,
|
|
1572
|
+
async: false,
|
|
1573
|
+
sideEffects: true
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
if (arg.type === "htmlLiteral") {
|
|
1578
|
+
const tagNode = arg;
|
|
1579
|
+
const tag = tagNode.tag ?? "div";
|
|
1580
|
+
const classes = tagNode.classes ?? [];
|
|
1581
|
+
const id = tagNode.id;
|
|
1582
|
+
let code = `(() => { const _el = document.createElement('${tag}');`;
|
|
1583
|
+
if (classes.length > 0) {
|
|
1584
|
+
code += ` _el.className = '${classes.join(" ")}';`;
|
|
1585
|
+
}
|
|
1586
|
+
if (id) {
|
|
1587
|
+
code += ` _el.id = '${id}';`;
|
|
1588
|
+
}
|
|
1589
|
+
code += ` ${target}.appendChild(_el); return _el; })()`;
|
|
1590
|
+
return { code, async: false, sideEffects: true };
|
|
1591
|
+
}
|
|
1592
|
+
ctx.requireHelper("addClass");
|
|
1593
|
+
return {
|
|
1594
|
+
code: `_rt.addClass(${target}, ${ctx.generateExpression(arg)})`,
|
|
1595
|
+
async: false,
|
|
1596
|
+
sideEffects: true
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
var RemoveCodegen = class {
|
|
1601
|
+
constructor() {
|
|
1602
|
+
this.command = "remove";
|
|
1603
|
+
}
|
|
1604
|
+
generate(node, ctx) {
|
|
1605
|
+
const args = node.args ?? [];
|
|
1606
|
+
if (args.length === 0) {
|
|
1607
|
+
const target2 = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1608
|
+
return {
|
|
1609
|
+
code: `${target2}.remove()`,
|
|
1610
|
+
async: false,
|
|
1611
|
+
sideEffects: true
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1615
|
+
const arg = args[0];
|
|
1616
|
+
if (arg.type === "selector") {
|
|
1617
|
+
const selector = arg.value;
|
|
1618
|
+
if (selector.startsWith(".")) {
|
|
1619
|
+
const className = sanitizeClassName(selector.slice(1));
|
|
1620
|
+
if (!className) return null;
|
|
1621
|
+
return {
|
|
1622
|
+
code: `${target}.classList.remove('${className}')`,
|
|
1623
|
+
async: false,
|
|
1624
|
+
sideEffects: true
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
ctx.requireHelper("removeClass");
|
|
1629
|
+
return {
|
|
1630
|
+
code: `_rt.removeClass(${target}, ${ctx.generateExpression(arg)})`,
|
|
1631
|
+
async: false,
|
|
1632
|
+
sideEffects: true
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
var SetCodegen = class {
|
|
1637
|
+
constructor() {
|
|
1638
|
+
this.command = "set";
|
|
1639
|
+
}
|
|
1640
|
+
generate(node, ctx) {
|
|
1641
|
+
const args = node.args ?? [];
|
|
1642
|
+
const modifiers = node.modifiers;
|
|
1643
|
+
const roles = node.roles;
|
|
1644
|
+
const targetNode = roles?.destination ?? args[0];
|
|
1645
|
+
const valueNode = roles?.patient ?? modifiers?.to ?? args[1];
|
|
1646
|
+
if (!targetNode || !valueNode) return null;
|
|
1647
|
+
const value = ctx.generateExpression(valueNode);
|
|
1648
|
+
if (targetNode.type === "variable") {
|
|
1649
|
+
const varNode = targetNode;
|
|
1650
|
+
const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
|
|
1651
|
+
const safeName = sanitizeIdentifier(name);
|
|
1652
|
+
if (varNode.scope === "local") {
|
|
1653
|
+
return {
|
|
1654
|
+
code: `_ctx.locals.set('${safeName}', ${value})`,
|
|
1655
|
+
async: false,
|
|
1656
|
+
sideEffects: true
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
if (varNode.scope === "global") {
|
|
1660
|
+
ctx.requireHelper("globals");
|
|
1661
|
+
return {
|
|
1662
|
+
code: `_rt.globals.set('${safeName}', ${value})`,
|
|
1663
|
+
async: false,
|
|
1664
|
+
sideEffects: true
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
if (targetNode.type === "possessive") {
|
|
1669
|
+
const possessive = targetNode;
|
|
1670
|
+
const obj = ctx.generateExpression(possessive.object);
|
|
1671
|
+
const prop = possessive.property;
|
|
1672
|
+
if (prop.startsWith("*")) {
|
|
1673
|
+
const styleProp = prop.slice(1);
|
|
1674
|
+
return {
|
|
1675
|
+
code: `${obj}.style.${styleProp} = ${value}`,
|
|
1676
|
+
async: false,
|
|
1677
|
+
sideEffects: true
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
if (prop.startsWith("@")) {
|
|
1681
|
+
const attrName = prop.slice(1);
|
|
1682
|
+
return {
|
|
1683
|
+
code: `${obj}.setAttribute('${sanitizeSelector(attrName)}', ${value})`,
|
|
1684
|
+
async: false,
|
|
1685
|
+
sideEffects: true
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
return {
|
|
1689
|
+
code: `${obj}.${sanitizeIdentifier(prop)} = ${value}`,
|
|
1690
|
+
async: false,
|
|
1691
|
+
sideEffects: true
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
if (targetNode.type === "member") {
|
|
1695
|
+
const memberCode = ctx.generateExpression(targetNode);
|
|
1696
|
+
return {
|
|
1697
|
+
code: `${memberCode} = ${value}`,
|
|
1698
|
+
async: false,
|
|
1699
|
+
sideEffects: true
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
return null;
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
var PutCodegen = class {
|
|
1706
|
+
constructor() {
|
|
1707
|
+
this.command = "put";
|
|
1708
|
+
}
|
|
1709
|
+
generate(node, ctx) {
|
|
1710
|
+
const args = node.args ?? [];
|
|
1711
|
+
const roles = node.roles;
|
|
1712
|
+
const modifiers = node.modifiers;
|
|
1713
|
+
const contentNode = roles?.patient ?? args[0];
|
|
1714
|
+
if (!contentNode) return null;
|
|
1715
|
+
const content = ctx.generateExpression(contentNode);
|
|
1716
|
+
let target = "_ctx.me";
|
|
1717
|
+
if (roles?.destination) {
|
|
1718
|
+
target = ctx.generateExpression(roles.destination);
|
|
1719
|
+
} else if (modifiers) {
|
|
1720
|
+
for (const key of ["into", "before", "after"]) {
|
|
1721
|
+
if (modifiers[key] && typeof modifiers[key] === "object") {
|
|
1722
|
+
target = ctx.generateExpression(modifiers[key]);
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
if (target === "_ctx.me" && node.target) {
|
|
1728
|
+
target = ctx.generateExpression(node.target);
|
|
1729
|
+
}
|
|
1730
|
+
let modifier = "into";
|
|
1731
|
+
if (roles?.method && roles.method.value) {
|
|
1732
|
+
modifier = String(roles.method.value);
|
|
1733
|
+
} else if (modifiers) {
|
|
1734
|
+
if (modifiers.before) modifier = "before";
|
|
1735
|
+
else if (modifiers.after) modifier = "after";
|
|
1736
|
+
else if (typeof modifiers.position === "string") modifier = modifiers.position;
|
|
1737
|
+
}
|
|
1738
|
+
switch (modifier) {
|
|
1739
|
+
case "into":
|
|
1740
|
+
return {
|
|
1741
|
+
code: `${target}.innerHTML = ${content}`,
|
|
1742
|
+
async: false,
|
|
1743
|
+
sideEffects: true
|
|
1744
|
+
};
|
|
1745
|
+
case "before":
|
|
1746
|
+
return {
|
|
1747
|
+
code: `${target}.insertAdjacentHTML('beforebegin', ${content})`,
|
|
1748
|
+
async: false,
|
|
1749
|
+
sideEffects: true
|
|
1750
|
+
};
|
|
1751
|
+
case "after":
|
|
1752
|
+
return {
|
|
1753
|
+
code: `${target}.insertAdjacentHTML('afterend', ${content})`,
|
|
1754
|
+
async: false,
|
|
1755
|
+
sideEffects: true
|
|
1756
|
+
};
|
|
1757
|
+
case "at start of":
|
|
1758
|
+
case "start":
|
|
1759
|
+
return {
|
|
1760
|
+
code: `${target}.insertAdjacentHTML('afterbegin', ${content})`,
|
|
1761
|
+
async: false,
|
|
1762
|
+
sideEffects: true
|
|
1763
|
+
};
|
|
1764
|
+
case "at end of":
|
|
1765
|
+
case "end":
|
|
1766
|
+
return {
|
|
1767
|
+
code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
|
|
1768
|
+
async: false,
|
|
1769
|
+
sideEffects: true
|
|
1770
|
+
};
|
|
1771
|
+
default:
|
|
1772
|
+
return {
|
|
1773
|
+
code: `${target}.innerHTML = ${content}`,
|
|
1774
|
+
async: false,
|
|
1775
|
+
sideEffects: true
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
var ShowCodegen = class {
|
|
1781
|
+
constructor() {
|
|
1782
|
+
this.command = "show";
|
|
1783
|
+
}
|
|
1784
|
+
generate(node, ctx) {
|
|
1785
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1786
|
+
return {
|
|
1787
|
+
code: `${target}.style.display = ''`,
|
|
1788
|
+
async: false,
|
|
1789
|
+
sideEffects: true
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1793
|
+
var HideCodegen = class {
|
|
1794
|
+
constructor() {
|
|
1795
|
+
this.command = "hide";
|
|
1796
|
+
}
|
|
1797
|
+
generate(node, ctx) {
|
|
1798
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1799
|
+
return {
|
|
1800
|
+
code: `${target}.style.display = 'none'`,
|
|
1801
|
+
async: false,
|
|
1802
|
+
sideEffects: true
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
var FocusCodegen = class {
|
|
1807
|
+
constructor() {
|
|
1808
|
+
this.command = "focus";
|
|
1809
|
+
}
|
|
1810
|
+
generate(node, ctx) {
|
|
1811
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1812
|
+
return {
|
|
1813
|
+
code: `${target}.focus()`,
|
|
1814
|
+
async: false,
|
|
1815
|
+
sideEffects: true
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
var BlurCodegen = class {
|
|
1820
|
+
constructor() {
|
|
1821
|
+
this.command = "blur";
|
|
1822
|
+
}
|
|
1823
|
+
generate(node, ctx) {
|
|
1824
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1825
|
+
return {
|
|
1826
|
+
code: `${target}.blur()`,
|
|
1827
|
+
async: false,
|
|
1828
|
+
sideEffects: true
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
var LogCodegen = class {
|
|
1833
|
+
constructor() {
|
|
1834
|
+
this.command = "log";
|
|
1835
|
+
}
|
|
1836
|
+
generate(node, ctx) {
|
|
1837
|
+
const args = node.args ?? [];
|
|
1838
|
+
const values = args.map((arg) => ctx.generateExpression(arg)).join(", ");
|
|
1839
|
+
return {
|
|
1840
|
+
code: `console.log(${values || "''"})`,
|
|
1841
|
+
async: false,
|
|
1842
|
+
sideEffects: true
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
var WaitCodegen = class {
|
|
1847
|
+
constructor() {
|
|
1848
|
+
this.command = "wait";
|
|
1849
|
+
}
|
|
1850
|
+
generate(node, ctx) {
|
|
1851
|
+
const args = node.args ?? [];
|
|
1852
|
+
const roles = node.roles;
|
|
1853
|
+
const arg = roles?.duration ?? args[0];
|
|
1854
|
+
if (!arg) return null;
|
|
1855
|
+
if (arg.type === "literal") {
|
|
1856
|
+
const value = arg.value;
|
|
1857
|
+
if (typeof value === "number") {
|
|
1858
|
+
ctx.requireHelper("wait");
|
|
1859
|
+
return {
|
|
1860
|
+
code: `await _rt.wait(${value})`,
|
|
1861
|
+
async: true,
|
|
1862
|
+
sideEffects: false
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
if (typeof value === "string") {
|
|
1866
|
+
const ms = parseDuration(value);
|
|
1867
|
+
if (ms !== null) {
|
|
1868
|
+
ctx.requireHelper("wait");
|
|
1869
|
+
return {
|
|
1870
|
+
code: `await _rt.wait(${ms})`,
|
|
1871
|
+
async: true,
|
|
1872
|
+
sideEffects: false
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
const duration = ctx.generateExpression(arg);
|
|
1878
|
+
ctx.requireHelper("wait");
|
|
1879
|
+
return {
|
|
1880
|
+
code: `await _rt.wait(${duration})`,
|
|
1881
|
+
async: true,
|
|
1882
|
+
sideEffects: false
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
};
|
|
1886
|
+
var FetchCodegen = class {
|
|
1887
|
+
constructor() {
|
|
1888
|
+
this.command = "fetch";
|
|
1889
|
+
}
|
|
1890
|
+
generate(node, ctx) {
|
|
1891
|
+
const args = node.args ?? [];
|
|
1892
|
+
const roles = node.roles;
|
|
1893
|
+
const modifiers = node.modifiers;
|
|
1894
|
+
const urlNode = roles?.source ?? args[0];
|
|
1895
|
+
if (!urlNode) return null;
|
|
1896
|
+
const url = ctx.generateExpression(urlNode);
|
|
1897
|
+
let format = "text";
|
|
1898
|
+
if (roles?.responseType) {
|
|
1899
|
+
const rt = roles.responseType;
|
|
1900
|
+
format = String(rt.name ?? rt.value ?? "text");
|
|
1901
|
+
} else if (modifiers?.as) {
|
|
1902
|
+
const asVal = modifiers.as;
|
|
1903
|
+
if (typeof asVal === "string") {
|
|
1904
|
+
format = asVal;
|
|
1905
|
+
} else if (typeof asVal === "object" && asVal !== null) {
|
|
1906
|
+
format = String(
|
|
1907
|
+
asVal.name ?? asVal.value ?? "text"
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
switch (format) {
|
|
1912
|
+
case "json":
|
|
1913
|
+
ctx.requireHelper("fetchJSON");
|
|
1914
|
+
return {
|
|
1915
|
+
code: `_ctx.it = await _rt.fetchJSON(${url})`,
|
|
1916
|
+
async: true,
|
|
1917
|
+
sideEffects: true
|
|
1918
|
+
};
|
|
1919
|
+
case "html":
|
|
1920
|
+
ctx.requireHelper("fetchHTML");
|
|
1921
|
+
return {
|
|
1922
|
+
code: `_ctx.it = await _rt.fetchHTML(${url})`,
|
|
1923
|
+
async: true,
|
|
1924
|
+
sideEffects: true
|
|
1925
|
+
};
|
|
1926
|
+
case "text":
|
|
1927
|
+
default:
|
|
1928
|
+
ctx.requireHelper("fetchText");
|
|
1929
|
+
return {
|
|
1930
|
+
code: `_ctx.it = await _rt.fetchText(${url})`,
|
|
1931
|
+
async: true,
|
|
1932
|
+
sideEffects: true
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
var SendCodegen = class {
|
|
1938
|
+
constructor() {
|
|
1939
|
+
this.command = "send";
|
|
1940
|
+
}
|
|
1941
|
+
generate(node, ctx) {
|
|
1942
|
+
const args = node.args ?? [];
|
|
1943
|
+
if (args.length === 0) return null;
|
|
1944
|
+
const eventName = ctx.generateExpression(args[0]);
|
|
1945
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
1946
|
+
const detail = args.length > 1 ? ctx.generateExpression(args[1]) : "undefined";
|
|
1947
|
+
ctx.requireHelper("send");
|
|
1948
|
+
return {
|
|
1949
|
+
code: `_rt.send(${target}, ${eventName}, ${detail})`,
|
|
1950
|
+
async: false,
|
|
1951
|
+
sideEffects: true
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
};
|
|
1955
|
+
var IncrementCodegen = class {
|
|
1956
|
+
constructor() {
|
|
1957
|
+
this.command = "increment";
|
|
1958
|
+
}
|
|
1959
|
+
generate(node, ctx) {
|
|
1960
|
+
const args = node.args ?? [];
|
|
1961
|
+
const roles = node.roles;
|
|
1962
|
+
const modifiers = node.modifiers;
|
|
1963
|
+
const target = roles?.destination ?? roles?.patient ?? args[0];
|
|
1964
|
+
if (!target) return null;
|
|
1965
|
+
const amountNode = roles?.quantity ?? modifiers?.by ?? args[1];
|
|
1966
|
+
const amount = amountNode ? ctx.generateExpression(amountNode) : "1";
|
|
1967
|
+
if (target.type === "variable") {
|
|
1968
|
+
const varNode = target;
|
|
1969
|
+
const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
|
|
1970
|
+
const safeName = sanitizeIdentifier(name);
|
|
1971
|
+
if (varNode.scope === "local") {
|
|
1972
|
+
return {
|
|
1973
|
+
code: `_ctx.locals.set('${safeName}', (_ctx.locals.get('${safeName}') || 0) + ${amount})`,
|
|
1974
|
+
async: false,
|
|
1975
|
+
sideEffects: true
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
if (varNode.scope === "global") {
|
|
1979
|
+
ctx.requireHelper("globals");
|
|
1980
|
+
return {
|
|
1981
|
+
code: `_rt.globals.set('${safeName}', (_rt.globals.get('${safeName}') || 0) + ${amount})`,
|
|
1982
|
+
async: false,
|
|
1983
|
+
sideEffects: true
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
const targetCode = ctx.generateExpression(target);
|
|
1988
|
+
return {
|
|
1989
|
+
code: `${targetCode}.textContent = (parseFloat(${targetCode}.textContent) || 0) + ${amount}`,
|
|
1990
|
+
async: false,
|
|
1991
|
+
sideEffects: true
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
var DecrementCodegen = class {
|
|
1996
|
+
constructor() {
|
|
1997
|
+
this.command = "decrement";
|
|
1998
|
+
}
|
|
1999
|
+
generate(node, ctx) {
|
|
2000
|
+
const args = node.args ?? [];
|
|
2001
|
+
const roles = node.roles;
|
|
2002
|
+
const modifiers = node.modifiers;
|
|
2003
|
+
const target = roles?.destination ?? roles?.patient ?? args[0];
|
|
2004
|
+
if (!target) return null;
|
|
2005
|
+
const amountNode = roles?.quantity ?? modifiers?.by ?? args[1];
|
|
2006
|
+
const amount = amountNode ? ctx.generateExpression(amountNode) : "1";
|
|
2007
|
+
if (target.type === "variable") {
|
|
2008
|
+
const varNode = target;
|
|
2009
|
+
const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
|
|
2010
|
+
const safeName = sanitizeIdentifier(name);
|
|
2011
|
+
if (varNode.scope === "local") {
|
|
2012
|
+
return {
|
|
2013
|
+
code: `_ctx.locals.set('${safeName}', (_ctx.locals.get('${safeName}') || 0) - ${amount})`,
|
|
2014
|
+
async: false,
|
|
2015
|
+
sideEffects: true
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
if (varNode.scope === "global") {
|
|
2019
|
+
ctx.requireHelper("globals");
|
|
2020
|
+
return {
|
|
2021
|
+
code: `_rt.globals.set('${safeName}', (_rt.globals.get('${safeName}') || 0) - ${amount})`,
|
|
2022
|
+
async: false,
|
|
2023
|
+
sideEffects: true
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
const targetCode = ctx.generateExpression(target);
|
|
2028
|
+
return {
|
|
2029
|
+
code: `${targetCode}.textContent = (parseFloat(${targetCode}.textContent) || 0) - ${amount}`,
|
|
2030
|
+
async: false,
|
|
2031
|
+
sideEffects: true
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
var HaltCodegen = class {
|
|
2036
|
+
constructor() {
|
|
2037
|
+
this.command = "halt";
|
|
2038
|
+
}
|
|
2039
|
+
generate(_node, ctx) {
|
|
2040
|
+
ctx.requireHelper("HALT");
|
|
2041
|
+
return {
|
|
2042
|
+
code: `throw _rt.HALT`,
|
|
2043
|
+
async: false,
|
|
2044
|
+
sideEffects: true
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
var ExitCodegen = class {
|
|
2049
|
+
constructor() {
|
|
2050
|
+
this.command = "exit";
|
|
2051
|
+
}
|
|
2052
|
+
generate(_node, ctx) {
|
|
2053
|
+
ctx.requireHelper("EXIT");
|
|
2054
|
+
return {
|
|
2055
|
+
code: `throw _rt.EXIT`,
|
|
2056
|
+
async: false,
|
|
2057
|
+
sideEffects: true
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
var ReturnCodegen = class {
|
|
2062
|
+
constructor() {
|
|
2063
|
+
this.command = "return";
|
|
2064
|
+
}
|
|
2065
|
+
generate(node, ctx) {
|
|
2066
|
+
const args = node.args ?? [];
|
|
2067
|
+
const value = args.length > 0 ? ctx.generateExpression(args[0]) : "undefined";
|
|
2068
|
+
return {
|
|
2069
|
+
code: `return ${value}`,
|
|
2070
|
+
async: false,
|
|
2071
|
+
sideEffects: true
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
};
|
|
2075
|
+
var CallCodegen = class {
|
|
2076
|
+
constructor() {
|
|
2077
|
+
this.command = "call";
|
|
2078
|
+
}
|
|
2079
|
+
generate(node, ctx) {
|
|
2080
|
+
const args = node.args ?? [];
|
|
2081
|
+
if (args.length === 0) return null;
|
|
2082
|
+
const fn = ctx.generateExpression(args[0]);
|
|
2083
|
+
return {
|
|
2084
|
+
code: fn,
|
|
2085
|
+
async: false,
|
|
2086
|
+
sideEffects: true
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
};
|
|
2090
|
+
var ScrollCodegen = class {
|
|
2091
|
+
constructor() {
|
|
2092
|
+
this.command = "scroll";
|
|
2093
|
+
}
|
|
2094
|
+
generate(node, ctx) {
|
|
2095
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2096
|
+
const behavior = node.modifiers?.smooth ? "'smooth'" : "'auto'";
|
|
2097
|
+
return {
|
|
2098
|
+
code: `${target}.scrollIntoView({ behavior: ${behavior} })`,
|
|
2099
|
+
async: false,
|
|
2100
|
+
sideEffects: true
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
var TakeCodegen = class {
|
|
2105
|
+
constructor() {
|
|
2106
|
+
this.command = "take";
|
|
2107
|
+
}
|
|
2108
|
+
generate(node, _ctx) {
|
|
2109
|
+
const args = node.args ?? [];
|
|
2110
|
+
if (args.length === 0) return null;
|
|
2111
|
+
const arg = args[0];
|
|
2112
|
+
if (arg.type !== "selector") return null;
|
|
2113
|
+
const selector = arg.value;
|
|
2114
|
+
if (!selector.startsWith(".")) return null;
|
|
2115
|
+
const className = sanitizeClassName(selector.slice(1));
|
|
2116
|
+
if (!className) return null;
|
|
2117
|
+
return {
|
|
2118
|
+
code: `(() => { const _me = _ctx.me; _me.parentElement?.querySelectorAll('.${className}').forEach(el => el.classList.remove('${className}')); _me.classList.add('${className}'); })()`,
|
|
2119
|
+
async: false,
|
|
2120
|
+
sideEffects: true
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
function generateIf(node, ctx, generateBody) {
|
|
2125
|
+
const exprCodegen = new ExpressionCodegen(ctx);
|
|
2126
|
+
const condition = exprCodegen.generate(node.condition);
|
|
2127
|
+
const thenBody = generateBody(node.thenBranch);
|
|
2128
|
+
let code = `if (${condition}) {
|
|
2129
|
+
${thenBody}
|
|
2130
|
+
}`;
|
|
2131
|
+
if (node.elseIfBranches) {
|
|
2132
|
+
for (const branch of node.elseIfBranches) {
|
|
2133
|
+
const branchCondition = exprCodegen.generate(branch.condition);
|
|
2134
|
+
const branchBody = generateBody(branch.body);
|
|
2135
|
+
code += ` else if (${branchCondition}) {
|
|
2136
|
+
${branchBody}
|
|
2137
|
+
}`;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
if (node.elseBranch) {
|
|
2141
|
+
const elseBody = generateBody(node.elseBranch);
|
|
2142
|
+
code += ` else {
|
|
2143
|
+
${elseBody}
|
|
2144
|
+
}`;
|
|
2145
|
+
}
|
|
2146
|
+
return code;
|
|
2147
|
+
}
|
|
2148
|
+
function generateRepeat(node, ctx, generateBody) {
|
|
2149
|
+
const exprCodegen = new ExpressionCodegen(ctx);
|
|
2150
|
+
const body = generateBody(node.body);
|
|
2151
|
+
if (node.count !== void 0) {
|
|
2152
|
+
const count = typeof node.count === "number" ? String(node.count) : exprCodegen.generate(node.count);
|
|
2153
|
+
return `for (let _i = 0; _i < ${count}; _i++) {
|
|
2154
|
+
_ctx.locals.set('index', _i);
|
|
2155
|
+
${body}
|
|
2156
|
+
}`;
|
|
2157
|
+
}
|
|
2158
|
+
if (node.whileCondition) {
|
|
2159
|
+
const condition = exprCodegen.generate(node.whileCondition);
|
|
2160
|
+
return `while (${condition}) {
|
|
2161
|
+
${body}
|
|
2162
|
+
}`;
|
|
2163
|
+
}
|
|
2164
|
+
return `while (true) {
|
|
2165
|
+
${body}
|
|
2166
|
+
}`;
|
|
2167
|
+
}
|
|
2168
|
+
function generateForEach(node, ctx, generateBody) {
|
|
2169
|
+
const exprCodegen = new ExpressionCodegen(ctx);
|
|
2170
|
+
const collection = exprCodegen.generate(node.collection);
|
|
2171
|
+
const itemName = sanitizeIdentifier(node.itemName);
|
|
2172
|
+
const indexName = node.indexName ? sanitizeIdentifier(node.indexName) : "index";
|
|
2173
|
+
const body = generateBody(node.body);
|
|
2174
|
+
return `{
|
|
2175
|
+
const _collection = ${collection};
|
|
2176
|
+
const _arr = Array.isArray(_collection) ? _collection : Array.from(_collection);
|
|
2177
|
+
for (let _i = 0; _i < _arr.length; _i++) {
|
|
2178
|
+
_ctx.locals.set('${itemName}', _arr[_i]);
|
|
2179
|
+
_ctx.locals.set('${indexName}', _i);
|
|
2180
|
+
${body}
|
|
2181
|
+
}
|
|
2182
|
+
}`;
|
|
2183
|
+
}
|
|
2184
|
+
function generateWhile(node, ctx, generateBody) {
|
|
2185
|
+
const exprCodegen = new ExpressionCodegen(ctx);
|
|
2186
|
+
const condition = exprCodegen.generate(node.condition);
|
|
2187
|
+
const body = generateBody(node.body);
|
|
2188
|
+
return `while (${condition}) {
|
|
2189
|
+
${body}
|
|
2190
|
+
}`;
|
|
2191
|
+
}
|
|
2192
|
+
var UnlessCodegen = class {
|
|
2193
|
+
constructor() {
|
|
2194
|
+
this.command = "unless";
|
|
2195
|
+
}
|
|
2196
|
+
generate(node, ctx) {
|
|
2197
|
+
const args = node.args ?? [];
|
|
2198
|
+
if (args.length === 0) return null;
|
|
2199
|
+
const condition = ctx.generateExpression(args[0]);
|
|
2200
|
+
return {
|
|
2201
|
+
code: `if (!(${condition}))`,
|
|
2202
|
+
async: false,
|
|
2203
|
+
sideEffects: false
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
var ThrowCodegen = class {
|
|
2208
|
+
constructor() {
|
|
2209
|
+
this.command = "throw";
|
|
2210
|
+
}
|
|
2211
|
+
generate(node, ctx) {
|
|
2212
|
+
const args = node.args ?? [];
|
|
2213
|
+
const value = args.length > 0 ? ctx.generateExpression(args[0]) : "'Error'";
|
|
2214
|
+
return {
|
|
2215
|
+
code: `throw new Error(${value})`,
|
|
2216
|
+
async: false,
|
|
2217
|
+
sideEffects: true
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
var DefaultCodegen = class {
|
|
2222
|
+
constructor() {
|
|
2223
|
+
this.command = "default";
|
|
2224
|
+
}
|
|
2225
|
+
generate(node, ctx) {
|
|
2226
|
+
const args = node.args ?? [];
|
|
2227
|
+
const roles = node.roles;
|
|
2228
|
+
const modifiers = node.modifiers;
|
|
2229
|
+
const targetNode = roles?.destination ?? args[0];
|
|
2230
|
+
const valueNode = roles?.patient ?? modifiers?.to ?? args[1];
|
|
2231
|
+
if (!targetNode || !valueNode) return null;
|
|
2232
|
+
const value = ctx.generateExpression(valueNode);
|
|
2233
|
+
if (targetNode.type === "variable") {
|
|
2234
|
+
const varNode = targetNode;
|
|
2235
|
+
const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
|
|
2236
|
+
const safeName = sanitizeIdentifier(name);
|
|
2237
|
+
if (varNode.scope === "local") {
|
|
2238
|
+
return {
|
|
2239
|
+
code: `if (_ctx.locals.get('${safeName}') == null) _ctx.locals.set('${safeName}', ${value})`,
|
|
2240
|
+
async: false,
|
|
2241
|
+
sideEffects: true
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
if (varNode.scope === "global") {
|
|
2245
|
+
ctx.requireHelper("globals");
|
|
2246
|
+
return {
|
|
2247
|
+
code: `if (_rt.globals.get('${safeName}') == null) _rt.globals.set('${safeName}', ${value})`,
|
|
2248
|
+
async: false,
|
|
2249
|
+
sideEffects: true
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
var GoCodegen = class {
|
|
2257
|
+
constructor() {
|
|
2258
|
+
this.command = "go";
|
|
2259
|
+
}
|
|
2260
|
+
generate(node, ctx) {
|
|
2261
|
+
const args = node.args ?? [];
|
|
2262
|
+
const roles = node.roles;
|
|
2263
|
+
const target = roles?.destination ?? args[0];
|
|
2264
|
+
if (!target) return null;
|
|
2265
|
+
if (target.type === "identifier") {
|
|
2266
|
+
const name = target.value;
|
|
2267
|
+
if (name === "back") {
|
|
2268
|
+
return { code: "history.back()", async: false, sideEffects: true };
|
|
2269
|
+
}
|
|
2270
|
+
if (name === "forward") {
|
|
2271
|
+
return { code: "history.forward()", async: false, sideEffects: true };
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
const url = ctx.generateExpression(target);
|
|
2275
|
+
return {
|
|
2276
|
+
code: `window.location.href = ${url}`,
|
|
2277
|
+
async: false,
|
|
2278
|
+
sideEffects: true
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
var AppendCodegen = class {
|
|
2283
|
+
constructor() {
|
|
2284
|
+
this.command = "append";
|
|
2285
|
+
}
|
|
2286
|
+
generate(node, ctx) {
|
|
2287
|
+
const args = node.args ?? [];
|
|
2288
|
+
const roles = node.roles;
|
|
2289
|
+
const contentNode = roles?.patient ?? args[0];
|
|
2290
|
+
if (!contentNode) return null;
|
|
2291
|
+
const content = ctx.generateExpression(contentNode);
|
|
2292
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2293
|
+
return {
|
|
2294
|
+
code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
|
|
2295
|
+
async: false,
|
|
2296
|
+
sideEffects: true
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
var PickCodegen = class {
|
|
2301
|
+
constructor() {
|
|
2302
|
+
this.command = "pick";
|
|
2303
|
+
}
|
|
2304
|
+
generate(node, ctx) {
|
|
2305
|
+
const args = node.args ?? [];
|
|
2306
|
+
const roles = node.roles;
|
|
2307
|
+
const collection = roles?.source ?? args[0];
|
|
2308
|
+
if (!collection) return null;
|
|
2309
|
+
const arr = ctx.generateExpression(collection);
|
|
2310
|
+
return {
|
|
2311
|
+
code: `_ctx.it = (() => { const _a = ${arr}; return _a[Math.floor(Math.random() * _a.length)]; })()`,
|
|
2312
|
+
async: false,
|
|
2313
|
+
sideEffects: true
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
var PushUrlCodegen = class {
|
|
2318
|
+
constructor() {
|
|
2319
|
+
this.command = "push-url";
|
|
2320
|
+
}
|
|
2321
|
+
generate(node, ctx) {
|
|
2322
|
+
const args = node.args ?? [];
|
|
2323
|
+
const roles = node.roles;
|
|
2324
|
+
const urlNode = roles?.destination ?? args[0];
|
|
2325
|
+
if (!urlNode) return null;
|
|
2326
|
+
const url = ctx.generateExpression(urlNode);
|
|
2327
|
+
return {
|
|
2328
|
+
code: `history.pushState({}, '', ${url})`,
|
|
2329
|
+
async: false,
|
|
2330
|
+
sideEffects: true
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
};
|
|
2334
|
+
var ReplaceUrlCodegen = class {
|
|
2335
|
+
constructor() {
|
|
2336
|
+
this.command = "replace-url";
|
|
2337
|
+
}
|
|
2338
|
+
generate(node, ctx) {
|
|
2339
|
+
const args = node.args ?? [];
|
|
2340
|
+
const roles = node.roles;
|
|
2341
|
+
const urlNode = roles?.destination ?? args[0];
|
|
2342
|
+
if (!urlNode) return null;
|
|
2343
|
+
const url = ctx.generateExpression(urlNode);
|
|
2344
|
+
return {
|
|
2345
|
+
code: `history.replaceState({}, '', ${url})`,
|
|
2346
|
+
async: false,
|
|
2347
|
+
sideEffects: true
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
var GetCodegen = class {
|
|
2352
|
+
constructor() {
|
|
2353
|
+
this.command = "get";
|
|
2354
|
+
}
|
|
2355
|
+
generate(node, ctx) {
|
|
2356
|
+
const args = node.args ?? [];
|
|
2357
|
+
const roles = node.roles;
|
|
2358
|
+
const expr = roles?.patient ?? args[0];
|
|
2359
|
+
if (!expr) return null;
|
|
2360
|
+
const value = ctx.generateExpression(expr);
|
|
2361
|
+
return {
|
|
2362
|
+
code: `_ctx.it = _ctx.result = ${value}`,
|
|
2363
|
+
async: false,
|
|
2364
|
+
sideEffects: true
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
var BreakCodegen = class {
|
|
2369
|
+
constructor() {
|
|
2370
|
+
this.command = "break";
|
|
2371
|
+
}
|
|
2372
|
+
generate(_node, _ctx) {
|
|
2373
|
+
return {
|
|
2374
|
+
code: "break",
|
|
2375
|
+
async: false,
|
|
2376
|
+
sideEffects: false
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
var ContinueCodegen = class {
|
|
2381
|
+
constructor() {
|
|
2382
|
+
this.command = "continue";
|
|
2383
|
+
}
|
|
2384
|
+
generate(_node, _ctx) {
|
|
2385
|
+
return {
|
|
2386
|
+
code: "continue",
|
|
2387
|
+
async: false,
|
|
2388
|
+
sideEffects: false
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2392
|
+
var BeepCodegen = class {
|
|
2393
|
+
constructor() {
|
|
2394
|
+
this.command = "beep";
|
|
2395
|
+
}
|
|
2396
|
+
generate(node, ctx) {
|
|
2397
|
+
const args = node.args ?? [];
|
|
2398
|
+
const roles = node.roles;
|
|
2399
|
+
const expr = roles?.patient ?? args[0];
|
|
2400
|
+
if (!expr) {
|
|
2401
|
+
return {
|
|
2402
|
+
code: `console.log('%c[beep]', 'color: orange; font-weight: bold')`,
|
|
2403
|
+
async: false,
|
|
2404
|
+
sideEffects: true
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
const value = ctx.generateExpression(expr);
|
|
2408
|
+
return {
|
|
2409
|
+
code: `console.log('%c[beep]', 'color: orange; font-weight: bold', ${value})`,
|
|
2410
|
+
async: false,
|
|
2411
|
+
sideEffects: true
|
|
2412
|
+
};
|
|
2413
|
+
}
|
|
2414
|
+
};
|
|
2415
|
+
var JsCodegen = class {
|
|
2416
|
+
constructor() {
|
|
2417
|
+
this.command = "js";
|
|
2418
|
+
}
|
|
2419
|
+
generate(node, ctx) {
|
|
2420
|
+
const args = node.args ?? [];
|
|
2421
|
+
const roles = node.roles;
|
|
2422
|
+
const bodyNode = roles?.patient ?? args[0];
|
|
2423
|
+
if (!bodyNode) return null;
|
|
2424
|
+
if (bodyNode.type === "literal") {
|
|
2425
|
+
const code = bodyNode.value;
|
|
2426
|
+
if (typeof code === "string") {
|
|
2427
|
+
return {
|
|
2428
|
+
code: `(function(_ctx) { ${code} })(_ctx)`,
|
|
2429
|
+
async: false,
|
|
2430
|
+
sideEffects: true
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
const value = ctx.generateExpression(bodyNode);
|
|
2435
|
+
return {
|
|
2436
|
+
code: `_ctx.it = _ctx.result = ${value}`,
|
|
2437
|
+
async: false,
|
|
2438
|
+
sideEffects: true
|
|
2439
|
+
};
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
var CopyCodegen = class {
|
|
2443
|
+
constructor() {
|
|
2444
|
+
this.command = "copy";
|
|
2445
|
+
}
|
|
2446
|
+
generate(node, ctx) {
|
|
2447
|
+
const args = node.args ?? [];
|
|
2448
|
+
const roles = node.roles;
|
|
2449
|
+
const contentNode = roles?.patient ?? args[0];
|
|
2450
|
+
if (!contentNode) return null;
|
|
2451
|
+
const content = ctx.generateExpression(contentNode);
|
|
2452
|
+
return {
|
|
2453
|
+
code: `await navigator.clipboard.writeText(String(${content}))`,
|
|
2454
|
+
async: true,
|
|
2455
|
+
sideEffects: true
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
var MakeCodegen = class {
|
|
2460
|
+
constructor() {
|
|
2461
|
+
this.command = "make";
|
|
2462
|
+
}
|
|
2463
|
+
generate(node, ctx) {
|
|
2464
|
+
const args = node.args ?? [];
|
|
2465
|
+
const roles = node.roles;
|
|
2466
|
+
const targetNode = roles?.patient ?? args[0];
|
|
2467
|
+
if (!targetNode) return null;
|
|
2468
|
+
if (targetNode.type === "htmlLiteral") {
|
|
2469
|
+
const tagNode = targetNode;
|
|
2470
|
+
const tag = tagNode.tag ?? "div";
|
|
2471
|
+
const classes = tagNode.classes ?? [];
|
|
2472
|
+
const id = tagNode.id;
|
|
2473
|
+
const attrs = tagNode.attributes ?? {};
|
|
2474
|
+
let code = `(() => { const _el = document.createElement('${tag}');`;
|
|
2475
|
+
if (classes.length > 0) {
|
|
2476
|
+
code += ` _el.className = '${classes.join(" ")}';`;
|
|
2477
|
+
}
|
|
2478
|
+
if (id) {
|
|
2479
|
+
code += ` _el.id = '${sanitizeIdentifier(id)}';`;
|
|
2480
|
+
}
|
|
2481
|
+
for (const [attr, val] of Object.entries(attrs)) {
|
|
2482
|
+
code += ` _el.setAttribute('${sanitizeSelector(attr)}', '${sanitizeSelector(val)}');`;
|
|
2483
|
+
}
|
|
2484
|
+
code += ` return _el; })()`;
|
|
2485
|
+
return {
|
|
2486
|
+
code: `_ctx.it = _ctx.result = ${code}`,
|
|
2487
|
+
async: false,
|
|
2488
|
+
sideEffects: true
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
if (targetNode.type === "literal") {
|
|
2492
|
+
const tag = targetNode.value;
|
|
2493
|
+
if (typeof tag === "string") {
|
|
2494
|
+
return {
|
|
2495
|
+
code: `_ctx.it = _ctx.result = document.createElement('${sanitizeSelector(tag)}')`,
|
|
2496
|
+
async: false,
|
|
2497
|
+
sideEffects: true
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
const expr = ctx.generateExpression(targetNode);
|
|
2502
|
+
return {
|
|
2503
|
+
code: `_ctx.it = _ctx.result = document.createElement(${expr})`,
|
|
2504
|
+
async: false,
|
|
2505
|
+
sideEffects: true
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
var SwapCodegen = class {
|
|
2510
|
+
constructor() {
|
|
2511
|
+
this.command = "swap";
|
|
2512
|
+
}
|
|
2513
|
+
generate(node, ctx) {
|
|
2514
|
+
const args = node.args ?? [];
|
|
2515
|
+
const roles = node.roles;
|
|
2516
|
+
if (args.length > 0 && args[0].type === "identifier" && args[0].value === "delete") {
|
|
2517
|
+
const targetNode2 = args[1] ?? node.target;
|
|
2518
|
+
if (!targetNode2) return null;
|
|
2519
|
+
const target2 = ctx.generateExpression(targetNode2);
|
|
2520
|
+
return { code: `${target2}.remove()`, async: false, sideEffects: true };
|
|
2521
|
+
}
|
|
2522
|
+
const contentNode = roles?.patient ?? args[args.length - 1];
|
|
2523
|
+
const targetNode = roles?.destination ?? node.target ?? args[0];
|
|
2524
|
+
if (!targetNode || !contentNode) return null;
|
|
2525
|
+
const target = ctx.generateExpression(targetNode);
|
|
2526
|
+
const content = ctx.generateExpression(contentNode);
|
|
2527
|
+
const strategy = this.resolveStrategy(node, args);
|
|
2528
|
+
switch (strategy) {
|
|
2529
|
+
case "outerHTML":
|
|
2530
|
+
return { code: `${target}.outerHTML = ${content}`, async: false, sideEffects: true };
|
|
2531
|
+
case "beforebegin":
|
|
2532
|
+
return {
|
|
2533
|
+
code: `${target}.insertAdjacentHTML('beforebegin', ${content})`,
|
|
2534
|
+
async: false,
|
|
2535
|
+
sideEffects: true
|
|
2536
|
+
};
|
|
2537
|
+
case "afterbegin":
|
|
2538
|
+
return {
|
|
2539
|
+
code: `${target}.insertAdjacentHTML('afterbegin', ${content})`,
|
|
2540
|
+
async: false,
|
|
2541
|
+
sideEffects: true
|
|
2542
|
+
};
|
|
2543
|
+
case "beforeend":
|
|
2544
|
+
return {
|
|
2545
|
+
code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
|
|
2546
|
+
async: false,
|
|
2547
|
+
sideEffects: true
|
|
2548
|
+
};
|
|
2549
|
+
case "afterend":
|
|
2550
|
+
return {
|
|
2551
|
+
code: `${target}.insertAdjacentHTML('afterend', ${content})`,
|
|
2552
|
+
async: false,
|
|
2553
|
+
sideEffects: true
|
|
2554
|
+
};
|
|
2555
|
+
case "morph":
|
|
2556
|
+
ctx.requireHelper("morph");
|
|
2557
|
+
return { code: `_rt.morph(${target}, ${content})`, async: false, sideEffects: true };
|
|
2558
|
+
case "innerHTML":
|
|
2559
|
+
default:
|
|
2560
|
+
return { code: `${target}.innerHTML = ${content}`, async: false, sideEffects: true };
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
resolveStrategy(node, args) {
|
|
2564
|
+
const mods = node.modifiers;
|
|
2565
|
+
if (mods?.strategy && typeof mods.strategy === "string") return mods.strategy.toLowerCase();
|
|
2566
|
+
if (args.length >= 2 && args[0].type === "identifier") {
|
|
2567
|
+
const name = (args[0].value ?? "").toLowerCase();
|
|
2568
|
+
const strategies = {
|
|
2569
|
+
innerhtml: "innerHTML",
|
|
2570
|
+
outerhtml: "outerHTML",
|
|
2571
|
+
morph: "morph",
|
|
2572
|
+
beforebegin: "beforebegin",
|
|
2573
|
+
afterbegin: "afterbegin",
|
|
2574
|
+
beforeend: "beforeend",
|
|
2575
|
+
afterend: "afterend",
|
|
2576
|
+
into: "innerHTML",
|
|
2577
|
+
over: "outerHTML"
|
|
2578
|
+
};
|
|
2579
|
+
if (strategies[name]) return strategies[name];
|
|
2580
|
+
}
|
|
2581
|
+
return "innerHTML";
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
var MorphCodegen = class {
|
|
2585
|
+
constructor() {
|
|
2586
|
+
this.command = "morph";
|
|
2587
|
+
}
|
|
2588
|
+
generate(node, ctx) {
|
|
2589
|
+
const args = node.args ?? [];
|
|
2590
|
+
const roles = node.roles;
|
|
2591
|
+
const targetNode = roles?.destination ?? node.target ?? args[0];
|
|
2592
|
+
const contentNode = roles?.patient ?? args[args.length > 1 ? args.length - 1 : 0];
|
|
2593
|
+
if (!targetNode) return null;
|
|
2594
|
+
const target = ctx.generateExpression(targetNode);
|
|
2595
|
+
ctx.requireHelper("morph");
|
|
2596
|
+
if (!contentNode || contentNode === targetNode) {
|
|
2597
|
+
return { code: `_rt.morph(${target}, '')`, async: false, sideEffects: true };
|
|
2598
|
+
}
|
|
2599
|
+
const content = ctx.generateExpression(contentNode);
|
|
2600
|
+
return { code: `_rt.morph(${target}, ${content})`, async: false, sideEffects: true };
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
var TransitionCodegen = class {
|
|
2604
|
+
constructor() {
|
|
2605
|
+
this.command = "transition";
|
|
2606
|
+
}
|
|
2607
|
+
generate(node, ctx) {
|
|
2608
|
+
const args = node.args ?? [];
|
|
2609
|
+
const roles = node.roles;
|
|
2610
|
+
const mods = node.modifiers;
|
|
2611
|
+
const propertyNode = roles?.patient ?? args[0];
|
|
2612
|
+
if (!propertyNode) return null;
|
|
2613
|
+
const property = ctx.generateExpression(propertyNode);
|
|
2614
|
+
const valueNode = mods?.to ?? args[1];
|
|
2615
|
+
if (!valueNode) return null;
|
|
2616
|
+
const value = ctx.generateExpression(valueNode);
|
|
2617
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2618
|
+
const durationNode = mods?.over;
|
|
2619
|
+
const duration = durationNode ? ctx.generateExpression(durationNode) : "300";
|
|
2620
|
+
const timingNode = mods?.with;
|
|
2621
|
+
const timing = timingNode ? ctx.generateExpression(timingNode) : "'ease'";
|
|
2622
|
+
ctx.requireHelper("transition");
|
|
2623
|
+
return {
|
|
2624
|
+
code: `await _rt.transition(${target}, ${property}, ${value}, ${duration}, ${timing})`,
|
|
2625
|
+
async: true,
|
|
2626
|
+
sideEffects: true
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
};
|
|
2630
|
+
var MeasureCodegen = class {
|
|
2631
|
+
constructor() {
|
|
2632
|
+
this.command = "measure";
|
|
2633
|
+
}
|
|
2634
|
+
generate(node, ctx) {
|
|
2635
|
+
const args = node.args ?? [];
|
|
2636
|
+
const roles = node.roles;
|
|
2637
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2638
|
+
const propNode = roles?.patient ?? args[0];
|
|
2639
|
+
if (!propNode) {
|
|
2640
|
+
return {
|
|
2641
|
+
code: `_ctx.it = _ctx.result = ${target}.getBoundingClientRect()`,
|
|
2642
|
+
async: false,
|
|
2643
|
+
sideEffects: true
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
if (propNode.type === "identifier") {
|
|
2647
|
+
const propName = propNode.value ?? "";
|
|
2648
|
+
const rectProps = ["width", "height", "top", "left", "right", "bottom", "x", "y"];
|
|
2649
|
+
if (rectProps.includes(propName.toLowerCase())) {
|
|
2650
|
+
return {
|
|
2651
|
+
code: `_ctx.it = _ctx.result = ${target}.getBoundingClientRect().${propName.toLowerCase()}`,
|
|
2652
|
+
async: false,
|
|
2653
|
+
sideEffects: true
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
const prop = ctx.generateExpression(propNode);
|
|
2658
|
+
ctx.requireHelper("measure");
|
|
2659
|
+
return {
|
|
2660
|
+
code: `_ctx.it = _ctx.result = _rt.measure(${target}, ${prop})`,
|
|
2661
|
+
async: false,
|
|
2662
|
+
sideEffects: true
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
};
|
|
2666
|
+
var SettleCodegen = class {
|
|
2667
|
+
constructor() {
|
|
2668
|
+
this.command = "settle";
|
|
2669
|
+
}
|
|
2670
|
+
generate(node, ctx) {
|
|
2671
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2672
|
+
const mods = node.modifiers;
|
|
2673
|
+
const timeoutNode = mods?.for;
|
|
2674
|
+
const timeout = timeoutNode ? ctx.generateExpression(timeoutNode) : "5000";
|
|
2675
|
+
ctx.requireHelper("settle");
|
|
2676
|
+
return {
|
|
2677
|
+
code: `await _rt.settle(${target}, ${timeout})`,
|
|
2678
|
+
async: true,
|
|
2679
|
+
sideEffects: true
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
var TellCodegen = class {
|
|
2684
|
+
constructor() {
|
|
2685
|
+
this.command = "tell";
|
|
2686
|
+
}
|
|
2687
|
+
generate(node, ctx) {
|
|
2688
|
+
const args = node.args ?? [];
|
|
2689
|
+
const roles = node.roles;
|
|
2690
|
+
const targetNode = roles?.destination ?? args[0];
|
|
2691
|
+
if (!targetNode) return null;
|
|
2692
|
+
const target = ctx.generateExpression(targetNode);
|
|
2693
|
+
return {
|
|
2694
|
+
code: `{ const _prevMe = _ctx.me; _ctx.me = ${target}; _ctx.you = ${target};`,
|
|
2695
|
+
async: false,
|
|
2696
|
+
sideEffects: true
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
var AsyncCodegen = class {
|
|
2701
|
+
constructor() {
|
|
2702
|
+
this.command = "async";
|
|
2703
|
+
}
|
|
2704
|
+
generate(_node, _ctx) {
|
|
2705
|
+
return {
|
|
2706
|
+
code: `(async () => {`,
|
|
2707
|
+
async: false,
|
|
2708
|
+
// The outer handler doesn't await this
|
|
2709
|
+
sideEffects: true
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
};
|
|
2713
|
+
var InstallCodegen = class {
|
|
2714
|
+
constructor() {
|
|
2715
|
+
this.command = "install";
|
|
2716
|
+
}
|
|
2717
|
+
generate(node, ctx) {
|
|
2718
|
+
const args = node.args ?? [];
|
|
2719
|
+
const roles = node.roles;
|
|
2720
|
+
const behaviorNode = roles?.patient ?? args[0];
|
|
2721
|
+
if (!behaviorNode) return null;
|
|
2722
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2723
|
+
let behaviorName;
|
|
2724
|
+
if (behaviorNode.type === "identifier") {
|
|
2725
|
+
behaviorName = behaviorNode.value ?? "";
|
|
2726
|
+
} else if (behaviorNode.type === "literal") {
|
|
2727
|
+
behaviorName = String(behaviorNode.value);
|
|
2728
|
+
} else {
|
|
2729
|
+
behaviorName = ctx.generateExpression(behaviorNode);
|
|
2730
|
+
ctx.requireHelper("installBehavior");
|
|
2731
|
+
return {
|
|
2732
|
+
code: `_rt.installBehavior(${target}, ${behaviorName})`,
|
|
2733
|
+
async: false,
|
|
2734
|
+
sideEffects: true
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
ctx.requireHelper("installBehavior");
|
|
2738
|
+
const paramsNode = args.length > 1 ? args[1] : void 0;
|
|
2739
|
+
if (paramsNode && paramsNode.type === "object") {
|
|
2740
|
+
const params = ctx.generateExpression(paramsNode);
|
|
2741
|
+
return {
|
|
2742
|
+
code: `_rt.installBehavior(${target}, '${sanitizeIdentifier(behaviorName)}', ${params})`,
|
|
2743
|
+
async: false,
|
|
2744
|
+
sideEffects: true
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
return {
|
|
2748
|
+
code: `_rt.installBehavior(${target}, '${sanitizeIdentifier(behaviorName)}')`,
|
|
2749
|
+
async: false,
|
|
2750
|
+
sideEffects: true
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
var RenderCodegen = class {
|
|
2755
|
+
constructor() {
|
|
2756
|
+
this.command = "render";
|
|
2757
|
+
}
|
|
2758
|
+
generate(node, ctx) {
|
|
2759
|
+
const args = node.args ?? [];
|
|
2760
|
+
const roles = node.roles;
|
|
2761
|
+
const mods = node.modifiers;
|
|
2762
|
+
const templateNode = roles?.patient ?? args[0];
|
|
2763
|
+
if (!templateNode) return null;
|
|
2764
|
+
const template = ctx.generateExpression(templateNode);
|
|
2765
|
+
const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
|
|
2766
|
+
const varsNode = mods?.with ?? (args.length > 1 ? args[1] : void 0);
|
|
2767
|
+
ctx.requireHelper("render");
|
|
2768
|
+
if (varsNode) {
|
|
2769
|
+
const vars = ctx.generateExpression(varsNode);
|
|
2770
|
+
return {
|
|
2771
|
+
code: `${target}.innerHTML = _rt.render(${template}, ${vars})`,
|
|
2772
|
+
async: false,
|
|
2773
|
+
sideEffects: true
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
return {
|
|
2777
|
+
code: `${target}.innerHTML = _rt.render(${template}, {})`,
|
|
2778
|
+
async: false,
|
|
2779
|
+
sideEffects: true
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
var commandCodegens = /* @__PURE__ */ new Map([
|
|
2784
|
+
["toggle", new ToggleCodegen()],
|
|
2785
|
+
["add", new AddCodegen()],
|
|
2786
|
+
["remove", new RemoveCodegen()],
|
|
2787
|
+
["set", new SetCodegen()],
|
|
2788
|
+
["put", new PutCodegen()],
|
|
2789
|
+
["show", new ShowCodegen()],
|
|
2790
|
+
["hide", new HideCodegen()],
|
|
2791
|
+
["focus", new FocusCodegen()],
|
|
2792
|
+
["blur", new BlurCodegen()],
|
|
2793
|
+
["log", new LogCodegen()],
|
|
2794
|
+
["wait", new WaitCodegen()],
|
|
2795
|
+
["fetch", new FetchCodegen()],
|
|
2796
|
+
["send", new SendCodegen()],
|
|
2797
|
+
["trigger", new SendCodegen()],
|
|
2798
|
+
// Alias
|
|
2799
|
+
["increment", new IncrementCodegen()],
|
|
2800
|
+
["decrement", new DecrementCodegen()],
|
|
2801
|
+
["halt", new HaltCodegen()],
|
|
2802
|
+
["exit", new ExitCodegen()],
|
|
2803
|
+
["return", new ReturnCodegen()],
|
|
2804
|
+
["call", new CallCodegen()],
|
|
2805
|
+
["scroll", new ScrollCodegen()],
|
|
2806
|
+
["take", new TakeCodegen()],
|
|
2807
|
+
["unless", new UnlessCodegen()],
|
|
2808
|
+
["throw", new ThrowCodegen()],
|
|
2809
|
+
["default", new DefaultCodegen()],
|
|
2810
|
+
["go", new GoCodegen()],
|
|
2811
|
+
["append", new AppendCodegen()],
|
|
2812
|
+
["pick", new PickCodegen()],
|
|
2813
|
+
["push-url", new PushUrlCodegen()],
|
|
2814
|
+
["replace-url", new ReplaceUrlCodegen()],
|
|
2815
|
+
["get", new GetCodegen()],
|
|
2816
|
+
["break", new BreakCodegen()],
|
|
2817
|
+
["continue", new ContinueCodegen()],
|
|
2818
|
+
["beep", new BeepCodegen()],
|
|
2819
|
+
["js", new JsCodegen()],
|
|
2820
|
+
["copy", new CopyCodegen()],
|
|
2821
|
+
["make", new MakeCodegen()],
|
|
2822
|
+
// Phase 1 additions
|
|
2823
|
+
["swap", new SwapCodegen()],
|
|
2824
|
+
["morph", new MorphCodegen()],
|
|
2825
|
+
["transition", new TransitionCodegen()],
|
|
2826
|
+
["measure", new MeasureCodegen()],
|
|
2827
|
+
["settle", new SettleCodegen()],
|
|
2828
|
+
["tell", new TellCodegen()],
|
|
2829
|
+
["async", new AsyncCodegen()],
|
|
2830
|
+
["install", new InstallCodegen()],
|
|
2831
|
+
["render", new RenderCodegen()]
|
|
2832
|
+
]);
|
|
2833
|
+
function generateCommand(node, ctx) {
|
|
2834
|
+
const codegen = commandCodegens.get(node.name);
|
|
2835
|
+
if (!codegen) {
|
|
2836
|
+
return null;
|
|
2837
|
+
}
|
|
2838
|
+
return codegen.generate(node, ctx);
|
|
2839
|
+
}
|
|
2840
|
+
function parseDuration(duration) {
|
|
2841
|
+
const match = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/i.exec(duration.trim());
|
|
2842
|
+
if (!match) return null;
|
|
2843
|
+
const value = parseFloat(match[1]);
|
|
2844
|
+
const unit = (match[2] ?? "ms").toLowerCase();
|
|
2845
|
+
switch (unit) {
|
|
2846
|
+
case "ms":
|
|
2847
|
+
return value;
|
|
2848
|
+
case "s":
|
|
2849
|
+
return value * 1e3;
|
|
2850
|
+
case "m":
|
|
2851
|
+
return value * 6e4;
|
|
2852
|
+
case "h":
|
|
2853
|
+
return value * 36e5;
|
|
2854
|
+
default:
|
|
2855
|
+
return value;
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
var EventHandlerCodegen = class {
|
|
2859
|
+
constructor(ctx, analysis) {
|
|
2860
|
+
this.ctx = ctx;
|
|
2861
|
+
this.analysis = analysis;
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2864
|
+
* Generate complete code for an event handler.
|
|
2865
|
+
*/
|
|
2866
|
+
generate(node) {
|
|
2867
|
+
const eventName = node.event;
|
|
2868
|
+
const modifiers = node.modifiers ?? {};
|
|
2869
|
+
const body = node.body ?? [];
|
|
2870
|
+
const bodyCode = this.generateBody(body);
|
|
2871
|
+
const isAsync = this.analysis.controlFlow.hasAsync;
|
|
2872
|
+
const listenerOptions = this.buildListenerOptions(modifiers);
|
|
2873
|
+
const modifierCode = this.generateModifierCode(modifiers);
|
|
2874
|
+
const handlerCode = this.generateHandlerFunction(eventName, bodyCode, modifierCode, isAsync);
|
|
2875
|
+
const bindingCode = this.generateBindingCode(eventName, modifiers, listenerOptions);
|
|
2876
|
+
const cleanup = this.generateCleanupCode(eventName, modifiers);
|
|
2877
|
+
const imports = this.collectImports();
|
|
2878
|
+
return {
|
|
2879
|
+
handlerCode,
|
|
2880
|
+
bindingCode,
|
|
2881
|
+
cleanup,
|
|
2882
|
+
async: isAsync,
|
|
2883
|
+
imports
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
// ===========================================================================
|
|
2887
|
+
// BODY GENERATION
|
|
2888
|
+
// ===========================================================================
|
|
2889
|
+
/**
|
|
2890
|
+
* Generate code for the handler body.
|
|
2891
|
+
*/
|
|
2892
|
+
generateBody(nodes) {
|
|
2893
|
+
const statements = [];
|
|
2894
|
+
for (const node of nodes) {
|
|
2895
|
+
const code = this.generateNode(node);
|
|
2896
|
+
if (code) {
|
|
2897
|
+
statements.push(code);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
return statements.join("\n");
|
|
2901
|
+
}
|
|
2902
|
+
/**
|
|
2903
|
+
* Generate code for a single AST node.
|
|
2904
|
+
*/
|
|
2905
|
+
generateNode(node) {
|
|
2906
|
+
switch (node.type) {
|
|
2907
|
+
case "command":
|
|
2908
|
+
return this.generateCommandCode(node);
|
|
2909
|
+
case "if":
|
|
2910
|
+
return generateIf(node, this.ctx, (nodes) => this.generateBody(nodes));
|
|
2911
|
+
case "repeat":
|
|
2912
|
+
return generateRepeat(node, this.ctx, (nodes) => this.generateBody(nodes));
|
|
2913
|
+
case "foreach":
|
|
2914
|
+
return generateForEach(node, this.ctx, (nodes) => this.generateBody(nodes));
|
|
2915
|
+
case "while":
|
|
2916
|
+
return generateWhile(node, this.ctx, (nodes) => this.generateBody(nodes));
|
|
2917
|
+
case "event": {
|
|
2918
|
+
const nested = node;
|
|
2919
|
+
return this.generateBody(nested.body ?? []);
|
|
2920
|
+
}
|
|
2921
|
+
default:
|
|
2922
|
+
return null;
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
/**
|
|
2926
|
+
* Generate code for a command.
|
|
2927
|
+
*/
|
|
2928
|
+
generateCommandCode(node) {
|
|
2929
|
+
const result = generateCommand(node, this.ctx);
|
|
2930
|
+
if (!result) {
|
|
2931
|
+
return null;
|
|
2932
|
+
}
|
|
2933
|
+
return result.code;
|
|
2934
|
+
}
|
|
2935
|
+
// ===========================================================================
|
|
2936
|
+
// MODIFIER HANDLING
|
|
2937
|
+
// ===========================================================================
|
|
2938
|
+
/**
|
|
2939
|
+
* Build AddEventListenerOptions from modifiers.
|
|
2940
|
+
*/
|
|
2941
|
+
buildListenerOptions(modifiers) {
|
|
2942
|
+
const options = {};
|
|
2943
|
+
if (modifiers.once) {
|
|
2944
|
+
options.once = true;
|
|
2945
|
+
}
|
|
2946
|
+
if (modifiers.passive) {
|
|
2947
|
+
options.passive = true;
|
|
2948
|
+
}
|
|
2949
|
+
if (modifiers.capture) {
|
|
2950
|
+
options.capture = true;
|
|
2951
|
+
}
|
|
2952
|
+
return options;
|
|
2953
|
+
}
|
|
2954
|
+
/**
|
|
2955
|
+
* Generate code for event modifiers (prevent, stop).
|
|
2956
|
+
*/
|
|
2957
|
+
generateModifierCode(modifiers) {
|
|
2958
|
+
const code = [];
|
|
2959
|
+
if (modifiers.prevent) {
|
|
2960
|
+
code.push("_event.preventDefault();");
|
|
2961
|
+
}
|
|
2962
|
+
if (modifiers.stop) {
|
|
2963
|
+
code.push("_event.stopPropagation();");
|
|
2964
|
+
}
|
|
2965
|
+
return code.join("\n");
|
|
2966
|
+
}
|
|
2967
|
+
// ===========================================================================
|
|
2968
|
+
// HANDLER FUNCTION GENERATION
|
|
2969
|
+
// ===========================================================================
|
|
2970
|
+
/**
|
|
2971
|
+
* Generate the complete handler function.
|
|
2972
|
+
*/
|
|
2973
|
+
generateHandlerFunction(_eventName, bodyCode, modifierCode, isAsync) {
|
|
2974
|
+
const asyncKeyword = isAsync ? "async " : "";
|
|
2975
|
+
const handlerId = this.ctx.handlerId;
|
|
2976
|
+
const contextInit = `const _ctx = _rt.createContext(_event, this);`;
|
|
2977
|
+
const combinedBody = [modifierCode, bodyCode].filter(Boolean).join("\n");
|
|
2978
|
+
const needsTryCatch = this.analysis.controlFlow.canThrow;
|
|
2979
|
+
let functionBody;
|
|
2980
|
+
if (needsTryCatch) {
|
|
2981
|
+
functionBody = `${contextInit}
|
|
2982
|
+
try {
|
|
2983
|
+
${combinedBody}
|
|
2984
|
+
} catch (_e) {
|
|
2985
|
+
if (_e === _rt.HALT) {
|
|
2986
|
+
_event.preventDefault();
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2989
|
+
if (_e === _rt.EXIT) {
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
throw _e;
|
|
2993
|
+
}`;
|
|
2994
|
+
} else {
|
|
2995
|
+
functionBody = `${contextInit}
|
|
2996
|
+
${combinedBody}`;
|
|
2997
|
+
}
|
|
2998
|
+
return `${asyncKeyword}function _handler_${handlerId}(_event) {
|
|
2999
|
+
${functionBody}
|
|
3000
|
+
}`;
|
|
3001
|
+
}
|
|
3002
|
+
// ===========================================================================
|
|
3003
|
+
// BINDING CODE GENERATION
|
|
3004
|
+
// ===========================================================================
|
|
3005
|
+
/**
|
|
3006
|
+
* Generate code to bind the handler to an element.
|
|
3007
|
+
*/
|
|
3008
|
+
generateBindingCode(eventName, modifiers, options) {
|
|
3009
|
+
const handlerId = this.ctx.handlerId;
|
|
3010
|
+
const event = JSON.stringify(eventName);
|
|
3011
|
+
let handler = `_handler_${handlerId}`;
|
|
3012
|
+
if (modifiers.debounce) {
|
|
3013
|
+
this.ctx.requireHelper("debounce");
|
|
3014
|
+
handler = `_rt.debounce(${handler}, ${modifiers.debounce})`;
|
|
3015
|
+
}
|
|
3016
|
+
if (modifiers.throttle) {
|
|
3017
|
+
this.ctx.requireHelper("throttle");
|
|
3018
|
+
handler = `_rt.throttle(${handler}, ${modifiers.throttle})`;
|
|
3019
|
+
}
|
|
3020
|
+
const optionsKeys = Object.keys(options).filter((k) => options[k]);
|
|
3021
|
+
const optionsArg = optionsKeys.length > 0 ? `, { ${optionsKeys.map((k) => `${k}: true`).join(", ")} }` : "";
|
|
3022
|
+
if (modifiers.from) {
|
|
3023
|
+
const delegateSelector = JSON.stringify(modifiers.from);
|
|
3024
|
+
this.ctx.requireHelper("delegate");
|
|
3025
|
+
return `_rt.delegate(_el, ${event}, ${delegateSelector}, ${handler}${optionsArg});`;
|
|
3026
|
+
}
|
|
3027
|
+
return `_el.addEventListener(${event}, ${handler}${optionsArg});`;
|
|
3028
|
+
}
|
|
3029
|
+
// ===========================================================================
|
|
3030
|
+
// CLEANUP CODE GENERATION
|
|
3031
|
+
// ===========================================================================
|
|
3032
|
+
/**
|
|
3033
|
+
* Generate cleanup code for removing the handler.
|
|
3034
|
+
*/
|
|
3035
|
+
generateCleanupCode(eventName, modifiers) {
|
|
3036
|
+
if (modifiers.once) {
|
|
3037
|
+
return null;
|
|
3038
|
+
}
|
|
3039
|
+
const handlerId = this.ctx.handlerId;
|
|
3040
|
+
const event = JSON.stringify(eventName);
|
|
3041
|
+
const capture = modifiers.capture ? ", true" : "";
|
|
3042
|
+
return `_el.removeEventListener(${event}, _handler_${handlerId}${capture});`;
|
|
3043
|
+
}
|
|
3044
|
+
// ===========================================================================
|
|
3045
|
+
// IMPORTS COLLECTION
|
|
3046
|
+
// ===========================================================================
|
|
3047
|
+
/**
|
|
3048
|
+
* Collect required runtime imports.
|
|
3049
|
+
*/
|
|
3050
|
+
collectImports() {
|
|
3051
|
+
const imports = ["createContext"];
|
|
3052
|
+
for (const helper of this.ctx.requiredHelpers) {
|
|
3053
|
+
if (!imports.includes(helper)) {
|
|
3054
|
+
imports.push(helper);
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
for (const helper of this.analysis.dependencies.runtimeHelpers) {
|
|
3058
|
+
if (!imports.includes(helper)) {
|
|
3059
|
+
imports.push(helper);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
return imports;
|
|
3063
|
+
}
|
|
3064
|
+
};
|
|
3065
|
+
function generateEventHandler(node, ctx, analysis) {
|
|
3066
|
+
const codegen = new EventHandlerCodegen(ctx, analysis);
|
|
3067
|
+
return codegen.generate(node);
|
|
3068
|
+
}
|
|
3069
|
+
function generateBindings(handlers) {
|
|
3070
|
+
const bindings = [];
|
|
3071
|
+
for (const { selector, eventName, handlerId, options } of handlers) {
|
|
3072
|
+
const sanitized = sanitizeSelector(selector);
|
|
3073
|
+
const event = JSON.stringify(eventName);
|
|
3074
|
+
const optionsKeys = options ? Object.keys(options).filter((k) => options[k]) : [];
|
|
3075
|
+
const optionsArg = optionsKeys.length > 0 ? `, { ${optionsKeys.map((k) => `${k}: true`).join(", ")} }` : "";
|
|
3076
|
+
bindings.push(
|
|
3077
|
+
`document.querySelectorAll('${sanitized}').forEach(_el => _el.addEventListener(${event}, _handler_${handlerId}${optionsArg}));`
|
|
3078
|
+
);
|
|
3079
|
+
}
|
|
3080
|
+
return bindings.join("\n");
|
|
3081
|
+
}
|
|
3082
|
+
function generateInitialization(handlers) {
|
|
3083
|
+
const bindings = generateBindings(handlers);
|
|
3084
|
+
return `_rt.ready(() => {
|
|
3085
|
+
${bindings}
|
|
3086
|
+
});`;
|
|
3087
|
+
}
|
|
3088
|
+
var DEFAULT_COMPILE_OPTIONS = {
|
|
3089
|
+
language: "en",
|
|
3090
|
+
confidenceThreshold: 0.7,
|
|
3091
|
+
debug: false,
|
|
3092
|
+
codegen: {
|
|
3093
|
+
target: "es2020",
|
|
3094
|
+
mode: "esm",
|
|
3095
|
+
minify: false,
|
|
3096
|
+
sourceMaps: true,
|
|
3097
|
+
runtimeImport: "@hyperfixi/aot-compiler/runtime",
|
|
3098
|
+
preserveComments: false,
|
|
3099
|
+
debugMode: false
|
|
3100
|
+
},
|
|
3101
|
+
optimizationLevel: 2
|
|
3102
|
+
};
|
|
3103
|
+
var DEFAULT_CODEGEN_OPTIONS = {
|
|
3104
|
+
target: "es2020",
|
|
3105
|
+
mode: "esm",
|
|
3106
|
+
minify: false,
|
|
3107
|
+
sourceMaps: true,
|
|
3108
|
+
runtimeImport: "@hyperfixi/aot-compiler/runtime",
|
|
3109
|
+
preserveComments: false,
|
|
3110
|
+
debugMode: false
|
|
3111
|
+
};
|
|
3112
|
+
var AOTCompiler = class {
|
|
3113
|
+
constructor() {
|
|
3114
|
+
this.parser = null;
|
|
3115
|
+
this.semanticParser = null;
|
|
3116
|
+
this.analyzer = new Analyzer();
|
|
3117
|
+
this.optimizer = new OptimizationPipeline();
|
|
3118
|
+
this.usedIds = /* @__PURE__ */ new Set();
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Set the traditional parser instance.
|
|
3122
|
+
*/
|
|
3123
|
+
setParser(parser) {
|
|
3124
|
+
this.parser = parser;
|
|
3125
|
+
}
|
|
3126
|
+
/**
|
|
3127
|
+
* Set the semantic parser for multilingual support.
|
|
3128
|
+
*/
|
|
3129
|
+
setSemanticParser(parser) {
|
|
3130
|
+
this.semanticParser = parser;
|
|
3131
|
+
}
|
|
3132
|
+
/**
|
|
3133
|
+
* Reset compiler state between compilations.
|
|
3134
|
+
*/
|
|
3135
|
+
reset() {
|
|
3136
|
+
this.usedIds.clear();
|
|
3137
|
+
}
|
|
3138
|
+
// ===========================================================================
|
|
3139
|
+
// EXTRACTION
|
|
3140
|
+
// ===========================================================================
|
|
3141
|
+
/**
|
|
3142
|
+
* Extract hyperscript from source code.
|
|
3143
|
+
*/
|
|
3144
|
+
extract(source, filename) {
|
|
3145
|
+
const scanner = this.createScanner(filename);
|
|
3146
|
+
return scanner.extract(source, filename);
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Create appropriate scanner for file type.
|
|
3150
|
+
*/
|
|
3151
|
+
createScanner(filename) {
|
|
3152
|
+
if (filename.endsWith(".vue")) {
|
|
3153
|
+
return new VueScanner();
|
|
3154
|
+
}
|
|
3155
|
+
if (filename.endsWith(".svelte")) {
|
|
3156
|
+
return new SvelteScanner();
|
|
3157
|
+
}
|
|
3158
|
+
if (filename.match(/\.(jsx|tsx)$/)) {
|
|
3159
|
+
return new JSXScanner();
|
|
3160
|
+
}
|
|
3161
|
+
return new HTMLScanner();
|
|
3162
|
+
}
|
|
3163
|
+
// ===========================================================================
|
|
3164
|
+
// PARSING
|
|
3165
|
+
// ===========================================================================
|
|
3166
|
+
/**
|
|
3167
|
+
* Parse a hyperscript string to AST.
|
|
3168
|
+
*/
|
|
3169
|
+
parse(code, options = {}) {
|
|
3170
|
+
const { language = "en", confidenceThreshold = 0.7, debug = false } = options;
|
|
3171
|
+
let ast = null;
|
|
3172
|
+
if (language !== "en" && this.semanticParser?.supportsLanguage(language)) {
|
|
3173
|
+
const result = this.semanticParser.analyze(code, language);
|
|
3174
|
+
if (result.node && result.confidence >= confidenceThreshold) {
|
|
3175
|
+
const { ast: semanticAst, warnings } = this.semanticParser.buildAST(result.node);
|
|
3176
|
+
if (debug && warnings.length > 0) {
|
|
3177
|
+
console.log(`[aot] Semantic warnings for "${code}":`, warnings);
|
|
3178
|
+
}
|
|
3179
|
+
ast = semanticAst;
|
|
3180
|
+
} else if (debug) {
|
|
3181
|
+
console.log(
|
|
3182
|
+
`[aot] Semantic parse failed for "${code}": ${result.errors?.join(", ") || "low confidence"}`
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
if (!ast && this.parser) {
|
|
3187
|
+
try {
|
|
3188
|
+
ast = this.parser.parse(code, language);
|
|
3189
|
+
} catch (error) {
|
|
3190
|
+
if (debug) {
|
|
3191
|
+
console.log(`[aot] Parse error for "${code}":`, error);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
if (!ast) {
|
|
3196
|
+
ast = this.createSimpleAST(code);
|
|
3197
|
+
}
|
|
3198
|
+
if (ast && ast.type !== "event") {
|
|
3199
|
+
ast = {
|
|
3200
|
+
type: "event",
|
|
3201
|
+
event: "click",
|
|
3202
|
+
modifiers: {},
|
|
3203
|
+
body: [ast]
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
return ast;
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Create a simple AST from code (for testing without full parser).
|
|
3210
|
+
*/
|
|
3211
|
+
createSimpleAST(code) {
|
|
3212
|
+
const eventMatch = /^on\s+(\w+)(?:\.(\w+))?\s+(.+)$/i.exec(code.trim());
|
|
3213
|
+
if (eventMatch) {
|
|
3214
|
+
const [, eventName, modifier, body] = eventMatch;
|
|
3215
|
+
return {
|
|
3216
|
+
type: "event",
|
|
3217
|
+
event: eventName,
|
|
3218
|
+
modifiers: modifier ? { [modifier]: true } : {},
|
|
3219
|
+
body: [this.parseSimpleCommand(body)].filter(Boolean)
|
|
3220
|
+
};
|
|
3221
|
+
}
|
|
3222
|
+
const cmd = this.parseSimpleCommand(code);
|
|
3223
|
+
if (cmd) {
|
|
3224
|
+
return {
|
|
3225
|
+
type: "event",
|
|
3226
|
+
event: "click",
|
|
3227
|
+
body: [cmd]
|
|
3228
|
+
};
|
|
3229
|
+
}
|
|
3230
|
+
return null;
|
|
3231
|
+
}
|
|
3232
|
+
/**
|
|
3233
|
+
* Parse a simple command (for testing).
|
|
3234
|
+
*/
|
|
3235
|
+
parseSimpleCommand(code) {
|
|
3236
|
+
const trimmed = code.trim();
|
|
3237
|
+
const toggleMatch = /^toggle\s+\.([a-zA-Z_][a-zA-Z0-9_-]*)$/i.exec(trimmed);
|
|
3238
|
+
if (toggleMatch) {
|
|
3239
|
+
return {
|
|
3240
|
+
type: "command",
|
|
3241
|
+
name: "toggle",
|
|
3242
|
+
args: [{ type: "selector", value: "." + toggleMatch[1] }]
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
const addMatch = /^add\s+\.([a-zA-Z_][a-zA-Z0-9_-]*)$/i.exec(trimmed);
|
|
3246
|
+
if (addMatch) {
|
|
3247
|
+
return {
|
|
3248
|
+
type: "command",
|
|
3249
|
+
name: "add",
|
|
3250
|
+
args: [{ type: "selector", value: "." + addMatch[1] }]
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
const removeMatch = /^remove\s+\.([a-zA-Z_][a-zA-Z0-9_-]*)$/i.exec(trimmed);
|
|
3254
|
+
if (removeMatch) {
|
|
3255
|
+
return {
|
|
3256
|
+
type: "command",
|
|
3257
|
+
name: "remove",
|
|
3258
|
+
args: [{ type: "selector", value: "." + removeMatch[1] }]
|
|
3259
|
+
};
|
|
3260
|
+
}
|
|
3261
|
+
if (/^show$/i.test(trimmed)) {
|
|
3262
|
+
return { type: "command", name: "show", args: [] };
|
|
3263
|
+
}
|
|
3264
|
+
if (/^hide$/i.test(trimmed)) {
|
|
3265
|
+
return { type: "command", name: "hide", args: [] };
|
|
3266
|
+
}
|
|
3267
|
+
if (/^focus$/i.test(trimmed)) {
|
|
3268
|
+
return { type: "command", name: "focus", args: [] };
|
|
3269
|
+
}
|
|
3270
|
+
if (/^blur$/i.test(trimmed)) {
|
|
3271
|
+
return { type: "command", name: "blur", args: [] };
|
|
3272
|
+
}
|
|
3273
|
+
const setMatch = /^set\s+:(\w+)\s+to\s+(.+)$/i.exec(trimmed);
|
|
3274
|
+
if (setMatch) {
|
|
3275
|
+
const [, varName, valueStr] = setMatch;
|
|
3276
|
+
return {
|
|
3277
|
+
type: "command",
|
|
3278
|
+
name: "set",
|
|
3279
|
+
args: [{ type: "variable", name: ":" + varName, scope: "local" }],
|
|
3280
|
+
modifiers: { to: this.parseSimpleValue(valueStr) }
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
const incrMatch = /^(increment|decrement)\s+:(\w+)$/i.exec(trimmed);
|
|
3284
|
+
if (incrMatch) {
|
|
3285
|
+
const [, cmd, varName] = incrMatch;
|
|
3286
|
+
return {
|
|
3287
|
+
type: "command",
|
|
3288
|
+
name: cmd.toLowerCase(),
|
|
3289
|
+
args: [{ type: "variable", name: ":" + varName, scope: "local" }]
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
const waitMatch = /^wait\s+(\d+(?:\.\d+)?)(ms|s)$/i.exec(trimmed);
|
|
3293
|
+
if (waitMatch) {
|
|
3294
|
+
const [, value, unit] = waitMatch;
|
|
3295
|
+
const ms = unit.toLowerCase() === "s" ? parseFloat(value) * 1e3 : parseFloat(value);
|
|
3296
|
+
return {
|
|
3297
|
+
type: "command",
|
|
3298
|
+
name: "wait",
|
|
3299
|
+
args: [{ type: "literal", value: ms }]
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
const logMatch = /^log\s+(.+)$/i.exec(trimmed);
|
|
3303
|
+
if (logMatch) {
|
|
3304
|
+
return {
|
|
3305
|
+
type: "command",
|
|
3306
|
+
name: "log",
|
|
3307
|
+
args: [this.parseSimpleValue(logMatch[1])]
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
const sendMatch = /^send\s+"([^"]+)"(?:\s+to\s+(\w+))?$/i.exec(trimmed);
|
|
3311
|
+
if (sendMatch) {
|
|
3312
|
+
const [, eventName, target] = sendMatch;
|
|
3313
|
+
const node = {
|
|
3314
|
+
type: "command",
|
|
3315
|
+
name: "send",
|
|
3316
|
+
args: [{ type: "literal", value: eventName }]
|
|
3317
|
+
};
|
|
3318
|
+
if (target) {
|
|
3319
|
+
node.target = { type: "identifier", value: target };
|
|
3320
|
+
}
|
|
3321
|
+
return node;
|
|
3322
|
+
}
|
|
3323
|
+
const fetchMatch = /^fetch\s+(\S+)(?:\s+as\s+(\w+))?$/i.exec(trimmed);
|
|
3324
|
+
if (fetchMatch) {
|
|
3325
|
+
const [, url, format] = fetchMatch;
|
|
3326
|
+
const node = {
|
|
3327
|
+
type: "command",
|
|
3328
|
+
name: "fetch",
|
|
3329
|
+
args: [{ type: "literal", value: url }]
|
|
3330
|
+
};
|
|
3331
|
+
if (format) {
|
|
3332
|
+
node.modifiers = { as: format };
|
|
3333
|
+
}
|
|
3334
|
+
return node;
|
|
3335
|
+
}
|
|
3336
|
+
const putMatch = /^put\s+"([^"]+)"\s+(into|before|after)\s+(\S+)$/i.exec(trimmed);
|
|
3337
|
+
if (putMatch) {
|
|
3338
|
+
const [, content, position, target] = putMatch;
|
|
3339
|
+
return {
|
|
3340
|
+
type: "command",
|
|
3341
|
+
name: "put",
|
|
3342
|
+
args: [{ type: "literal", value: content }],
|
|
3343
|
+
modifiers: { position, [position]: this.parseSimpleValue(target) }
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
/**
|
|
3349
|
+
* Parse a simple value string to an AST node.
|
|
3350
|
+
*/
|
|
3351
|
+
parseSimpleValue(valueStr) {
|
|
3352
|
+
const trimmed = valueStr.trim();
|
|
3353
|
+
const strMatch = /^"([^"]*)"$/.exec(trimmed) ?? /^'([^']*)'$/.exec(trimmed);
|
|
3354
|
+
if (strMatch) {
|
|
3355
|
+
return { type: "literal", value: strMatch[1] };
|
|
3356
|
+
}
|
|
3357
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
3358
|
+
return { type: "literal", value: parseFloat(trimmed) };
|
|
3359
|
+
}
|
|
3360
|
+
if (trimmed === "true") return { type: "literal", value: true };
|
|
3361
|
+
if (trimmed === "false") return { type: "literal", value: false };
|
|
3362
|
+
if (/^[#.][a-zA-Z_]/.test(trimmed)) {
|
|
3363
|
+
return { type: "selector", value: trimmed };
|
|
3364
|
+
}
|
|
3365
|
+
if (trimmed.startsWith(":")) {
|
|
3366
|
+
return { type: "variable", name: trimmed, scope: "local" };
|
|
3367
|
+
}
|
|
3368
|
+
return { type: "identifier", value: trimmed };
|
|
3369
|
+
}
|
|
3370
|
+
// ===========================================================================
|
|
3371
|
+
// ANALYSIS
|
|
3372
|
+
// ===========================================================================
|
|
3373
|
+
/**
|
|
3374
|
+
* Analyze an AST for optimization and code generation.
|
|
3375
|
+
*/
|
|
3376
|
+
analyze(ast) {
|
|
3377
|
+
return this.analyzer.analyze(ast);
|
|
3378
|
+
}
|
|
3379
|
+
// ===========================================================================
|
|
3380
|
+
// COMPILATION
|
|
3381
|
+
// ===========================================================================
|
|
3382
|
+
/**
|
|
3383
|
+
* Compile a single hyperscript string to JavaScript.
|
|
3384
|
+
*/
|
|
3385
|
+
compileScript(code, options = {}) {
|
|
3386
|
+
const mergedOptions = { ...DEFAULT_COMPILE_OPTIONS, ...options };
|
|
3387
|
+
const ast = this.parse(code, mergedOptions);
|
|
3388
|
+
if (!ast) {
|
|
3389
|
+
return {
|
|
3390
|
+
success: false,
|
|
3391
|
+
errors: ["Failed to parse hyperscript"],
|
|
3392
|
+
warnings: [],
|
|
3393
|
+
metadata: {
|
|
3394
|
+
handlerId: "",
|
|
3395
|
+
parserUsed: "traditional",
|
|
3396
|
+
commandsUsed: [],
|
|
3397
|
+
optimizationsApplied: [],
|
|
3398
|
+
needsRuntime: true,
|
|
3399
|
+
runtimeHelpers: []
|
|
3400
|
+
}
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
const analysis = this.analyze(ast);
|
|
3404
|
+
const optimized = this.optimizer.optimize(ast, analysis, mergedOptions.optimizationLevel ?? 2);
|
|
3405
|
+
const handlerId = this.generateHandlerId(ast, code);
|
|
3406
|
+
const ctx = this.createCodegenContext(handlerId, analysis, mergedOptions);
|
|
3407
|
+
const generated = this.generateCode(optimized, ctx, analysis);
|
|
3408
|
+
const allHelpers = /* @__PURE__ */ new Set([...ctx.requiredHelpers, ...generated.imports]);
|
|
3409
|
+
if (optimized.type === "event") {
|
|
3410
|
+
const eventNode = optimized;
|
|
3411
|
+
const bodyLen = eventNode.body?.length ?? 0;
|
|
3412
|
+
if (bodyLen > 0 && generated.code) {
|
|
3413
|
+
const handlerMatch = /function\s+_handler_\w+\(_event\)\s*\{([\s\S]*)\}\s*$/.exec(
|
|
3414
|
+
generated.code
|
|
3415
|
+
);
|
|
3416
|
+
if (handlerMatch) {
|
|
3417
|
+
const bodyContent = handlerMatch[1].replace(/const _ctx\s*=\s*[^;]*;/g, "").replace(/const _el\s*=\s*[^;]*;/g, "").trim();
|
|
3418
|
+
if (!bodyContent) {
|
|
3419
|
+
analysis.warnings.push(
|
|
3420
|
+
`Event handler body appears empty despite ${bodyLen} parsed command(s). This may indicate a code generation failure for: "${code}"`
|
|
3421
|
+
);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
return {
|
|
3427
|
+
success: true,
|
|
3428
|
+
code: generated.code,
|
|
3429
|
+
warnings: analysis.warnings,
|
|
3430
|
+
metadata: {
|
|
3431
|
+
handlerId,
|
|
3432
|
+
parserUsed: mergedOptions.language !== "en" ? "semantic" : "traditional",
|
|
3433
|
+
language: mergedOptions.language,
|
|
3434
|
+
commandsUsed: Array.from(analysis.commandsUsed),
|
|
3435
|
+
optimizationsApplied: optimized._optimizations ?? [],
|
|
3436
|
+
needsRuntime: allHelpers.size > 0,
|
|
3437
|
+
runtimeHelpers: Array.from(allHelpers)
|
|
3438
|
+
}
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
/**
|
|
3442
|
+
* Compile a pre-parsed AST to JavaScript.
|
|
3443
|
+
* Skips parsing entirely — accepts an interchange-format ASTNode directly.
|
|
3444
|
+
* Use this when the AST has already been produced by a semantic parser,
|
|
3445
|
+
* explicit syntax parser, or other external source.
|
|
3446
|
+
*/
|
|
3447
|
+
compileAST(ast, options = {}) {
|
|
3448
|
+
const mergedOptions = { ...DEFAULT_COMPILE_OPTIONS, ...options };
|
|
3449
|
+
let normalized = ast;
|
|
3450
|
+
if (normalized.type !== "event") {
|
|
3451
|
+
normalized = {
|
|
3452
|
+
type: "event",
|
|
3453
|
+
event: "click",
|
|
3454
|
+
modifiers: {},
|
|
3455
|
+
body: [normalized]
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
const analysis = this.analyze(normalized);
|
|
3459
|
+
const optimized = this.optimizer.optimize(
|
|
3460
|
+
normalized,
|
|
3461
|
+
analysis,
|
|
3462
|
+
mergedOptions.optimizationLevel ?? 2
|
|
3463
|
+
);
|
|
3464
|
+
const sourceHint = JSON.stringify(ast).slice(0, 200);
|
|
3465
|
+
const handlerId = this.generateHandlerId(optimized, sourceHint);
|
|
3466
|
+
const ctx = this.createCodegenContext(handlerId, analysis, mergedOptions);
|
|
3467
|
+
const generated = this.generateCode(optimized, ctx, analysis);
|
|
3468
|
+
const allHelpers = /* @__PURE__ */ new Set([...ctx.requiredHelpers, ...generated.imports]);
|
|
3469
|
+
if (optimized.type === "event") {
|
|
3470
|
+
const eventNode = optimized;
|
|
3471
|
+
const bodyLen = eventNode.body?.length ?? 0;
|
|
3472
|
+
if (bodyLen > 0 && generated.code) {
|
|
3473
|
+
const handlerMatch = /function\s+_handler_\w+\(_event\)\s*\{([\s\S]*)\}\s*$/.exec(
|
|
3474
|
+
generated.code
|
|
3475
|
+
);
|
|
3476
|
+
if (handlerMatch) {
|
|
3477
|
+
const bodyContent = handlerMatch[1].replace(/const _ctx\s*=\s*[^;]*;/g, "").replace(/const _el\s*=\s*[^;]*;/g, "").trim();
|
|
3478
|
+
if (!bodyContent) {
|
|
3479
|
+
analysis.warnings.push(
|
|
3480
|
+
`Event handler body appears empty despite ${bodyLen} parsed command(s). This may indicate a code generation failure for pre-parsed AST.`
|
|
3481
|
+
);
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
return {
|
|
3487
|
+
success: true,
|
|
3488
|
+
code: generated.code,
|
|
3489
|
+
warnings: analysis.warnings,
|
|
3490
|
+
metadata: {
|
|
3491
|
+
handlerId,
|
|
3492
|
+
parserUsed: "traditional",
|
|
3493
|
+
// pre-parsed, no parser involved
|
|
3494
|
+
language: mergedOptions.language,
|
|
3495
|
+
commandsUsed: Array.from(analysis.commandsUsed),
|
|
3496
|
+
optimizationsApplied: optimized._optimizations ?? [],
|
|
3497
|
+
needsRuntime: allHelpers.size > 0,
|
|
3498
|
+
runtimeHelpers: Array.from(allHelpers)
|
|
3499
|
+
}
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
/**
|
|
3503
|
+
* Compile multiple extracted scripts.
|
|
3504
|
+
*/
|
|
3505
|
+
compile(scripts, options = {}) {
|
|
3506
|
+
const handlers = [];
|
|
3507
|
+
const fallbacks = [];
|
|
3508
|
+
const allImports = /* @__PURE__ */ new Set();
|
|
3509
|
+
for (const script of scripts) {
|
|
3510
|
+
const scriptOptions = {
|
|
3511
|
+
...options,
|
|
3512
|
+
language: script.language ?? options.language ?? "en"
|
|
3513
|
+
};
|
|
3514
|
+
const result = this.compileScript(script.code, scriptOptions);
|
|
3515
|
+
if (result.success && result.code) {
|
|
3516
|
+
handlers.push({
|
|
3517
|
+
id: result.metadata.handlerId,
|
|
3518
|
+
source: script.code,
|
|
3519
|
+
events: this.extractEvents(result.code),
|
|
3520
|
+
code: result.code,
|
|
3521
|
+
binding: {
|
|
3522
|
+
elementId: script.elementId,
|
|
3523
|
+
elementSelector: script.elementSelector
|
|
3524
|
+
}
|
|
3525
|
+
});
|
|
3526
|
+
for (const helper of result.metadata.runtimeHelpers) {
|
|
3527
|
+
allImports.add(helper);
|
|
3528
|
+
}
|
|
3529
|
+
} else {
|
|
3530
|
+
fallbacks.push({
|
|
3531
|
+
id: `fallback_${fallbacks.length}`,
|
|
3532
|
+
script: script.code,
|
|
3533
|
+
reason: result.errors?.join(", ") ?? "Unknown error",
|
|
3534
|
+
location: script.location
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
const combinedCode = this.generateCombinedCode(handlers, Array.from(allImports), options);
|
|
3539
|
+
return {
|
|
3540
|
+
handlers,
|
|
3541
|
+
code: combinedCode,
|
|
3542
|
+
fallbacks,
|
|
3543
|
+
stats: {
|
|
3544
|
+
total: scripts.length,
|
|
3545
|
+
compiled: handlers.length,
|
|
3546
|
+
fallbacks: fallbacks.length,
|
|
3547
|
+
totalSize: combinedCode.length,
|
|
3548
|
+
runtimeSize: this.estimateRuntimeSize(allImports)
|
|
3549
|
+
}
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
// ===========================================================================
|
|
3553
|
+
// CODE GENERATION
|
|
3554
|
+
// ===========================================================================
|
|
3555
|
+
/**
|
|
3556
|
+
* Generate JavaScript code from an optimized AST.
|
|
3557
|
+
*/
|
|
3558
|
+
generateCode(ast, ctx, analysis) {
|
|
3559
|
+
if (ast.type === "event") {
|
|
3560
|
+
const eventCodegen = new EventHandlerCodegen(ctx, analysis);
|
|
3561
|
+
const generated = eventCodegen.generate(ast);
|
|
3562
|
+
return {
|
|
3563
|
+
code: generated.handlerCode,
|
|
3564
|
+
imports: generated.imports
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
const exprCodegen = new ExpressionCodegen(ctx);
|
|
3568
|
+
return {
|
|
3569
|
+
code: exprCodegen.generate(ast),
|
|
3570
|
+
imports: Array.from(ctx.requiredHelpers)
|
|
3571
|
+
};
|
|
3572
|
+
}
|
|
3573
|
+
/**
|
|
3574
|
+
* Generate combined code for multiple handlers.
|
|
3575
|
+
*/
|
|
3576
|
+
generateCombinedCode(handlers, imports, options) {
|
|
3577
|
+
const codegenOptions = { ...DEFAULT_CODEGEN_OPTIONS, ...options.codegen };
|
|
3578
|
+
const lines = [];
|
|
3579
|
+
const allImports = [.../* @__PURE__ */ new Set([...imports, "ready"])];
|
|
3580
|
+
const importList = allImports.join(", ");
|
|
3581
|
+
if (codegenOptions.mode === "iife") {
|
|
3582
|
+
lines.push("(function() {");
|
|
3583
|
+
lines.push("var _ls = typeof lokascriptRuntime !== 'undefined' ? lokascriptRuntime : {};");
|
|
3584
|
+
lines.push("var _rt = { " + allImports.map((i) => `${i}: _ls.${i}`).join(", ") + " };");
|
|
3585
|
+
} else {
|
|
3586
|
+
if (codegenOptions.mode === "esm") {
|
|
3587
|
+
lines.push(`import { ${importList} } from '${codegenOptions.runtimeImport}';`);
|
|
3588
|
+
} else if (codegenOptions.mode === "cjs") {
|
|
3589
|
+
lines.push(`const { ${importList} } = require('${codegenOptions.runtimeImport}');`);
|
|
3590
|
+
}
|
|
3591
|
+
lines.push("const _rt = { " + allImports.map((i) => `${i}: ${i}`).join(", ") + " };");
|
|
3592
|
+
}
|
|
3593
|
+
lines.push("");
|
|
3594
|
+
for (const handler of handlers) {
|
|
3595
|
+
if (codegenOptions.preserveComments) {
|
|
3596
|
+
lines.push(`// Original: ${handler.source}`);
|
|
3597
|
+
}
|
|
3598
|
+
lines.push(handler.code);
|
|
3599
|
+
lines.push("");
|
|
3600
|
+
}
|
|
3601
|
+
lines.push("// Bind handlers to elements");
|
|
3602
|
+
lines.push("_rt.ready(() => {");
|
|
3603
|
+
for (const handler of handlers) {
|
|
3604
|
+
const selector = handler.binding.elementId ? `#${handler.binding.elementId}` : handler.binding.elementSelector ?? "[_]";
|
|
3605
|
+
for (const event of handler.events) {
|
|
3606
|
+
lines.push(` document.querySelectorAll('${selector}').forEach(_el => {`);
|
|
3607
|
+
lines.push(` _el.addEventListener('${event}', _handler_${handler.id});`);
|
|
3608
|
+
lines.push(" });");
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
lines.push("});");
|
|
3612
|
+
if (codegenOptions.mode === "iife") {
|
|
3613
|
+
lines.push("})();");
|
|
3614
|
+
}
|
|
3615
|
+
return lines.join("\n");
|
|
3616
|
+
}
|
|
3617
|
+
// ===========================================================================
|
|
3618
|
+
// HELPERS
|
|
3619
|
+
// ===========================================================================
|
|
3620
|
+
/**
|
|
3621
|
+
* Generate a unique handler ID.
|
|
3622
|
+
*/
|
|
3623
|
+
generateHandlerId(ast, code) {
|
|
3624
|
+
const event = ast.event ?? "handler";
|
|
3625
|
+
const body = ast.body ?? [];
|
|
3626
|
+
const firstCmd = body[0];
|
|
3627
|
+
const command = firstCmd?.name ?? "action";
|
|
3628
|
+
let hash = 5381;
|
|
3629
|
+
for (let i = 0; i < code.length; i++) {
|
|
3630
|
+
hash = (hash << 5) + hash ^ code.charCodeAt(i);
|
|
3631
|
+
}
|
|
3632
|
+
const hashStr = Math.abs(hash).toString(36).slice(0, 4);
|
|
3633
|
+
let id = `${event}_${command}_${hashStr}`;
|
|
3634
|
+
let suffix = 0;
|
|
3635
|
+
while (this.usedIds.has(id)) {
|
|
3636
|
+
id = `${event}_${command}_${hashStr}${suffix++}`;
|
|
3637
|
+
}
|
|
3638
|
+
this.usedIds.add(id);
|
|
3639
|
+
return id;
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Create a codegen context.
|
|
3643
|
+
*/
|
|
3644
|
+
createCodegenContext(handlerId, analysis, options) {
|
|
3645
|
+
const selectorCache = /* @__PURE__ */ new Map();
|
|
3646
|
+
const requiredHelpers = /* @__PURE__ */ new Set();
|
|
3647
|
+
let idCounter = 0;
|
|
3648
|
+
for (const info of analysis.expressions.selectors) {
|
|
3649
|
+
if (info.canCache && info.usages.length > 1) {
|
|
3650
|
+
const cacheKey = "_sel_" + sanitizeIdentifier(info.selector).slice(0, 20) + "_" + idCounter++;
|
|
3651
|
+
selectorCache.set(info.selector, cacheKey);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
const exprCodegenRef = { current: null };
|
|
3655
|
+
const ctx = {
|
|
3656
|
+
handlerId,
|
|
3657
|
+
generateId: (prefix = "_id") => `${prefix}_${idCounter++}`,
|
|
3658
|
+
generateExpression: (node) => {
|
|
3659
|
+
if (!exprCodegenRef.current) {
|
|
3660
|
+
exprCodegenRef.current = new ExpressionCodegen(ctx);
|
|
3661
|
+
}
|
|
3662
|
+
return exprCodegenRef.current.generate(node);
|
|
3663
|
+
},
|
|
3664
|
+
implicitTarget: "_ctx.me",
|
|
3665
|
+
localVarDeclarations: "",
|
|
3666
|
+
canCacheSelector: (selector) => selectorCache.has(selector),
|
|
3667
|
+
getCachedSelector: (selector) => selectorCache.get(selector) ?? `document.querySelector('${selector}')`,
|
|
3668
|
+
requireHelper: (name) => {
|
|
3669
|
+
requiredHelpers.add(name);
|
|
3670
|
+
},
|
|
3671
|
+
requiredHelpers,
|
|
3672
|
+
analysis,
|
|
3673
|
+
options: { ...DEFAULT_CODEGEN_OPTIONS, ...options.codegen }
|
|
3674
|
+
};
|
|
3675
|
+
return ctx;
|
|
3676
|
+
}
|
|
3677
|
+
/**
|
|
3678
|
+
* Extract event names from generated code.
|
|
3679
|
+
*/
|
|
3680
|
+
extractEvents(code) {
|
|
3681
|
+
const events = [];
|
|
3682
|
+
const match = /function\s+_handler_\w+\s*\(\s*_event\s*\)/.exec(code);
|
|
3683
|
+
if (match) {
|
|
3684
|
+
events.push("click");
|
|
3685
|
+
}
|
|
3686
|
+
return events.length > 0 ? events : ["click"];
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Estimate runtime size based on required helpers.
|
|
3690
|
+
*/
|
|
3691
|
+
estimateRuntimeSize(imports) {
|
|
3692
|
+
const helperSizes = {
|
|
3693
|
+
createContext: 200,
|
|
3694
|
+
HALT: 50,
|
|
3695
|
+
EXIT: 50,
|
|
3696
|
+
globals: 100,
|
|
3697
|
+
toggle: 150,
|
|
3698
|
+
toggleAttr: 100,
|
|
3699
|
+
addClass: 80,
|
|
3700
|
+
removeClass: 80,
|
|
3701
|
+
getProp: 120,
|
|
3702
|
+
setProp: 120,
|
|
3703
|
+
getValues: 180,
|
|
3704
|
+
contains: 150,
|
|
3705
|
+
matches: 50,
|
|
3706
|
+
debounce: 150,
|
|
3707
|
+
throttle: 150,
|
|
3708
|
+
wait: 80,
|
|
3709
|
+
send: 150,
|
|
3710
|
+
delegate: 200,
|
|
3711
|
+
fetchJSON: 150,
|
|
3712
|
+
fetchText: 150,
|
|
3713
|
+
fetchHTML: 180,
|
|
3714
|
+
ready: 100
|
|
3715
|
+
};
|
|
3716
|
+
let total = 500;
|
|
3717
|
+
for (const helper of imports) {
|
|
3718
|
+
total += helperSizes[helper] ?? 100;
|
|
3719
|
+
}
|
|
3720
|
+
return total;
|
|
3721
|
+
}
|
|
3722
|
+
};
|
|
3723
|
+
function createCompiler() {
|
|
3724
|
+
return new AOTCompiler();
|
|
3725
|
+
}
|
|
3726
|
+
async function compileHyperscript(code, options) {
|
|
3727
|
+
const compiler = new AOTCompiler();
|
|
3728
|
+
const result = compiler.compileScript(code, options);
|
|
3729
|
+
if (!result.success) {
|
|
3730
|
+
throw new Error(result.errors?.join(", ") ?? "Compilation failed");
|
|
3731
|
+
}
|
|
3732
|
+
return result.code ?? "";
|
|
3733
|
+
}
|
|
3734
|
+
async function createMultilingualCompiler() {
|
|
3735
|
+
const compiler = new AOTCompiler();
|
|
3736
|
+
try {
|
|
3737
|
+
const { createCoreParserAdapter: createCoreParserAdapter2 } = await import("./core-parser-adapter-VCJB52SO-MDHRIVZ7.js");
|
|
3738
|
+
compiler.setParser(await createCoreParserAdapter2());
|
|
3739
|
+
} catch {
|
|
3740
|
+
}
|
|
3741
|
+
try {
|
|
3742
|
+
const { createSemanticAdapter: createSemanticAdapter2 } = await import("./semantic-adapter-7HTMTO75-MFFBR7K3.js");
|
|
3743
|
+
compiler.setSemanticParser(await createSemanticAdapter2());
|
|
3744
|
+
} catch {
|
|
3745
|
+
}
|
|
3746
|
+
return compiler;
|
|
3747
|
+
}
|
|
3748
|
+
var VERSION = "1.0.0";
|
|
3749
|
+
export {
|
|
3750
|
+
AOTCompiler,
|
|
3751
|
+
Analyzer,
|
|
3752
|
+
ConstantFoldingPass,
|
|
3753
|
+
CoreParserAdapter,
|
|
3754
|
+
DeadCodeEliminationPass,
|
|
3755
|
+
EventHandlerCodegen,
|
|
3756
|
+
ExpressionCodegen,
|
|
3757
|
+
HTMLScanner,
|
|
3758
|
+
JSXScanner,
|
|
3759
|
+
LoopUnrollingPass,
|
|
3760
|
+
OptimizationPipeline,
|
|
3761
|
+
SelectorCachingPass,
|
|
3762
|
+
SemanticParserAdapter,
|
|
3763
|
+
SvelteScanner,
|
|
3764
|
+
VERSION,
|
|
3765
|
+
VueScanner,
|
|
3766
|
+
analyze,
|
|
3767
|
+
commandCodegens,
|
|
3768
|
+
compileHyperscript,
|
|
3769
|
+
createCompiler,
|
|
3770
|
+
createCoreParserAdapter,
|
|
3771
|
+
createMultilingualCompiler,
|
|
3772
|
+
createOptimizer,
|
|
3773
|
+
createScanner,
|
|
3774
|
+
createSemanticAdapter,
|
|
3775
|
+
generateBindings,
|
|
3776
|
+
generateCommand,
|
|
3777
|
+
generateEventHandler,
|
|
3778
|
+
generateExpression,
|
|
3779
|
+
generateForEach,
|
|
3780
|
+
generateIf,
|
|
3781
|
+
generateInitialization,
|
|
3782
|
+
generateRepeat,
|
|
3783
|
+
generateWhile,
|
|
3784
|
+
optimize,
|
|
3785
|
+
sanitizeClassName,
|
|
3786
|
+
sanitizeIdentifier,
|
|
3787
|
+
sanitizeSelector,
|
|
3788
|
+
scanFiles
|
|
3789
|
+
};
|
|
3790
|
+
//# sourceMappingURL=dist-ICUX26U7.js.map
|