@sola-air-ui/compiler 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +225 -41
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -25,6 +25,11 @@ function scopeStyles(css, scopeHash) {
|
|
|
25
25
|
.map(part => {
|
|
26
26
|
const trimmed = part.trim();
|
|
27
27
|
if (!trimmed) return part;
|
|
28
|
+
// Insert scope hash before pseudo-class/element (e.g. button:hover → button.hash:hover)
|
|
29
|
+
const pseudoIdx = trimmed.search(/::?[\w-]/);
|
|
30
|
+
if (pseudoIdx > 0) {
|
|
31
|
+
return `${trimmed.slice(0, pseudoIdx)}.${scopeHash}${trimmed.slice(pseudoIdx)}`;
|
|
32
|
+
}
|
|
28
33
|
return `${trimmed}.${scopeHash}`;
|
|
29
34
|
})
|
|
30
35
|
.join(', ');
|
|
@@ -52,14 +57,138 @@ function extractScriptAndStyle(source) {
|
|
|
52
57
|
return { script, style, template };
|
|
53
58
|
}
|
|
54
59
|
|
|
60
|
+
// Returns the index of the closing } that matches the { at openPos,
|
|
61
|
+
// respecting string literals and nested braces. Returns -1 if unbalanced.
|
|
62
|
+
function matchBrace(text, openPos) {
|
|
63
|
+
let depth = 0;
|
|
64
|
+
let inStr = null;
|
|
65
|
+
for (let i = openPos; i < text.length; i++) {
|
|
66
|
+
const ch = text[i];
|
|
67
|
+
if (inStr) {
|
|
68
|
+
if (ch === '\\') { i++; continue; }
|
|
69
|
+
if (ch === inStr) inStr = null;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === '"' || ch === "'" || ch === '`') { inStr = ch; continue; }
|
|
73
|
+
if (ch === '{') depth++;
|
|
74
|
+
else if (ch === '}') { if (--depth === 0) return i; }
|
|
75
|
+
}
|
|
76
|
+
return -1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Walk the template and resolve {expr} in attribute values using balanced-brace
|
|
80
|
+
// matching, storing dynamic expressions in dynAttrs. Event/bind handlers are
|
|
81
|
+
// stored as plain strings; other expressions become __soladyn_N__ markers.
|
|
82
|
+
function preprocessTemplate(template, dynAttrs) {
|
|
83
|
+
let result = '';
|
|
84
|
+
let i = 0;
|
|
85
|
+
while (i < template.length) {
|
|
86
|
+
// Opening tag (not closing, not comment)
|
|
87
|
+
if (template[i] === '<' && template[i + 1] !== '/' && template[i + 1] !== '!') {
|
|
88
|
+
result += '<';
|
|
89
|
+
i++;
|
|
90
|
+
// Tag name
|
|
91
|
+
while (i < template.length && /[^\s>\/]/.test(template[i])) result += template[i++];
|
|
92
|
+
// Attributes
|
|
93
|
+
while (i < template.length && template[i] !== '>' && !(template[i] === '/' && template[i + 1] === '>')) {
|
|
94
|
+
if (/\s/.test(template[i])) { result += template[i++]; continue; }
|
|
95
|
+
// Attribute name
|
|
96
|
+
let attrName = '';
|
|
97
|
+
while (i < template.length && /[^\s=\/>]/.test(template[i])) attrName += template[i++];
|
|
98
|
+
if (!attrName) { result += template[i++]; continue; }
|
|
99
|
+
if (template[i] !== '=') { result += attrName; continue; }
|
|
100
|
+
i++; // skip =
|
|
101
|
+
if (template[i] === '{') {
|
|
102
|
+
// Unquoted brace expression: attr={expr}
|
|
103
|
+
const closeIdx = matchBrace(template, i);
|
|
104
|
+
if (closeIdx !== -1) {
|
|
105
|
+
const expr = template.slice(i + 1, closeIdx).trim();
|
|
106
|
+
if (!expr.startsWith('#') && !expr.startsWith('/') && !expr.startsWith(':')) {
|
|
107
|
+
if (/^on:[\w]+$/.test(attrName) || /^bind:[\w]+$/.test(attrName)) {
|
|
108
|
+
result += `${attrName}="${expr}"`;
|
|
109
|
+
} else {
|
|
110
|
+
dynAttrs.push(expr);
|
|
111
|
+
result += `${attrName}="__soladyn_${dynAttrs.length - 1}__"`;
|
|
112
|
+
}
|
|
113
|
+
i = closeIdx + 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
result += `${attrName}={`; i++;
|
|
118
|
+
} else if (template[i] === '"' || template[i] === "'") {
|
|
119
|
+
// Quoted attribute value — scan for {expr} interpolations
|
|
120
|
+
const quote = template[i++];
|
|
121
|
+
let inner = ''; let hasDyn = false;
|
|
122
|
+
while (i < template.length && template[i] !== quote) {
|
|
123
|
+
if (template[i] === '{') {
|
|
124
|
+
const closeIdx = matchBrace(template, i);
|
|
125
|
+
if (closeIdx !== -1) {
|
|
126
|
+
const expr = template.slice(i + 1, closeIdx).trim();
|
|
127
|
+
if (!expr.startsWith('#') && !expr.startsWith('/') && !expr.startsWith(':')) {
|
|
128
|
+
hasDyn = true;
|
|
129
|
+
inner += `\${${expr}}`; i = closeIdx + 1; continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
inner += template[i++];
|
|
134
|
+
}
|
|
135
|
+
if (i < template.length) i++; // skip closing quote
|
|
136
|
+
if (hasDyn) {
|
|
137
|
+
dynAttrs.push('`' + inner + '`');
|
|
138
|
+
result += `${attrName}="__soladyn_${dynAttrs.length - 1}__"`;
|
|
139
|
+
} else {
|
|
140
|
+
result += `${attrName}="${inner}"`;
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
result += `${attrName}=`;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (i < template.length && template[i] === '/') { result += '/'; i++; }
|
|
148
|
+
if (i < template.length && template[i] === '>') { result += '>'; i++; }
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
result += template[i++];
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Replace {expr} in text content with <sola-expr> tags, using balanced-brace
|
|
157
|
+
// matching so expressions like {fn({key: val})} work correctly.
|
|
55
158
|
function extractExpressions(text) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
159
|
+
let result = '';
|
|
160
|
+
let i = 0;
|
|
161
|
+
while (i < text.length) {
|
|
162
|
+
if (text[i] === '{') {
|
|
163
|
+
const closeIdx = matchBrace(text, i);
|
|
164
|
+
if (closeIdx === -1) { result += text[i++]; continue; }
|
|
165
|
+
const inner = text.slice(i + 1, closeIdx).trim();
|
|
166
|
+
if (inner.startsWith('#') || inner.startsWith('/') || inner.startsWith(':')) {
|
|
167
|
+
result += text.slice(i, closeIdx + 1); i = closeIdx + 1;
|
|
168
|
+
} else {
|
|
169
|
+
result += `<sola-expr expr="${inner.replace(/"/g, '"')}"></sola-expr>`;
|
|
170
|
+
i = closeIdx + 1;
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
result += text[i++];
|
|
60
174
|
}
|
|
61
|
-
|
|
62
|
-
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Strip TypeScript type annotations so Acorn (plain JS parser) can handle
|
|
180
|
+
// script blocks written in TS. Covers the common patterns: type annotations,
|
|
181
|
+
// interface/type declarations, angle-bracket generics, and `as` casts.
|
|
182
|
+
function stripTypeScript(code) {
|
|
183
|
+
// Remove interface and type alias declarations
|
|
184
|
+
code = code.replace(/^\s*(export\s+)?(interface|type)\s+\w[\s\S]*?(?=\n(?:export|const|let|var|function|class|\/\/|$))/gm, '');
|
|
185
|
+
// Remove inline type annotations: `: Type` before = , ) , ; , , or end of line
|
|
186
|
+
code = code.replace(/:\s*[\w<>\[\]|&{},\s.?]+(?=[=,);{}\n])/g, '');
|
|
187
|
+
// Remove `as Type` casts
|
|
188
|
+
code = code.replace(/\s+as\s+[\w<>\[\]|&{}.?]+/g, '');
|
|
189
|
+
// Remove generic type parameters from function/class declarations
|
|
190
|
+
code = code.replace(/<[^>()=]+>(?=\s*\()/g, '');
|
|
191
|
+
return code;
|
|
63
192
|
}
|
|
64
193
|
|
|
65
194
|
export function compile(source, options = {}) {
|
|
@@ -69,6 +198,11 @@ export function compile(source, options = {}) {
|
|
|
69
198
|
const scopedCss = hasStyles ? scopeStyles(styleContent, scopeHash) : '';
|
|
70
199
|
|
|
71
200
|
let rawTemplate = rawTemplateSource;
|
|
201
|
+
|
|
202
|
+
// Pre-process attribute-bound expressions BEFORE extractExpressions using
|
|
203
|
+
// balanced-brace matching (handles nested object literals, ternaries, etc.)
|
|
204
|
+
const dynAttrs = [];
|
|
205
|
+
rawTemplate = preprocessTemplate(rawTemplate, dynAttrs);
|
|
72
206
|
rawTemplate = extractExpressions(rawTemplate);
|
|
73
207
|
|
|
74
208
|
// Pre-process logic blocks: structured if-then-else
|
|
@@ -76,8 +210,8 @@ export function compile(source, options = {}) {
|
|
|
76
210
|
rawTemplate = rawTemplate
|
|
77
211
|
.replace(/{#if\s+([\s\S]*?)}([\s\S]*?){:else}([\s\S]*?){\/if}/g, '<sola-if condition="$1"><sola-then>$2</sola-then><sola-else>$3</sola-else></sola-if>')
|
|
78
212
|
.replace(/{#if\s+([\s\S]*?)}([\s\S]*?){\/if}/g, '<sola-if condition="$1"><sola-then>$2</sola-then></sola-if>')
|
|
79
|
-
.replace(/{#each\s+([\s\S]*?)\s+as\s+([\w]+)(?:\s*,\s*([\w]+))?}/g,
|
|
80
|
-
(_, arr, item, index) => `<sola-each array="${arr}" item="${item}"${index ? ` index="${index}"` : ''}>`)
|
|
213
|
+
.replace(/{#each\s+([\s\S]*?)\s+as\s+([\w]+)(?:\s*,\s*([\w]+))?(?:\s*\(([^)]+)\))?}/g,
|
|
214
|
+
(_, arr, item, index, key) => `<sola-each array="${arr}" item="${item}"${index ? ` index="${index}"` : ''}${key ? ` key="${key.trim()}"` : ''}>`)
|
|
81
215
|
.replace(/{\/each}/g, '</sola-each>');
|
|
82
216
|
|
|
83
217
|
// Parse HTML AST
|
|
@@ -109,14 +243,16 @@ export function compile(source, options = {}) {
|
|
|
109
243
|
parser.end();
|
|
110
244
|
|
|
111
245
|
// Parse JS AST with Acorn
|
|
112
|
-
|
|
246
|
+
// Strip TS first — AST positions and all slice() calls operate on the stripped source
|
|
247
|
+
const strippedScript = scriptContent.trim() ? stripTypeScript(scriptContent) : scriptContent;
|
|
248
|
+
let jsOutput = strippedScript;
|
|
113
249
|
const stateVars = new Set();
|
|
114
250
|
const exportedProps = [];
|
|
115
251
|
const componentImports = [];
|
|
116
252
|
|
|
117
|
-
if (
|
|
253
|
+
if (strippedScript.trim()) {
|
|
118
254
|
try {
|
|
119
|
-
const ast = acorn.parse(
|
|
255
|
+
const ast = acorn.parse(strippedScript, { ecmaVersion: 'latest', sourceType: 'module' });
|
|
120
256
|
const edits = [];
|
|
121
257
|
|
|
122
258
|
function walkAST(node, parent) {
|
|
@@ -143,12 +279,12 @@ export function compile(source, options = {}) {
|
|
|
143
279
|
node.declaration.declarations.forEach(decl => {
|
|
144
280
|
const name = decl.id.name;
|
|
145
281
|
const defaultVal = decl.init
|
|
146
|
-
?
|
|
282
|
+
? strippedScript.slice(decl.init.start, decl.init.end)
|
|
147
283
|
: 'undefined';
|
|
148
284
|
exportedProps.push({ name, default: defaultVal });
|
|
149
285
|
edits.push({
|
|
150
286
|
start: node.start,
|
|
151
|
-
end: node.
|
|
287
|
+
end: node.end,
|
|
152
288
|
replacement: ''
|
|
153
289
|
});
|
|
154
290
|
});
|
|
@@ -163,7 +299,7 @@ export function compile(source, options = {}) {
|
|
|
163
299
|
const varName = decl.id.name;
|
|
164
300
|
stateVars.add(varName);
|
|
165
301
|
const initArg = decl.init.arguments[0];
|
|
166
|
-
const initVal = initArg ?
|
|
302
|
+
const initVal = initArg ? strippedScript.slice(initArg.start, initArg.end) : 'undefined';
|
|
167
303
|
edits.push({
|
|
168
304
|
start: node.start,
|
|
169
305
|
end: node.end,
|
|
@@ -172,15 +308,18 @@ export function compile(source, options = {}) {
|
|
|
172
308
|
} else if (callee.type === 'Identifier' && callee.name === '$derived') {
|
|
173
309
|
const varName = decl.id.name;
|
|
174
310
|
const expr = decl.init.arguments[0];
|
|
175
|
-
const exprSrc = expr ?
|
|
311
|
+
const exprSrc = expr ? strippedScript.slice(expr.start, expr.end) : 'undefined';
|
|
312
|
+
const isFn = expr && (expr.type === 'ArrowFunctionExpression' || expr.type === 'FunctionExpression');
|
|
176
313
|
edits.push({
|
|
177
314
|
start: node.start,
|
|
178
315
|
end: node.end,
|
|
179
|
-
replacement:
|
|
316
|
+
replacement: isFn
|
|
317
|
+
? `const ${varName} = createDerived(${exprSrc});`
|
|
318
|
+
: `const ${varName} = createDerived(() => ${exprSrc});`
|
|
180
319
|
});
|
|
181
320
|
} else if (callee.type === 'Identifier' && callee.name === '$effect') {
|
|
182
321
|
const body = decl.init.arguments[0];
|
|
183
|
-
const bodySrc = body ?
|
|
322
|
+
const bodySrc = body ? strippedScript.slice(body.start, body.end) : '() => {}';
|
|
184
323
|
edits.push({
|
|
185
324
|
start: node.start,
|
|
186
325
|
end: node.end,
|
|
@@ -188,10 +327,10 @@ export function compile(source, options = {}) {
|
|
|
188
327
|
});
|
|
189
328
|
} else if (callee.type === 'Identifier' && callee.name === '$intent') {
|
|
190
329
|
const promptArg = decl.init.arguments[0]
|
|
191
|
-
?
|
|
330
|
+
? strippedScript.slice(decl.init.arguments[0].start, decl.init.arguments[0].end)
|
|
192
331
|
: "''";
|
|
193
332
|
const fallbackArg = decl.init.arguments[1]
|
|
194
|
-
?
|
|
333
|
+
? strippedScript.slice(decl.init.arguments[1].start, decl.init.arguments[1].end)
|
|
195
334
|
: 'null';
|
|
196
335
|
edits.push({
|
|
197
336
|
start: node.start,
|
|
@@ -200,10 +339,10 @@ export function compile(source, options = {}) {
|
|
|
200
339
|
});
|
|
201
340
|
} else if (callee.type === 'Identifier' && callee.name === '$data') {
|
|
202
341
|
const sourceArg = decl.init.arguments[0]
|
|
203
|
-
?
|
|
342
|
+
? strippedScript.slice(decl.init.arguments[0].start, decl.init.arguments[0].end)
|
|
204
343
|
: "''";
|
|
205
344
|
const optsArg = decl.init.arguments[1]
|
|
206
|
-
? ', ' +
|
|
345
|
+
? ', ' + strippedScript.slice(decl.init.arguments[1].start, decl.init.arguments[1].end)
|
|
207
346
|
: '';
|
|
208
347
|
edits.push({
|
|
209
348
|
start: node.start,
|
|
@@ -220,7 +359,7 @@ export function compile(source, options = {}) {
|
|
|
220
359
|
node.left.type === 'Identifier' &&
|
|
221
360
|
stateVars.has(node.left.name)) {
|
|
222
361
|
const name = node.left.name;
|
|
223
|
-
const rightSource =
|
|
362
|
+
const rightSource = strippedScript.slice(node.right.start, node.right.end);
|
|
224
363
|
if (node.operator === '=') {
|
|
225
364
|
edits.push({ start: node.start, end: node.end, replacement: `set_${name}(${rightSource})` });
|
|
226
365
|
} else {
|
|
@@ -260,7 +399,9 @@ export function compile(source, options = {}) {
|
|
|
260
399
|
jsOutput = jsOutput.slice(0, edit.start) + edit.replacement + jsOutput.slice(edit.end);
|
|
261
400
|
}
|
|
262
401
|
} catch (err) {
|
|
263
|
-
|
|
402
|
+
const loc = err.loc ? ` (line ${err.loc.line}, col ${err.loc.column})` : '';
|
|
403
|
+
const filePath = typeof options === 'string' ? options : (options.filename || '<script>');
|
|
404
|
+
throw new Error(`[sola compiler] Parse error in ${filePath}${loc}:\n ${err.message}`);
|
|
264
405
|
}
|
|
265
406
|
}
|
|
266
407
|
|
|
@@ -336,26 +477,53 @@ export function compile(source, options = {}) {
|
|
|
336
477
|
return;
|
|
337
478
|
}
|
|
338
479
|
|
|
339
|
-
// {#each}
|
|
480
|
+
// {#each} with optional keyed diffing: {#each items as item (item.id)}
|
|
340
481
|
if (node.name === 'sola-each') {
|
|
341
482
|
const eid = `e${uid++}`;
|
|
483
|
+
const keyExpr = node.attribs.key || null;
|
|
342
484
|
domCode += ` const ${eid}_a = document.createComment('each');\n`;
|
|
343
485
|
domCode += ` ${parentVar}.appendChild(${eid}_a);\n`;
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
domCode += `
|
|
486
|
+
if (keyExpr) {
|
|
487
|
+
// Keyed: maintain a Map<key, Node[]> and reconcile on each run
|
|
488
|
+
domCode += ` let ${eid}_keyMap = new Map();\n`;
|
|
489
|
+
domCode += ` createEffect(() => {\n`;
|
|
490
|
+
domCode += ` const _items = ${node.attribs.array} || [];\n`;
|
|
491
|
+
domCode += ` const _nextKeys = _items.map((${node.attribs.item}, _i) => String(${keyExpr}));\n`;
|
|
492
|
+
domCode += ` // Remove stale keys\n`;
|
|
493
|
+
domCode += ` for (const [_k, _nodes] of ${eid}_keyMap) {\n`;
|
|
494
|
+
domCode += ` if (!_nextKeys.includes(_k)) { _nodes.forEach(n => n.remove()); ${eid}_keyMap.delete(_k); }\n`;
|
|
495
|
+
domCode += ` }\n`;
|
|
496
|
+
domCode += ` // Insert/reorder items\n`;
|
|
497
|
+
domCode += ` let _anchor = ${eid}_a.nextSibling;\n`;
|
|
498
|
+
domCode += ` for (let _i = 0; _i < _items.length; _i++) {\n`;
|
|
499
|
+
domCode += ` const ${node.attribs.item} = _items[_i];\n`;
|
|
500
|
+
if (node.attribs.index) domCode += ` const ${node.attribs.index} = _i;\n`;
|
|
501
|
+
domCode += ` const _key = String(${keyExpr});\n`;
|
|
502
|
+
domCode += ` if (!${eid}_keyMap.has(_key)) {\n`;
|
|
503
|
+
domCode += ` const f = document.createDocumentFragment();\n`;
|
|
504
|
+
node.children.forEach(child => emitNode(child, 'f'));
|
|
505
|
+
domCode += ` const _nodes = Array.from(f.childNodes);\n`;
|
|
506
|
+
domCode += ` ${eid}_keyMap.set(_key, _nodes);\n`;
|
|
507
|
+
domCode += ` ${eid}_a.parentNode.insertBefore(f, _anchor);\n`;
|
|
508
|
+
domCode += ` }\n`;
|
|
509
|
+
domCode += ` }\n`;
|
|
510
|
+
domCode += ` });\n`;
|
|
511
|
+
} else {
|
|
512
|
+
// Unkeyed: full teardown + rebuild
|
|
513
|
+
domCode += ` let ${eid}_els = [];\n`;
|
|
514
|
+
domCode += ` createEffect(() => {\n`;
|
|
515
|
+
domCode += ` ${eid}_els.forEach(e => e.remove()); ${eid}_els = [];\n`;
|
|
516
|
+
domCode += ` const _items = ${node.attribs.array} || [];\n`;
|
|
517
|
+
domCode += ` const f = document.createDocumentFragment();\n`;
|
|
518
|
+
domCode += ` for (let _i = 0; _i < _items.length; _i++) {\n`;
|
|
519
|
+
domCode += ` const ${node.attribs.item} = _items[_i];\n`;
|
|
520
|
+
if (node.attribs.index) domCode += ` const ${node.attribs.index} = _i;\n`;
|
|
521
|
+
node.children.forEach(child => emitNode(child, 'f'));
|
|
522
|
+
domCode += ` }\n`;
|
|
523
|
+
domCode += ` Array.from(f.childNodes).forEach(n => ${eid}_els.push(n));\n`;
|
|
524
|
+
domCode += ` ${eid}_a.parentNode.insertBefore(f, ${eid}_a.nextSibling);\n`;
|
|
525
|
+
domCode += ` });\n`;
|
|
353
526
|
}
|
|
354
|
-
node.children.forEach(child => emitNode(child, 'f'));
|
|
355
|
-
domCode += ` }\n`;
|
|
356
|
-
domCode += ` Array.from(f.childNodes).forEach(n => ${eid}_els.push(n));\n`;
|
|
357
|
-
domCode += ` ${eid}_a.parentNode.insertBefore(f, ${eid}_a.nextSibling);\n`;
|
|
358
|
-
domCode += ` });\n`;
|
|
359
527
|
return;
|
|
360
528
|
}
|
|
361
529
|
|
|
@@ -367,15 +535,31 @@ export function compile(source, options = {}) {
|
|
|
367
535
|
domCode += ` ${id}.classList.add('${scopeHash}');\n`;
|
|
368
536
|
}
|
|
369
537
|
|
|
538
|
+
const BOOLEAN_ATTRS = new Set(['disabled', 'checked', 'selected', 'readonly', 'required', 'multiple', 'open']);
|
|
370
539
|
for (const [key, val] of Object.entries(node.attribs || {})) {
|
|
540
|
+
// Dynamic attribute pre-processed from {expr} in attribute values
|
|
541
|
+
const dynMatch = /^__soladyn_(\d+)__$/.exec(val);
|
|
542
|
+
if (dynMatch) {
|
|
543
|
+
const expr = dynAttrs[parseInt(dynMatch[1])];
|
|
544
|
+
if (BOOLEAN_ATTRS.has(key)) {
|
|
545
|
+
domCode += ` createEffect(() => { const _v = !!(${expr}); if (_v) ${id}.setAttribute('${key}', ''); else ${id}.removeAttribute('${key}'); });\n`;
|
|
546
|
+
} else if (key === 'class' || key === 'className') {
|
|
547
|
+
const suffix = hasStyles ? ` + ' ${scopeHash}'` : '';
|
|
548
|
+
domCode += ` createEffect(() => { ${id}.className = (${expr})${suffix}; });\n`;
|
|
549
|
+
} else {
|
|
550
|
+
domCode += ` createEffect(() => { ${id}.setAttribute('${key}', String(${expr})); });\n`;
|
|
551
|
+
}
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
371
554
|
if (key.startsWith('on:')) {
|
|
372
555
|
const eventName = key.slice(3);
|
|
373
556
|
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(eventName)) continue;
|
|
374
|
-
|
|
557
|
+
// val is the handler expression (pre-processed from {expr}), pass directly
|
|
558
|
+
domCode += ` ${id}.addEventListener('${eventName}', ${val});\n`;
|
|
375
559
|
} else if (key.startsWith('on')) {
|
|
376
560
|
const eventName = key.slice(2).toLowerCase();
|
|
377
561
|
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(eventName)) continue;
|
|
378
|
-
domCode += ` ${id}.addEventListener('${eventName}',
|
|
562
|
+
domCode += ` ${id}.addEventListener('${eventName}', ${val});\n`;
|
|
379
563
|
} else if (key.startsWith('bind:')) {
|
|
380
564
|
const prop = key.slice(5);
|
|
381
565
|
if (prop === 'value') {
|
|
@@ -402,7 +586,7 @@ export function compile(source, options = {}) {
|
|
|
402
586
|
|
|
403
587
|
// Assemble final output module
|
|
404
588
|
let output = '';
|
|
405
|
-
output += `// Compiled by @sola-air-ui/compiler v1.0.
|
|
589
|
+
output += `// Compiled by @sola-air-ui/compiler v1.0.2\n`;
|
|
406
590
|
output += `import { createSignal, createDerived, createEffect, createIntent, createData, onMount, onDestroy, pushContext, popContext, __flush_mounts, __flush_destroys } from '@sola-air-ui/core';\n`;
|
|
407
591
|
|
|
408
592
|
for (const imp of componentImports) {
|