olova 2.0.67 → 2.0.70
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/CHANGELOG.md +5 -5
- package/README.md +42 -42
- package/dist/compiler.d.ts +76 -1
- package/dist/compiler.js +566 -420
- package/dist/compiler.js.map +1 -1
- package/dist/core.d.ts +2 -2
- package/dist/core.js +400 -38
- package/dist/core.js.map +1 -1
- package/dist/global.d.ts +1 -1
- package/dist/global.js +54 -8
- package/dist/global.js.map +1 -1
- package/dist/index.js +534 -424
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +8 -3
- package/dist/runtime.js +512 -32
- package/dist/runtime.js.map +1 -1
- package/dist/{signals-core-BdfWh1Yt.d.ts → signals-core-BWZ5zXK5.d.ts} +6 -5
- package/dist/vite.js +534 -424
- package/dist/vite.js.map +1 -1
- package/package.json +86 -83
package/dist/compiler.js
CHANGED
|
@@ -3,8 +3,161 @@ import generatorModule from '@babel/generator';
|
|
|
3
3
|
import traverseModule from '@babel/traverse';
|
|
4
4
|
import * as t from '@babel/types';
|
|
5
5
|
import ts from 'typescript';
|
|
6
|
+
import * as csstree from 'css-tree';
|
|
7
|
+
import { parseDocument } from 'htmlparser2';
|
|
6
8
|
|
|
7
9
|
// compiler/compile.ts
|
|
10
|
+
function createScopeAttr(scopeId) {
|
|
11
|
+
return scopeId ? `data-o-scope-${scopeId}` : null;
|
|
12
|
+
}
|
|
13
|
+
function scopeSelectorList(selectorList, scopeAttr) {
|
|
14
|
+
const applyScopeToSegment = (segment) => {
|
|
15
|
+
const trimmed = segment.trim();
|
|
16
|
+
if (!trimmed) {
|
|
17
|
+
return trimmed;
|
|
18
|
+
}
|
|
19
|
+
if (trimmed.includes(scopeAttr)) {
|
|
20
|
+
return trimmed;
|
|
21
|
+
}
|
|
22
|
+
const globalPattern = /:global\(([^()]+)\)/g;
|
|
23
|
+
const localCandidate = trimmed.replace(globalPattern, "").trim();
|
|
24
|
+
const normalized = trimmed.replace(globalPattern, "$1");
|
|
25
|
+
if (!localCandidate) {
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
if (normalized === ":root") {
|
|
29
|
+
return `[${scopeAttr}]`;
|
|
30
|
+
}
|
|
31
|
+
const pseudoIndex = normalized.search(/(?<!:):(?!:)/);
|
|
32
|
+
if (pseudoIndex === -1) {
|
|
33
|
+
return `${normalized}[${scopeAttr}]`;
|
|
34
|
+
}
|
|
35
|
+
return `${normalized.slice(0, pseudoIndex)}[${scopeAttr}]${normalized.slice(pseudoIndex)}`;
|
|
36
|
+
};
|
|
37
|
+
return selectorList.split(",").map((selector) => selector.trim()).filter(Boolean).map((selector) => {
|
|
38
|
+
if (selector.startsWith("@") || selector.includes(scopeAttr)) {
|
|
39
|
+
return selector;
|
|
40
|
+
}
|
|
41
|
+
return selector.split(/(\s+|[>+~])/).map((part) => {
|
|
42
|
+
if (!part || /^(\s+|[>+~])$/.test(part)) {
|
|
43
|
+
return part;
|
|
44
|
+
}
|
|
45
|
+
return applyScopeToSegment(part);
|
|
46
|
+
}).join("");
|
|
47
|
+
}).join(", ");
|
|
48
|
+
}
|
|
49
|
+
function scopeCss(css, scopeAttr) {
|
|
50
|
+
const ast = csstree.parse(css, {
|
|
51
|
+
context: "stylesheet",
|
|
52
|
+
positions: false
|
|
53
|
+
});
|
|
54
|
+
csstree.walk(ast, {
|
|
55
|
+
visit: "Rule",
|
|
56
|
+
enter(node) {
|
|
57
|
+
const rule = node;
|
|
58
|
+
if (!rule.prelude || rule.prelude.type !== "SelectorList") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
rule.prelude = csstree.parse(
|
|
62
|
+
scopeSelectorList(csstree.generate(rule.prelude), scopeAttr),
|
|
63
|
+
{
|
|
64
|
+
context: "selectorList",
|
|
65
|
+
positions: false
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return csstree.generate(ast);
|
|
71
|
+
}
|
|
72
|
+
function hashScopeSeed(input) {
|
|
73
|
+
let hash = 2166136261;
|
|
74
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
75
|
+
hash ^= input.charCodeAt(index);
|
|
76
|
+
hash = Math.imul(hash, 16777619);
|
|
77
|
+
}
|
|
78
|
+
return Math.abs(hash >>> 0).toString(36);
|
|
79
|
+
}
|
|
80
|
+
function collectCss(blocks, scopeId) {
|
|
81
|
+
const scopeAttr = createScopeAttr(scopeId);
|
|
82
|
+
return blocks.map((block) => {
|
|
83
|
+
const content = block.content.trim();
|
|
84
|
+
if (!content) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
if (!("global" in block.attrs) && scopeAttr) {
|
|
88
|
+
return scopeCss(content, scopeAttr);
|
|
89
|
+
}
|
|
90
|
+
return content;
|
|
91
|
+
}).filter(Boolean).join("\n\n");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// compiler/diagnostics.ts
|
|
95
|
+
function indexToLocation(source, index) {
|
|
96
|
+
const safeIndex = Math.max(0, Math.min(index, source.length));
|
|
97
|
+
const lines = source.slice(0, safeIndex).split("\n");
|
|
98
|
+
return {
|
|
99
|
+
line: lines.length,
|
|
100
|
+
column: lines[lines.length - 1]?.length ?? 0,
|
|
101
|
+
index: safeIndex
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function createCodeFrame(source, line, column, contextLines = 2) {
|
|
105
|
+
const lines = source.split("\n");
|
|
106
|
+
const start = Math.max(0, line - 1 - contextLines);
|
|
107
|
+
const end = Math.min(lines.length, line + contextLines);
|
|
108
|
+
return lines.slice(start, end).map((content, offset) => {
|
|
109
|
+
const lineNumber = start + offset + 1;
|
|
110
|
+
const prefix = `${String(lineNumber).padStart(4, " ")} | `;
|
|
111
|
+
if (lineNumber !== line) {
|
|
112
|
+
return `${prefix}${content}`;
|
|
113
|
+
}
|
|
114
|
+
return `${prefix}${content}
|
|
115
|
+
| ${" ".repeat(Math.max(column, 0))}^`;
|
|
116
|
+
}).join("\n");
|
|
117
|
+
}
|
|
118
|
+
function createCompileError(message, source, index, options) {
|
|
119
|
+
const loc = indexToLocation(source, index);
|
|
120
|
+
const error = new Error(message);
|
|
121
|
+
error.loc = { start: loc };
|
|
122
|
+
error.frame = createCodeFrame(source, loc.line, loc.column);
|
|
123
|
+
error.phase = options?.phase;
|
|
124
|
+
error.hint = options?.hint;
|
|
125
|
+
error.diagnostic = {
|
|
126
|
+
message,
|
|
127
|
+
filename: options?.filename,
|
|
128
|
+
phase: options?.phase,
|
|
129
|
+
line: loc.line,
|
|
130
|
+
column: loc.column + 1,
|
|
131
|
+
index: loc.index,
|
|
132
|
+
frame: error.frame,
|
|
133
|
+
hint: options?.hint
|
|
134
|
+
};
|
|
135
|
+
return error;
|
|
136
|
+
}
|
|
137
|
+
function normalizeCompileError(error, source, filename = "component.olova") {
|
|
138
|
+
const compileError = error instanceof Error ? error : new Error(String(error));
|
|
139
|
+
const start = compileError.loc?.start ?? compileError.loc;
|
|
140
|
+
const line = start?.line;
|
|
141
|
+
const column = start?.column;
|
|
142
|
+
const phasePrefix = compileError.phase ? ` [${compileError.phase}]` : "";
|
|
143
|
+
const locationSuffix = line === void 0 || column === void 0 ? "" : ` (${filename}:${line}:${column + 1})`;
|
|
144
|
+
const normalizedMessage = compileError.message.replace(/^\[olova\]\s*/, "");
|
|
145
|
+
compileError.message = `[olova]${phasePrefix} ${normalizedMessage}${locationSuffix}`;
|
|
146
|
+
if (line !== void 0 && column !== void 0 && !compileError.frame) {
|
|
147
|
+
compileError.frame = createCodeFrame(source, line, column);
|
|
148
|
+
}
|
|
149
|
+
compileError.diagnostic = {
|
|
150
|
+
message: normalizedMessage,
|
|
151
|
+
filename,
|
|
152
|
+
phase: compileError.phase,
|
|
153
|
+
line,
|
|
154
|
+
column: column === void 0 ? void 0 : column + 1,
|
|
155
|
+
index: start?.index,
|
|
156
|
+
frame: compileError.frame,
|
|
157
|
+
hint: compileError.hint
|
|
158
|
+
};
|
|
159
|
+
throw compileError;
|
|
160
|
+
}
|
|
8
161
|
|
|
9
162
|
// compiler/parse.ts
|
|
10
163
|
function isWhitespace(char) {
|
|
@@ -177,17 +330,17 @@ function removeRanges(source, ranges) {
|
|
|
177
330
|
return output.trim();
|
|
178
331
|
}
|
|
179
332
|
function parseOlovaFile(source) {
|
|
180
|
-
const
|
|
333
|
+
const scriptBlocks = collectTopLevelBlocks(source, "script");
|
|
181
334
|
const styleBlocks = collectTopLevelBlocks(source, "style");
|
|
182
335
|
const ranges = styleBlocks.map((block) => ({
|
|
183
336
|
start: block.fullStart,
|
|
184
337
|
end: block.closeEnd
|
|
185
338
|
}));
|
|
186
|
-
|
|
339
|
+
for (const scriptBlock of scriptBlocks) {
|
|
187
340
|
ranges.push({ start: scriptBlock.fullStart, end: scriptBlock.closeEnd });
|
|
188
341
|
}
|
|
189
342
|
return {
|
|
190
|
-
script:
|
|
343
|
+
script: scriptBlocks.map((block) => block.inner.trim()).filter(Boolean).join("\n\n"),
|
|
191
344
|
template: removeRanges(source, ranges),
|
|
192
345
|
styles: styleBlocks.map((block) => ({
|
|
193
346
|
content: block.inner.trim(),
|
|
@@ -195,55 +348,30 @@ function parseOlovaFile(source) {
|
|
|
195
348
|
}))
|
|
196
349
|
};
|
|
197
350
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
if (char === ">") {
|
|
218
|
-
return index;
|
|
219
|
-
}
|
|
220
|
-
index += 1;
|
|
221
|
-
}
|
|
222
|
-
return -1;
|
|
223
|
-
}
|
|
224
|
-
function readQuotedValue(source, start, quote) {
|
|
225
|
-
let index = start + 1;
|
|
226
|
-
let value = "";
|
|
227
|
-
while (index < source.length) {
|
|
228
|
-
const char = source[index];
|
|
229
|
-
if (char === "\\" && index + 1 < source.length) {
|
|
230
|
-
value += source.slice(index, index + 2);
|
|
231
|
-
index += 2;
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
if (char === quote) {
|
|
235
|
-
return { value, end: index + 1 };
|
|
236
|
-
}
|
|
237
|
-
value += char;
|
|
238
|
-
index += 1;
|
|
239
|
-
}
|
|
240
|
-
return { value, end: source.length };
|
|
351
|
+
var VOID_TAGS = /* @__PURE__ */ new Set([
|
|
352
|
+
"area",
|
|
353
|
+
"base",
|
|
354
|
+
"br",
|
|
355
|
+
"col",
|
|
356
|
+
"embed",
|
|
357
|
+
"hr",
|
|
358
|
+
"img",
|
|
359
|
+
"input",
|
|
360
|
+
"link",
|
|
361
|
+
"meta",
|
|
362
|
+
"param",
|
|
363
|
+
"source",
|
|
364
|
+
"track",
|
|
365
|
+
"wbr"
|
|
366
|
+
]);
|
|
367
|
+
function normalizeNewlines(value) {
|
|
368
|
+
return value.replace(/\r\n?/g, "\n");
|
|
241
369
|
}
|
|
242
|
-
function
|
|
370
|
+
function readBracedValue(source, start) {
|
|
243
371
|
let index = start + 1;
|
|
244
372
|
let depth = 1;
|
|
245
|
-
let value = "{";
|
|
246
373
|
let quote = null;
|
|
374
|
+
let value = "{";
|
|
247
375
|
while (index < source.length && depth > 0) {
|
|
248
376
|
const char = source[index];
|
|
249
377
|
const prev = source[index - 1];
|
|
@@ -269,158 +397,106 @@ function readBraceValue(source, start) {
|
|
|
269
397
|
}
|
|
270
398
|
return { value, end: index };
|
|
271
399
|
}
|
|
272
|
-
function
|
|
273
|
-
const
|
|
400
|
+
function protectOlovaAttrExpressions(html) {
|
|
401
|
+
const placeholders = /* @__PURE__ */ new Map();
|
|
402
|
+
let output = "";
|
|
274
403
|
let index = 0;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (index
|
|
280
|
-
break;
|
|
281
|
-
}
|
|
282
|
-
let nameEnd = index;
|
|
283
|
-
while (nameEnd < source.length && !isWhitespace2(source[nameEnd]) && source[nameEnd] !== "=") {
|
|
284
|
-
nameEnd += 1;
|
|
285
|
-
}
|
|
286
|
-
const name = source.slice(index, nameEnd).trim();
|
|
287
|
-
index = nameEnd;
|
|
288
|
-
if (!name) {
|
|
404
|
+
let count = 0;
|
|
405
|
+
while (index < html.length) {
|
|
406
|
+
const char = html[index];
|
|
407
|
+
output += char;
|
|
408
|
+
if (char !== "=" || html[index + 1] !== "{") {
|
|
289
409
|
index += 1;
|
|
290
410
|
continue;
|
|
291
411
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
412
|
+
const braced = readBracedValue(html, index + 1);
|
|
413
|
+
const key = `__OLOVA_ATTR_EXPR_${count++}__`;
|
|
414
|
+
placeholders.set(key, braced.value);
|
|
415
|
+
output += `"${key}"`;
|
|
416
|
+
index = braced.end;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
return { html: output, placeholders };
|
|
420
|
+
}
|
|
421
|
+
function normalizeAttrs(attribs) {
|
|
422
|
+
const output = {};
|
|
423
|
+
for (const [name, value] of Object.entries(attribs)) {
|
|
424
|
+
output[name] = value === "" ? true : value;
|
|
425
|
+
}
|
|
426
|
+
return output;
|
|
427
|
+
}
|
|
428
|
+
function isSelfClosingElement(node) {
|
|
429
|
+
return VOID_TAGS.has(node.name.toLowerCase());
|
|
430
|
+
}
|
|
431
|
+
function toAstNode(node) {
|
|
432
|
+
if (node.type === "text") {
|
|
433
|
+
return {
|
|
434
|
+
type: "text",
|
|
435
|
+
content: normalizeNewlines(node.data)
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (node.type === "comment") {
|
|
439
|
+
return {
|
|
440
|
+
type: "comment",
|
|
441
|
+
content: normalizeNewlines(node.data)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (node.type === "tag" || node.type === "script" || node.type === "style") {
|
|
445
|
+
const element = node;
|
|
446
|
+
return {
|
|
447
|
+
type: "element",
|
|
448
|
+
tag: element.name,
|
|
449
|
+
attrs: normalizeAttrs(element.attribs ?? {}),
|
|
450
|
+
children: toAstNodes(element.children ?? []),
|
|
451
|
+
isSelfClosing: isSelfClosingElement(element)
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
function toAstNodes(nodes) {
|
|
457
|
+
const output = [];
|
|
458
|
+
for (const node of nodes) {
|
|
459
|
+
const normalized = toAstNode(node);
|
|
460
|
+
if (normalized) {
|
|
461
|
+
output.push(normalized);
|
|
323
462
|
}
|
|
324
|
-
attrs[name] = source.slice(start, index);
|
|
325
463
|
}
|
|
326
|
-
return
|
|
464
|
+
return output;
|
|
327
465
|
}
|
|
328
466
|
function parseHTML(html) {
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
stack[stack.length - 1].children.push({
|
|
340
|
-
type: "text",
|
|
341
|
-
content: html.slice(textStart)
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
break;
|
|
345
|
-
}
|
|
346
|
-
if (tagStart > textStart) {
|
|
347
|
-
stack[stack.length - 1].children.push({
|
|
348
|
-
type: "text",
|
|
349
|
-
content: html.slice(textStart, tagStart)
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
i = tagStart;
|
|
353
|
-
if (html.startsWith("<!--", i)) {
|
|
354
|
-
const commentEnd = html.indexOf("-->", i + 4);
|
|
355
|
-
if (commentEnd === -1) {
|
|
356
|
-
stack[stack.length - 1].children.push({
|
|
357
|
-
type: "comment",
|
|
358
|
-
content: html.slice(i + 4)
|
|
359
|
-
});
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
stack[stack.length - 1].children.push({
|
|
363
|
-
type: "comment",
|
|
364
|
-
content: html.slice(i + 4, commentEnd)
|
|
365
|
-
});
|
|
366
|
-
i = commentEnd + 3;
|
|
367
|
-
continue;
|
|
467
|
+
const protectedHtml = protectOlovaAttrExpressions(html);
|
|
468
|
+
const document = parseDocument(protectedHtml.html, {
|
|
469
|
+
decodeEntities: false,
|
|
470
|
+
recognizeSelfClosing: true,
|
|
471
|
+
lowerCaseAttributeNames: false,
|
|
472
|
+
lowerCaseTags: false
|
|
473
|
+
});
|
|
474
|
+
const rootNodes = document.children.filter((node) => {
|
|
475
|
+
if (node.type === "directive" || node.type === "cdata") {
|
|
476
|
+
return false;
|
|
368
477
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
478
|
+
return true;
|
|
479
|
+
});
|
|
480
|
+
const ast = toAstNodes(rootNodes);
|
|
481
|
+
const restorePlaceholders = (nodes) => {
|
|
482
|
+
for (const node of nodes) {
|
|
483
|
+
if (node.type !== "element") {
|
|
373
484
|
continue;
|
|
374
485
|
}
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
486
|
+
for (const [name, value] of Object.entries(node.attrs)) {
|
|
487
|
+
if (typeof value !== "string") {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const restored = protectedHtml.placeholders.get(value);
|
|
491
|
+
if (restored) {
|
|
492
|
+
node.attrs[name] = restored;
|
|
493
|
+
}
|
|
382
494
|
}
|
|
383
|
-
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
const tagEnd = findTagEnd2(html, i + 1);
|
|
387
|
-
if (tagEnd === -1) {
|
|
388
|
-
stack[stack.length - 1].children.push({ type: "text", content: "<" });
|
|
389
|
-
i++;
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
const rawTag = html.slice(i + 1, tagEnd).trim();
|
|
393
|
-
let tagNameEnd = 0;
|
|
394
|
-
while (tagNameEnd < rawTag.length && !isWhitespace2(rawTag[tagNameEnd]) && rawTag[tagNameEnd] !== "/") {
|
|
395
|
-
tagNameEnd += 1;
|
|
396
|
-
}
|
|
397
|
-
const tag = rawTag.slice(0, tagNameEnd);
|
|
398
|
-
if (!tag) {
|
|
399
|
-
stack[stack.length - 1].children.push({ type: "text", content: "<" });
|
|
400
|
-
i++;
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
const attrsStr = rawTag.slice(tagNameEnd).replace(/\/\s*$/, "").trim();
|
|
404
|
-
const selfClose = /\/\s*$/.test(rawTag);
|
|
405
|
-
const attrs = parseAttributes2(attrsStr);
|
|
406
|
-
const isNativeVoidTag = tag === tag.toLowerCase() && /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/.test(
|
|
407
|
-
tag
|
|
408
|
-
);
|
|
409
|
-
const isSelfClosing = !!selfClose || isNativeVoidTag;
|
|
410
|
-
const element = {
|
|
411
|
-
type: "element",
|
|
412
|
-
tag,
|
|
413
|
-
attrs,
|
|
414
|
-
children: [],
|
|
415
|
-
isSelfClosing
|
|
416
|
-
};
|
|
417
|
-
stack[stack.length - 1].children.push(element);
|
|
418
|
-
if (!isSelfClosing) {
|
|
419
|
-
stack.push({ node: element, children: element.children });
|
|
495
|
+
restorePlaceholders(node.children);
|
|
420
496
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return
|
|
497
|
+
};
|
|
498
|
+
restorePlaceholders(ast);
|
|
499
|
+
return ast;
|
|
424
500
|
}
|
|
425
501
|
|
|
426
502
|
// compiler/dom-generator.ts
|
|
@@ -478,7 +554,7 @@ function processNode(node, parentVar, stateNames, transformExpr, counters, build
|
|
|
478
554
|
const varName = `_el${buildCtx.localCount++}`;
|
|
479
555
|
const useSvgNamespace = isSvgContext(tag, inSvg);
|
|
480
556
|
buildCtx.statements.push(
|
|
481
|
-
useSvgNamespace ? `const ${varName} = document.createElementNS(${JSON.stringify(SVG_NAMESPACE)},
|
|
557
|
+
useSvgNamespace ? `const ${varName} = document.createElementNS(${JSON.stringify(SVG_NAMESPACE)}, ${JSON.stringify(tag)});` : `const ${varName} = document.createElement(${JSON.stringify(tag)});`
|
|
482
558
|
);
|
|
483
559
|
for (const [attr, val] of Object.entries(node.attrs)) {
|
|
484
560
|
if (attr.startsWith("data-o-on-")) {
|
|
@@ -492,10 +568,12 @@ function processNode(node, parentVar, stateNames, transformExpr, counters, build
|
|
|
492
568
|
}
|
|
493
569
|
} else {
|
|
494
570
|
if (val === true) {
|
|
495
|
-
buildCtx.statements.push(
|
|
571
|
+
buildCtx.statements.push(
|
|
572
|
+
`${varName}.setAttribute(${JSON.stringify(attr)}, "");`
|
|
573
|
+
);
|
|
496
574
|
} else {
|
|
497
575
|
buildCtx.statements.push(
|
|
498
|
-
`${varName}.setAttribute(
|
|
576
|
+
`${varName}.setAttribute(${JSON.stringify(attr)}, ${JSON.stringify(val)});`
|
|
499
577
|
);
|
|
500
578
|
}
|
|
501
579
|
}
|
|
@@ -541,12 +619,14 @@ function generateBuildFunction(html, stateNames, transformExpr, counters, compon
|
|
|
541
619
|
}
|
|
542
620
|
|
|
543
621
|
// compiler/compile.ts
|
|
544
|
-
var
|
|
622
|
+
var generate2 = generatorModule.default ?? generatorModule;
|
|
545
623
|
var traverse = traverseModule.default ?? traverseModule;
|
|
546
624
|
var CORE_KNOWN_IMPORTS = /* @__PURE__ */ new Set([
|
|
547
625
|
"Signal",
|
|
548
626
|
"computed",
|
|
549
627
|
"createApp",
|
|
628
|
+
"createErrorBoundary",
|
|
629
|
+
"dangerouslySetHtml",
|
|
550
630
|
"defineComponent",
|
|
551
631
|
"derived",
|
|
552
632
|
"effect",
|
|
@@ -554,53 +634,20 @@ var CORE_KNOWN_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
554
634
|
"global",
|
|
555
635
|
"hasContext",
|
|
556
636
|
"mount",
|
|
637
|
+
"onCleanup",
|
|
638
|
+
"onMount",
|
|
639
|
+
"replaceComponent",
|
|
557
640
|
"setContext",
|
|
558
|
-
"state"
|
|
641
|
+
"state",
|
|
642
|
+
"Suspense"
|
|
559
643
|
]);
|
|
560
644
|
function escapeTemplate(source) {
|
|
561
645
|
return source.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
|
|
562
646
|
}
|
|
563
|
-
function
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
return {
|
|
567
|
-
line: lines.length,
|
|
568
|
-
column: lines[lines.length - 1]?.length ?? 0,
|
|
569
|
-
index: safeIndex
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
function createCodeFrame(source, line, column, contextLines = 2) {
|
|
573
|
-
const lines = source.split("\n");
|
|
574
|
-
const start = Math.max(0, line - 1 - contextLines);
|
|
575
|
-
const end = Math.min(lines.length, line + contextLines);
|
|
576
|
-
return lines.slice(start, end).map((content, offset) => {
|
|
577
|
-
const lineNumber = start + offset + 1;
|
|
578
|
-
const prefix = `${String(lineNumber).padStart(4, " ")} | `;
|
|
579
|
-
if (lineNumber !== line) {
|
|
580
|
-
return `${prefix}${content}`;
|
|
581
|
-
}
|
|
582
|
-
return `${prefix}${content}
|
|
583
|
-
| ${" ".repeat(Math.max(column, 0))}^`;
|
|
584
|
-
}).join("\n");
|
|
585
|
-
}
|
|
586
|
-
function createCompileError(message, source, index) {
|
|
587
|
-
const loc = indexToLocation(source, index);
|
|
588
|
-
const error = new Error(message);
|
|
589
|
-
error.loc = { start: loc };
|
|
590
|
-
error.frame = createCodeFrame(source, loc.line, loc.column);
|
|
591
|
-
return error;
|
|
592
|
-
}
|
|
593
|
-
function normalizeCompileError(error, source, filename = "component.olova") {
|
|
594
|
-
const compileError = error instanceof Error ? error : new Error(String(error));
|
|
595
|
-
const start = compileError.loc?.start ?? compileError.loc;
|
|
596
|
-
const line = start?.line;
|
|
597
|
-
const column = start?.column;
|
|
598
|
-
const locationSuffix = line === void 0 || column === void 0 ? "" : ` (${filename}:${line}:${column + 1})`;
|
|
599
|
-
compileError.message = `[olova] ${compileError.message.replace(/^\[olova\]\s*/, "")}${locationSuffix}`;
|
|
600
|
-
if (line !== void 0 && column !== void 0 && !compileError.frame) {
|
|
601
|
-
compileError.frame = createCodeFrame(source, line, column);
|
|
647
|
+
function emitDevWarnings(warnings) {
|
|
648
|
+
for (const warning of warnings) {
|
|
649
|
+
console.warn(warning);
|
|
602
650
|
}
|
|
603
|
-
throw compileError;
|
|
604
651
|
}
|
|
605
652
|
function parseModule(code) {
|
|
606
653
|
return parse(code, {
|
|
@@ -608,6 +655,17 @@ function parseModule(code) {
|
|
|
608
655
|
plugins: ["typescript", "jsx"]
|
|
609
656
|
});
|
|
610
657
|
}
|
|
658
|
+
function runCompilePhase(phase, fn) {
|
|
659
|
+
try {
|
|
660
|
+
return fn();
|
|
661
|
+
} catch (error) {
|
|
662
|
+
const compileError = error instanceof Error ? error : new Error(String(error));
|
|
663
|
+
if (!compileError.phase) {
|
|
664
|
+
compileError.phase = phase;
|
|
665
|
+
}
|
|
666
|
+
throw compileError;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
611
669
|
function transpileTypeScript(code, filename = "component.ts") {
|
|
612
670
|
const result = ts.transpileModule(code, {
|
|
613
671
|
fileName: filename,
|
|
@@ -627,20 +685,22 @@ function transpileTypeScript(code, filename = "component.ts") {
|
|
|
627
685
|
};
|
|
628
686
|
}
|
|
629
687
|
function compileModuleScript(script, filename = "module.olova.ts") {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
688
|
+
return runCompilePhase("analyze-script", () => {
|
|
689
|
+
const scriptInfo = collectScriptInfo(script);
|
|
690
|
+
return runCompilePhase(
|
|
691
|
+
"transpile",
|
|
692
|
+
() => transpileTypeScript(
|
|
693
|
+
[scriptInfo.importsCode, scriptInfo.scriptBodyCode].filter(Boolean).join("\n"),
|
|
694
|
+
filename
|
|
695
|
+
)
|
|
696
|
+
);
|
|
697
|
+
});
|
|
635
698
|
}
|
|
636
699
|
function isModuleOnlyScript(script) {
|
|
637
700
|
if (!script.trim()) {
|
|
638
701
|
return false;
|
|
639
702
|
}
|
|
640
|
-
|
|
641
|
-
return ast.program.body.every(
|
|
642
|
-
(node) => t.isImportDeclaration(node) || t.isExportNamedDeclaration(node) || t.isExportAllDeclaration(node) || t.isExportDefaultDeclaration(node)
|
|
643
|
-
);
|
|
703
|
+
return true;
|
|
644
704
|
}
|
|
645
705
|
function isStateInit(node) {
|
|
646
706
|
return !!(node && t.isCallExpression(node) && (t.isIdentifier(node.callee) && node.callee.name === "state" || t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object) && node.callee.object.name === "__olovaGlobal" && t.isIdentifier(node.callee.property) && (node.callee.property.name === "state" || node.callee.property.name === "get")));
|
|
@@ -966,7 +1026,7 @@ function collectScriptInfo(script) {
|
|
|
966
1026
|
return false;
|
|
967
1027
|
}
|
|
968
1028
|
let found = false;
|
|
969
|
-
traverse(parseModule(`(${
|
|
1029
|
+
traverse(parseModule(`(${generate2(node).code})`), {
|
|
970
1030
|
JSXElement() {
|
|
971
1031
|
found = true;
|
|
972
1032
|
},
|
|
@@ -991,7 +1051,7 @@ function collectScriptInfo(script) {
|
|
|
991
1051
|
traverse(ast, {
|
|
992
1052
|
JSXElement(path) {
|
|
993
1053
|
const transformed = transformExpression(
|
|
994
|
-
|
|
1054
|
+
generate2(path.node).code,
|
|
995
1055
|
stateNames,
|
|
996
1056
|
allSignalNames,
|
|
997
1057
|
jsxFunctionNames
|
|
@@ -1005,7 +1065,7 @@ function collectScriptInfo(script) {
|
|
|
1005
1065
|
},
|
|
1006
1066
|
JSXFragment(path) {
|
|
1007
1067
|
const transformed = transformExpression(
|
|
1008
|
-
|
|
1068
|
+
generate2(path.node).code,
|
|
1009
1069
|
stateNames,
|
|
1010
1070
|
allSignalNames,
|
|
1011
1071
|
jsxFunctionNames
|
|
@@ -1037,8 +1097,8 @@ function collectScriptInfo(script) {
|
|
|
1037
1097
|
const bodyNodes = ast.program.body.filter(
|
|
1038
1098
|
(node) => !t.isImportDeclaration(node)
|
|
1039
1099
|
);
|
|
1040
|
-
const importsCode = importNodes.map((node) =>
|
|
1041
|
-
const scriptBodyCode = bodyNodes.map((node) =>
|
|
1100
|
+
const importsCode = importNodes.map((node) => generate2(node).code).join("\n");
|
|
1101
|
+
const scriptBodyCode = bodyNodes.map((node) => generate2(node).code).join("\n");
|
|
1042
1102
|
return {
|
|
1043
1103
|
importsCode,
|
|
1044
1104
|
scriptBodyCode,
|
|
@@ -1112,7 +1172,7 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
|
|
|
1112
1172
|
const escapeCall = (input) => t.callExpression(t.identifier("__olovaEscape"), [input]);
|
|
1113
1173
|
const expressionContainsJsx = (input) => {
|
|
1114
1174
|
let found = false;
|
|
1115
|
-
traverse(parseModule(`(${
|
|
1175
|
+
traverse(parseModule(`(${generate2(input).code})`), {
|
|
1116
1176
|
JSXElement() {
|
|
1117
1177
|
found = true;
|
|
1118
1178
|
},
|
|
@@ -1130,7 +1190,7 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
|
|
|
1130
1190
|
return true;
|
|
1131
1191
|
}
|
|
1132
1192
|
let found = false;
|
|
1133
|
-
traverse(parseModule(`(${
|
|
1193
|
+
traverse(parseModule(`(${generate2(input).code})`), {
|
|
1134
1194
|
CallExpression(path) {
|
|
1135
1195
|
if (t.isIdentifier(path.node.callee) && jsxFunctionNames.has(path.node.callee.name)) {
|
|
1136
1196
|
found = true;
|
|
@@ -1185,7 +1245,7 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
|
|
|
1185
1245
|
return walkMember(name);
|
|
1186
1246
|
};
|
|
1187
1247
|
const transformEmbeddedJsxExpression = (input) => {
|
|
1188
|
-
const wrapped = parseModule(`(${
|
|
1248
|
+
const wrapped = parseModule(`(${generate2(input).code})`);
|
|
1189
1249
|
let containsNestedJsx = false;
|
|
1190
1250
|
traverse(wrapped, {
|
|
1191
1251
|
JSXElement(path) {
|
|
@@ -1372,7 +1432,7 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
|
|
|
1372
1432
|
return { code: expr.trim(), containsJsx };
|
|
1373
1433
|
}
|
|
1374
1434
|
containsJsx = containsJsx || expressionProducesHtml(statement.expression);
|
|
1375
|
-
return { code:
|
|
1435
|
+
return { code: generate2(statement.expression).code, containsJsx };
|
|
1376
1436
|
}
|
|
1377
1437
|
function jsxToTemplateHtml(node) {
|
|
1378
1438
|
if (t.isJSXElement(node)) {
|
|
@@ -1381,15 +1441,15 @@ function jsxToTemplateHtml(node) {
|
|
|
1381
1441
|
if (t.isJSXIdentifier(opening.name)) {
|
|
1382
1442
|
tag = opening.name.name;
|
|
1383
1443
|
} else if (t.isJSXMemberExpression(opening.name)) {
|
|
1384
|
-
const
|
|
1385
|
-
tag =
|
|
1444
|
+
const walk2 = (n) => t.isJSXIdentifier(n) ? n.name : `${walk2(n.object)}.${n.property.name}`;
|
|
1445
|
+
tag = walk2(opening.name);
|
|
1386
1446
|
} else if (t.isJSXNamespacedName(opening.name)) {
|
|
1387
1447
|
tag = `${opening.name.namespace.name}:${opening.name.name.name}`;
|
|
1388
1448
|
}
|
|
1389
1449
|
let attrsStr = "";
|
|
1390
1450
|
for (const attr of opening.attributes) {
|
|
1391
1451
|
if (t.isJSXSpreadAttribute(attr)) {
|
|
1392
|
-
attrsStr += ` {...${
|
|
1452
|
+
attrsStr += ` {...${generate2(attr.argument).code}}`;
|
|
1393
1453
|
} else {
|
|
1394
1454
|
let attrName = "";
|
|
1395
1455
|
if (t.isJSXIdentifier(attr.name)) attrName = attr.name.name;
|
|
@@ -1399,7 +1459,7 @@ function jsxToTemplateHtml(node) {
|
|
|
1399
1459
|
attrsStr += ` ${attrName}="${attr.value.value}"`;
|
|
1400
1460
|
else if (t.isJSXExpressionContainer(attr.value)) {
|
|
1401
1461
|
if (!t.isJSXEmptyExpression(attr.value.expression)) {
|
|
1402
|
-
attrsStr += ` ${attrName}={${
|
|
1462
|
+
attrsStr += ` ${attrName}={${generate2(attr.value.expression).code}}`;
|
|
1403
1463
|
}
|
|
1404
1464
|
}
|
|
1405
1465
|
}
|
|
@@ -1415,7 +1475,7 @@ function jsxToTemplateHtml(node) {
|
|
|
1415
1475
|
}
|
|
1416
1476
|
if (t.isJSXExpressionContainer(node)) {
|
|
1417
1477
|
if (t.isJSXEmptyExpression(node.expression)) return "";
|
|
1418
|
-
return `{${
|
|
1478
|
+
return `{${generate2(node.expression).code}}`;
|
|
1419
1479
|
}
|
|
1420
1480
|
if (t.isJSXFragment(node)) {
|
|
1421
1481
|
return node.children.map(jsxToTemplateHtml).join("");
|
|
@@ -1425,56 +1485,78 @@ function jsxToTemplateHtml(node) {
|
|
|
1425
1485
|
function replaceTemplateExpressions(input, replacer) {
|
|
1426
1486
|
let output = "";
|
|
1427
1487
|
let index = 0;
|
|
1488
|
+
let inTag = false;
|
|
1489
|
+
let tagQuote = null;
|
|
1490
|
+
let tagBraceDepth = 0;
|
|
1428
1491
|
while (index < input.length) {
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1492
|
+
const char = input[index];
|
|
1493
|
+
const prev = index > 0 ? input[index - 1] : "";
|
|
1494
|
+
if (inTag) {
|
|
1495
|
+
output += char;
|
|
1496
|
+
if (tagQuote) {
|
|
1497
|
+
if (char === tagQuote && prev !== "\\") {
|
|
1498
|
+
tagQuote = null;
|
|
1499
|
+
}
|
|
1500
|
+
index += 1;
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
1504
|
+
tagQuote = char;
|
|
1505
|
+
} else if (char === "{") {
|
|
1506
|
+
tagBraceDepth += 1;
|
|
1507
|
+
} else if (char === "}" && tagBraceDepth > 0) {
|
|
1508
|
+
tagBraceDepth -= 1;
|
|
1509
|
+
} else if (char === ">" && tagBraceDepth === 0) {
|
|
1510
|
+
inTag = false;
|
|
1511
|
+
}
|
|
1512
|
+
index += 1;
|
|
1513
|
+
continue;
|
|
1433
1514
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1515
|
+
if (char === "<") {
|
|
1516
|
+
inTag = true;
|
|
1517
|
+
output += char;
|
|
1518
|
+
index += 1;
|
|
1519
|
+
continue;
|
|
1437
1520
|
}
|
|
1438
|
-
if (
|
|
1439
|
-
output +=
|
|
1440
|
-
index
|
|
1521
|
+
if (char !== "{") {
|
|
1522
|
+
output += char;
|
|
1523
|
+
index += 1;
|
|
1441
1524
|
continue;
|
|
1442
1525
|
}
|
|
1443
|
-
if (
|
|
1444
|
-
output +=
|
|
1445
|
-
index
|
|
1526
|
+
if (prev === "$") {
|
|
1527
|
+
output += char;
|
|
1528
|
+
index += 1;
|
|
1446
1529
|
continue;
|
|
1447
1530
|
}
|
|
1448
|
-
output += input.slice(index, start);
|
|
1449
1531
|
let depth = 1;
|
|
1450
|
-
let cursor =
|
|
1532
|
+
let cursor = index + 1;
|
|
1451
1533
|
let quote = null;
|
|
1452
1534
|
while (cursor < input.length && depth > 0) {
|
|
1453
|
-
const
|
|
1454
|
-
const
|
|
1535
|
+
const char2 = input[cursor];
|
|
1536
|
+
const prev2 = input[cursor - 1];
|
|
1455
1537
|
if (quote) {
|
|
1456
|
-
if (
|
|
1538
|
+
if (char2 === quote && prev2 !== "\\") {
|
|
1457
1539
|
quote = null;
|
|
1458
1540
|
}
|
|
1459
1541
|
cursor += 1;
|
|
1460
1542
|
continue;
|
|
1461
1543
|
}
|
|
1462
|
-
if (
|
|
1463
|
-
quote =
|
|
1544
|
+
if (char2 === "'" || char2 === '"' || char2 === "`") {
|
|
1545
|
+
quote = char2;
|
|
1464
1546
|
cursor += 1;
|
|
1465
1547
|
continue;
|
|
1466
1548
|
}
|
|
1467
|
-
if (
|
|
1549
|
+
if (char2 === "{") {
|
|
1468
1550
|
depth += 1;
|
|
1469
|
-
} else if (
|
|
1551
|
+
} else if (char2 === "}") {
|
|
1470
1552
|
depth -= 1;
|
|
1471
1553
|
}
|
|
1472
1554
|
cursor += 1;
|
|
1473
1555
|
}
|
|
1474
1556
|
if (depth !== 0) {
|
|
1475
|
-
throw createCompileError("[olova] Unclosed template expression block.", input,
|
|
1557
|
+
throw createCompileError("[olova] Unclosed template expression block.", input, index);
|
|
1476
1558
|
}
|
|
1477
|
-
const expression = input.slice(
|
|
1559
|
+
const expression = input.slice(index + 1, cursor - 1);
|
|
1478
1560
|
output += replacer(expression);
|
|
1479
1561
|
index = cursor;
|
|
1480
1562
|
}
|
|
@@ -1508,7 +1590,7 @@ function parsePropsFromAttrs(attrs, stateNames, allSignalNames, jsxFunctionNames
|
|
|
1508
1590
|
}
|
|
1509
1591
|
return props.length === 0 ? "{}" : `{ ${props.join(", ")} }`;
|
|
1510
1592
|
}
|
|
1511
|
-
function transformTemplate(template, stateNames, allSignalNames, jsxFunctionNames, componentNames, counters, scopeId) {
|
|
1593
|
+
function transformTemplate(template, stateNames, allSignalNames, jsxFunctionNames, componentNames, counters, scopeId, devWarnings, filename = "component.olova") {
|
|
1512
1594
|
let html = template;
|
|
1513
1595
|
const textBindings = [];
|
|
1514
1596
|
const htmlBindings = [];
|
|
@@ -1525,7 +1607,7 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1525
1607
|
const node = exprNode.expression;
|
|
1526
1608
|
const containsJsxNode = (n) => {
|
|
1527
1609
|
let found = false;
|
|
1528
|
-
traverse(parseModule(`(${
|
|
1610
|
+
traverse(parseModule(`(${generate2(n).code})`), {
|
|
1529
1611
|
JSXElement() {
|
|
1530
1612
|
found = true;
|
|
1531
1613
|
},
|
|
@@ -1538,7 +1620,7 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1538
1620
|
if (t.isLogicalExpression(node) && node.operator === "&&") {
|
|
1539
1621
|
if (containsJsxNode(node.right)) {
|
|
1540
1622
|
const id = `i${counters.if++}`;
|
|
1541
|
-
const condition =
|
|
1623
|
+
const condition = generate2(node.left).code;
|
|
1542
1624
|
const trueBranchHtml = jsxToTemplateHtml(node.right);
|
|
1543
1625
|
ifBindings.push({
|
|
1544
1626
|
id,
|
|
@@ -1556,7 +1638,9 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1556
1638
|
jsxFunctionNames,
|
|
1557
1639
|
componentNames,
|
|
1558
1640
|
counters,
|
|
1559
|
-
scopeId
|
|
1641
|
+
scopeId,
|
|
1642
|
+
devWarnings,
|
|
1643
|
+
filename
|
|
1560
1644
|
)
|
|
1561
1645
|
});
|
|
1562
1646
|
return `__O_IF_${id}__`;
|
|
@@ -1564,9 +1648,9 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1564
1648
|
} else if (t.isConditionalExpression(node)) {
|
|
1565
1649
|
if (containsJsxNode(node.consequent) || containsJsxNode(node.alternate)) {
|
|
1566
1650
|
const id = `i${counters.if++}`;
|
|
1567
|
-
const condition =
|
|
1568
|
-
const trueBranchHtml = containsJsxNode(node.consequent) ? jsxToTemplateHtml(node.consequent) : `{${
|
|
1569
|
-
const falseBranchHtml = containsJsxNode(node.alternate) ? jsxToTemplateHtml(node.alternate) : `{${
|
|
1651
|
+
const condition = generate2(node.test).code;
|
|
1652
|
+
const trueBranchHtml = containsJsxNode(node.consequent) ? jsxToTemplateHtml(node.consequent) : `{${generate2(node.consequent).code}}`;
|
|
1653
|
+
const falseBranchHtml = containsJsxNode(node.alternate) ? jsxToTemplateHtml(node.alternate) : `{${generate2(node.alternate).code}}`;
|
|
1570
1654
|
ifBindings.push({
|
|
1571
1655
|
id,
|
|
1572
1656
|
expr: transformExpression(
|
|
@@ -1583,7 +1667,9 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1583
1667
|
jsxFunctionNames,
|
|
1584
1668
|
componentNames,
|
|
1585
1669
|
counters,
|
|
1586
|
-
scopeId
|
|
1670
|
+
scopeId,
|
|
1671
|
+
devWarnings,
|
|
1672
|
+
filename
|
|
1587
1673
|
),
|
|
1588
1674
|
falseBranch: transformTemplate(
|
|
1589
1675
|
falseBranchHtml,
|
|
@@ -1592,7 +1678,9 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1592
1678
|
jsxFunctionNames,
|
|
1593
1679
|
componentNames,
|
|
1594
1680
|
counters,
|
|
1595
|
-
scopeId
|
|
1681
|
+
scopeId,
|
|
1682
|
+
devWarnings,
|
|
1683
|
+
filename
|
|
1596
1684
|
)
|
|
1597
1685
|
});
|
|
1598
1686
|
return `__O_IF_${id}__`;
|
|
@@ -1647,6 +1735,7 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1647
1735
|
continue;
|
|
1648
1736
|
}
|
|
1649
1737
|
const normalizedAttrName = attrName === "className" ? "class" : attrName;
|
|
1738
|
+
const lowerAttrName = normalizedAttrName.toLowerCase();
|
|
1650
1739
|
if (rawValue === true) {
|
|
1651
1740
|
output += ` ${normalizedAttrName}`;
|
|
1652
1741
|
continue;
|
|
@@ -1679,6 +1768,16 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1679
1768
|
output += ` ${normalizedAttrName}="__O_ATTR_${id}__"`;
|
|
1680
1769
|
continue;
|
|
1681
1770
|
}
|
|
1771
|
+
if (devWarnings && lowerAttrName.startsWith("on")) {
|
|
1772
|
+
devWarnings.add(
|
|
1773
|
+
`[olova] Avoid static inline event attributes like "${normalizedAttrName}" in templates (${filename}). Prefer ${normalizedAttrName}={handler}.`
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
if (devWarnings && (lowerAttrName === "href" || lowerAttrName === "src" || lowerAttrName === "xlink:href") && /^\s*javascript:/i.test(rawValue)) {
|
|
1777
|
+
devWarnings.add(
|
|
1778
|
+
`[olova] Suspicious javascript: URL found in static "${normalizedAttrName}" attribute (${filename}).`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1682
1781
|
output += ` ${normalizedAttrName}="${rawValue.replace(/"/g, """)}"`;
|
|
1683
1782
|
}
|
|
1684
1783
|
return output;
|
|
@@ -1727,6 +1826,73 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1727
1826
|
defaultNodes.push(child);
|
|
1728
1827
|
}
|
|
1729
1828
|
const slotFactories = [];
|
|
1829
|
+
if (node.tag === "Suspense" && typeof node.attrs.fallback === "string") {
|
|
1830
|
+
const rawValue = node.attrs.fallback;
|
|
1831
|
+
if (rawValue.startsWith("{") && rawValue.endsWith("}")) {
|
|
1832
|
+
const fallbackExpression = rawValue.slice(1, -1).trim();
|
|
1833
|
+
const fallbackAst = parseModule(`(${fallbackExpression})`);
|
|
1834
|
+
const fallbackStatement = fallbackAst.program.body[0];
|
|
1835
|
+
const fallbackNode = fallbackStatement && t.isExpressionStatement(fallbackStatement) ? fallbackStatement.expression : null;
|
|
1836
|
+
if (fallbackNode && (t.isJSXElement(fallbackNode) || t.isJSXFragment(fallbackNode))) {
|
|
1837
|
+
slotFactories.push({
|
|
1838
|
+
varName: `__slot_${id}_fallback`,
|
|
1839
|
+
name: "fallback",
|
|
1840
|
+
descriptor: transformTemplate(
|
|
1841
|
+
jsxToTemplateHtml(fallbackNode),
|
|
1842
|
+
stateNames,
|
|
1843
|
+
allSignalNames,
|
|
1844
|
+
jsxFunctionNames,
|
|
1845
|
+
componentNames,
|
|
1846
|
+
counters,
|
|
1847
|
+
scopeId,
|
|
1848
|
+
devWarnings,
|
|
1849
|
+
filename
|
|
1850
|
+
)
|
|
1851
|
+
});
|
|
1852
|
+
} else {
|
|
1853
|
+
const transformedFallback = transformExpression(
|
|
1854
|
+
fallbackExpression,
|
|
1855
|
+
stateNames,
|
|
1856
|
+
allSignalNames,
|
|
1857
|
+
jsxFunctionNames,
|
|
1858
|
+
scopeId
|
|
1859
|
+
);
|
|
1860
|
+
const fallbackBindingId = `fallback_${id}`;
|
|
1861
|
+
slotFactories.push({
|
|
1862
|
+
varName: `__slot_${id}_fallback`,
|
|
1863
|
+
name: "fallback",
|
|
1864
|
+
descriptor: {
|
|
1865
|
+
html: `__O_HTML_${fallbackBindingId}__`,
|
|
1866
|
+
textBindings: [],
|
|
1867
|
+
htmlBindings: [
|
|
1868
|
+
{
|
|
1869
|
+
id: fallbackBindingId,
|
|
1870
|
+
expr: transformedFallback.containsJsx ? `__olovaDangerouslySetHtml(${transformedFallback.code})` : transformedFallback.code
|
|
1871
|
+
}
|
|
1872
|
+
],
|
|
1873
|
+
attrBindings: [],
|
|
1874
|
+
eventBindings: [],
|
|
1875
|
+
slotBindings: [],
|
|
1876
|
+
componentBindings: [],
|
|
1877
|
+
ifBindings: [],
|
|
1878
|
+
buildFnStr: generateBuildFunction(
|
|
1879
|
+
`__O_HTML_${fallbackBindingId}__`,
|
|
1880
|
+
stateNames,
|
|
1881
|
+
(expr) => transformExpression(
|
|
1882
|
+
expr,
|
|
1883
|
+
stateNames,
|
|
1884
|
+
allSignalNames,
|
|
1885
|
+
jsxFunctionNames,
|
|
1886
|
+
scopeId
|
|
1887
|
+
),
|
|
1888
|
+
counters,
|
|
1889
|
+
componentNames
|
|
1890
|
+
)
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1730
1896
|
if (defaultNodes.length > 0) {
|
|
1731
1897
|
slotFactories.push({
|
|
1732
1898
|
varName: `__slot_${id}_default`,
|
|
@@ -1738,7 +1904,9 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1738
1904
|
jsxFunctionNames,
|
|
1739
1905
|
componentNames,
|
|
1740
1906
|
counters,
|
|
1741
|
-
scopeId
|
|
1907
|
+
scopeId,
|
|
1908
|
+
devWarnings,
|
|
1909
|
+
filename
|
|
1742
1910
|
)
|
|
1743
1911
|
});
|
|
1744
1912
|
}
|
|
@@ -1761,7 +1929,9 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1761
1929
|
id,
|
|
1762
1930
|
component: node.tag,
|
|
1763
1931
|
propsExpr: parsePropsFromAttrs(
|
|
1764
|
-
node.
|
|
1932
|
+
node.tag === "Suspense" ? Object.fromEntries(
|
|
1933
|
+
Object.entries(node.attrs).filter(([key]) => key !== "fallback")
|
|
1934
|
+
) : node.attrs,
|
|
1765
1935
|
stateNames,
|
|
1766
1936
|
allSignalNames,
|
|
1767
1937
|
jsxFunctionNames
|
|
@@ -1786,7 +1956,7 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
|
|
|
1786
1956
|
scopeId
|
|
1787
1957
|
);
|
|
1788
1958
|
const buildFnStr = generateBuildFunction(
|
|
1789
|
-
|
|
1959
|
+
html,
|
|
1790
1960
|
stateNames,
|
|
1791
1961
|
resolveExpr,
|
|
1792
1962
|
counters,
|
|
@@ -1869,14 +2039,20 @@ function buildComponentCode(parsed, options) {
|
|
|
1869
2039
|
slot: 0,
|
|
1870
2040
|
if: 0
|
|
1871
2041
|
};
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2042
|
+
const warnings = options?.dev ? /* @__PURE__ */ new Set() : void 0;
|
|
2043
|
+
const transformed = runCompilePhase(
|
|
2044
|
+
"transform-template",
|
|
2045
|
+
() => transformTemplate(
|
|
2046
|
+
parsed.template,
|
|
2047
|
+
scriptInfo.stateNames,
|
|
2048
|
+
scriptInfo.allSignalNames,
|
|
2049
|
+
scriptInfo.jsxFunctionNames,
|
|
2050
|
+
scriptInfo.componentNames,
|
|
2051
|
+
counters,
|
|
2052
|
+
options?.scopeId,
|
|
2053
|
+
warnings,
|
|
2054
|
+
options?.hmrId ?? "component.olova"
|
|
2055
|
+
)
|
|
1880
2056
|
);
|
|
1881
2057
|
const exportHead = options?.exportName ? `export const ${options.exportName} = __defineComponent((__props, __slots) => {` : `const __olovaComponent = __defineComponent((__props, __slots) => {`;
|
|
1882
2058
|
const exportTail = options?.exportName ? `}, ${JSON.stringify(options?.hmrId)});` : `}, ${JSON.stringify(options?.hmrId)});
|
|
@@ -1890,6 +2066,9 @@ if (import.meta.hot) {
|
|
|
1890
2066
|
});
|
|
1891
2067
|
}
|
|
1892
2068
|
` : "";
|
|
2069
|
+
if (warnings) {
|
|
2070
|
+
emitDevWarnings(warnings);
|
|
2071
|
+
}
|
|
1893
2072
|
const code = `
|
|
1894
2073
|
${exportHead}
|
|
1895
2074
|
const props = __props;
|
|
@@ -2003,143 +2182,110 @@ ${hmrCode}
|
|
|
2003
2182
|
`;
|
|
2004
2183
|
return { importsCode: scriptInfo.importsCode, code };
|
|
2005
2184
|
}
|
|
2006
|
-
function
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
return
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
return part;
|
|
2048
|
-
}
|
|
2049
|
-
return applyScopeToSegment(part);
|
|
2050
|
-
}).join("");
|
|
2051
|
-
}).join(", ");
|
|
2052
|
-
}
|
|
2053
|
-
function scopeCss(css, scopeAttr) {
|
|
2054
|
-
let output = "";
|
|
2055
|
-
let index = 0;
|
|
2056
|
-
while (index < css.length) {
|
|
2057
|
-
const open = css.indexOf("{", index);
|
|
2058
|
-
if (open < 0) {
|
|
2059
|
-
output += css.slice(index);
|
|
2060
|
-
break;
|
|
2061
|
-
}
|
|
2062
|
-
const selector = css.slice(index, open).trim();
|
|
2063
|
-
let depth = 1;
|
|
2064
|
-
let cursor = open + 1;
|
|
2065
|
-
while (cursor < css.length && depth > 0) {
|
|
2066
|
-
if (css[cursor] === "{") {
|
|
2067
|
-
depth += 1;
|
|
2068
|
-
} else if (css[cursor] === "}") {
|
|
2069
|
-
depth -= 1;
|
|
2070
|
-
}
|
|
2071
|
-
cursor += 1;
|
|
2072
|
-
}
|
|
2073
|
-
const body = css.slice(open + 1, cursor - 1);
|
|
2074
|
-
if (/^@(?:media|supports|container|layer)\b/i.test(selector)) {
|
|
2075
|
-
output += `${selector} {${scopeCss(body, scopeAttr)}}`;
|
|
2076
|
-
} else if (/^@(?:keyframes|-webkit-keyframes)\b/i.test(selector)) {
|
|
2077
|
-
output += `${selector} {${body}}`;
|
|
2078
|
-
} else {
|
|
2079
|
-
output += `${scopeSelectorList(selector, scopeAttr)} {${body}}`;
|
|
2080
|
-
}
|
|
2081
|
-
index = cursor;
|
|
2185
|
+
function analyzeOlovaPhases(source, options) {
|
|
2186
|
+
const filename = options?.filename ?? "component.olova";
|
|
2187
|
+
try {
|
|
2188
|
+
const parsed = runCompilePhase(
|
|
2189
|
+
"parse-file",
|
|
2190
|
+
() => parseOlovaFile(source)
|
|
2191
|
+
);
|
|
2192
|
+
const script = runCompilePhase(
|
|
2193
|
+
"analyze-script",
|
|
2194
|
+
() => collectScriptInfo(parsed.script)
|
|
2195
|
+
);
|
|
2196
|
+
const scopeId = parsed.styles.some((style) => !("global" in style.attrs)) ? hashScopeSeed(`${filename}:${options?.exportName ?? "default"}`) : void 0;
|
|
2197
|
+
const template = runCompilePhase(
|
|
2198
|
+
"transform-template",
|
|
2199
|
+
() => transformTemplate(
|
|
2200
|
+
parsed.template,
|
|
2201
|
+
script.stateNames,
|
|
2202
|
+
script.allSignalNames,
|
|
2203
|
+
script.jsxFunctionNames,
|
|
2204
|
+
script.componentNames,
|
|
2205
|
+
{
|
|
2206
|
+
text: 0,
|
|
2207
|
+
attr: 0,
|
|
2208
|
+
event: 0,
|
|
2209
|
+
comp: 0,
|
|
2210
|
+
slot: 0,
|
|
2211
|
+
if: 0
|
|
2212
|
+
},
|
|
2213
|
+
scopeId,
|
|
2214
|
+
options?.dev ? /* @__PURE__ */ new Set() : void 0,
|
|
2215
|
+
filename
|
|
2216
|
+
)
|
|
2217
|
+
);
|
|
2218
|
+
return {
|
|
2219
|
+
parsed,
|
|
2220
|
+
script,
|
|
2221
|
+
template,
|
|
2222
|
+
scopeId
|
|
2223
|
+
};
|
|
2224
|
+
} catch (error) {
|
|
2225
|
+
return normalizeCompileError(error, source, filename);
|
|
2082
2226
|
}
|
|
2083
|
-
return output;
|
|
2084
|
-
}
|
|
2085
|
-
function collectCss(blocks, scopeId) {
|
|
2086
|
-
const scopeAttr = createScopeAttr(scopeId);
|
|
2087
|
-
return blocks.map((block) => {
|
|
2088
|
-
const content = block.content.trim();
|
|
2089
|
-
if (!content) {
|
|
2090
|
-
return "";
|
|
2091
|
-
}
|
|
2092
|
-
if (!("global" in block.attrs) && scopeAttr) {
|
|
2093
|
-
return scopeCss(content, scopeAttr);
|
|
2094
|
-
}
|
|
2095
|
-
return content;
|
|
2096
|
-
}).filter(Boolean).join("\n\n");
|
|
2097
2227
|
}
|
|
2098
2228
|
function compileOlovaScriptModule(source, options) {
|
|
2099
2229
|
try {
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2230
|
+
return runCompilePhase("analyze-script", () => {
|
|
2231
|
+
const scriptInfo = collectScriptInfo(source);
|
|
2232
|
+
return runCompilePhase(
|
|
2233
|
+
"transpile",
|
|
2234
|
+
() => transpileTypeScript(
|
|
2235
|
+
[scriptInfo.importsCode, scriptInfo.scriptBodyCode].filter(Boolean).join("\n"),
|
|
2236
|
+
options?.filename ?? "module.ts"
|
|
2237
|
+
)
|
|
2238
|
+
);
|
|
2239
|
+
});
|
|
2105
2240
|
} catch (error) {
|
|
2106
2241
|
return normalizeCompileError(error, source, options?.filename ?? "module.ts");
|
|
2107
2242
|
}
|
|
2108
2243
|
}
|
|
2109
2244
|
function compileOlova(source, options) {
|
|
2245
|
+
const filename = options?.filename ?? "component.olova";
|
|
2110
2246
|
try {
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2247
|
+
return runCompilePhase("parse-file", () => {
|
|
2248
|
+
const parsed = parseOlovaFile(source);
|
|
2249
|
+
if (!parsed.template.trim() && isModuleOnlyScript(parsed.script)) {
|
|
2250
|
+
const moduleResult = compileModuleScript(parsed.script, `${filename}.ts`);
|
|
2251
|
+
const css2 = runCompilePhase(
|
|
2252
|
+
"generate-module",
|
|
2253
|
+
() => collectCss(parsed.styles)
|
|
2254
|
+
);
|
|
2255
|
+
return {
|
|
2256
|
+
...moduleResult,
|
|
2257
|
+
css: css2
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
const scopeId = parsed.styles.some((style) => !("global" in style.attrs)) ? hashScopeSeed(`${filename}:${options?.exportName ?? "default"}`) : void 0;
|
|
2261
|
+
const compiled = runCompilePhase(
|
|
2262
|
+
"generate-module",
|
|
2263
|
+
() => buildComponentCode(parsed, {
|
|
2264
|
+
scopeId,
|
|
2265
|
+
hmrId: filename,
|
|
2266
|
+
dev: options?.dev
|
|
2267
|
+
})
|
|
2268
|
+
);
|
|
2269
|
+
const runtimeImport = options?.dev ? `import { defineComponent as __defineComponent, replaceComponent as __replaceComponent } from 'olova/runtime';` : `import { defineComponent as __defineComponent } from 'olova/runtime';`;
|
|
2270
|
+
const result = runCompilePhase("transpile", () => transpileTypeScript(`
|
|
2126
2271
|
${runtimeImport}
|
|
2127
2272
|
${compiled.importsCode}
|
|
2128
2273
|
${compiled.code}
|
|
2129
|
-
`,
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2274
|
+
`, `${filename}.ts`));
|
|
2275
|
+
const css = runCompilePhase(
|
|
2276
|
+
"generate-module",
|
|
2277
|
+
() => collectCss(parsed.styles, scopeId)
|
|
2278
|
+
);
|
|
2279
|
+
return {
|
|
2280
|
+
...result,
|
|
2281
|
+
css
|
|
2282
|
+
};
|
|
2283
|
+
});
|
|
2134
2284
|
} catch (error) {
|
|
2135
|
-
return normalizeCompileError(
|
|
2136
|
-
error,
|
|
2137
|
-
source,
|
|
2138
|
-
options?.filename ?? "component.olova"
|
|
2139
|
-
);
|
|
2285
|
+
return normalizeCompileError(error, source, filename);
|
|
2140
2286
|
}
|
|
2141
2287
|
}
|
|
2142
2288
|
|
|
2143
|
-
export { compileOlova, compileOlovaScriptModule, generateBuildFunction, parseHTML };
|
|
2289
|
+
export { analyzeOlovaPhases, compileOlova, compileOlovaScriptModule, generateBuildFunction, parseHTML };
|
|
2144
2290
|
//# sourceMappingURL=compiler.js.map
|
|
2145
2291
|
//# sourceMappingURL=compiler.js.map
|