@sola-air-ui/compiler 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +58 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sola-air-ui/compiler",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Compiler for Sola single-file components (.sola) — transforms template + script + style to ES modules",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
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(', ');
@@ -69,6 +74,35 @@ export function compile(source, options = {}) {
69
74
  const scopedCss = hasStyles ? scopeStyles(styleContent, scopeHash) : '';
70
75
 
71
76
  let rawTemplate = rawTemplateSource;
77
+
78
+ // Pre-process attribute-bound expressions BEFORE extractExpressions.
79
+ // extractExpressions replaces {expr} with <sola-expr> HTML tags, which breaks
80
+ // htmlparser2 when those tags land inside attribute values.
81
+ const dynAttrs = []; // expressions for __soladyn_N__ markers
82
+
83
+ // Event handlers: on:event={expr} → on:event="expr"
84
+ rawTemplate = rawTemplate.replace(/\bon:([\w]+)=\{([^}]+)\}/g, 'on:$1="$2"');
85
+ // Bind: bind:prop={expr} → bind:prop="expr"
86
+ rawTemplate = rawTemplate.replace(/\bbind:([\w]+)=\{([^}]+)\}/g, 'bind:$1="$2"');
87
+ // Quoted string interpolation: attr="text {expr} text" → attr="__soladyn_N__"
88
+ // Run iteratively to handle multiple {expr} per value.
89
+ let _prev;
90
+ do {
91
+ _prev = rawTemplate;
92
+ rawTemplate = rawTemplate.replace(/([\w:-]+)="([^"]*\{[^{}]+\}[^"]*)"/g, (_, attr, val) => {
93
+ const jsExpr = '`' + val.replace(/\{([^{}]+)\}/g, (__, e) => `\${${e.trim()}}`) + '`';
94
+ const i = dynAttrs.length;
95
+ dynAttrs.push(jsExpr);
96
+ return `${attr}="__soladyn_${i}__"`;
97
+ });
98
+ } while (rawTemplate !== _prev);
99
+ // Unquoted attr expressions: attr={expr} → attr="__soladyn_N__"
100
+ rawTemplate = rawTemplate.replace(/([\w:-]+)=\{([^{}]+)\}/g, (_, attr, expr) => {
101
+ const i = dynAttrs.length;
102
+ dynAttrs.push(expr.trim());
103
+ return `${attr}="__soladyn_${i}__"`;
104
+ });
105
+
72
106
  rawTemplate = extractExpressions(rawTemplate);
73
107
 
74
108
  // Pre-process logic blocks: structured if-then-else
@@ -148,7 +182,7 @@ export function compile(source, options = {}) {
148
182
  exportedProps.push({ name, default: defaultVal });
149
183
  edits.push({
150
184
  start: node.start,
151
- end: node.start + 7,
185
+ end: node.end,
152
186
  replacement: ''
153
187
  });
154
188
  });
@@ -173,10 +207,13 @@ export function compile(source, options = {}) {
173
207
  const varName = decl.id.name;
174
208
  const expr = decl.init.arguments[0];
175
209
  const exprSrc = expr ? scriptContent.slice(expr.start, expr.end) : 'undefined';
210
+ const isFn = expr && (expr.type === 'ArrowFunctionExpression' || expr.type === 'FunctionExpression');
176
211
  edits.push({
177
212
  start: node.start,
178
213
  end: node.end,
179
- replacement: `const ${varName} = createDerived(() => ${exprSrc});`
214
+ replacement: isFn
215
+ ? `const ${varName} = createDerived(${exprSrc});`
216
+ : `const ${varName} = createDerived(() => ${exprSrc});`
180
217
  });
181
218
  } else if (callee.type === 'Identifier' && callee.name === '$effect') {
182
219
  const body = decl.init.arguments[0];
@@ -367,15 +404,31 @@ export function compile(source, options = {}) {
367
404
  domCode += ` ${id}.classList.add('${scopeHash}');\n`;
368
405
  }
369
406
 
407
+ const BOOLEAN_ATTRS = new Set(['disabled', 'checked', 'selected', 'readonly', 'required', 'multiple', 'open']);
370
408
  for (const [key, val] of Object.entries(node.attribs || {})) {
409
+ // Dynamic attribute pre-processed from {expr} in attribute values
410
+ const dynMatch = /^__soladyn_(\d+)__$/.exec(val);
411
+ if (dynMatch) {
412
+ const expr = dynAttrs[parseInt(dynMatch[1])];
413
+ if (BOOLEAN_ATTRS.has(key)) {
414
+ domCode += ` createEffect(() => { const _v = !!(${expr}); if (_v) ${id}.setAttribute('${key}', ''); else ${id}.removeAttribute('${key}'); });\n`;
415
+ } else if (key === 'class' || key === 'className') {
416
+ const suffix = hasStyles ? ` + ' ${scopeHash}'` : '';
417
+ domCode += ` createEffect(() => { ${id}.className = (${expr})${suffix}; });\n`;
418
+ } else {
419
+ domCode += ` createEffect(() => { ${id}.setAttribute('${key}', String(${expr})); });\n`;
420
+ }
421
+ continue;
422
+ }
371
423
  if (key.startsWith('on:')) {
372
424
  const eventName = key.slice(3);
373
425
  if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(eventName)) continue;
374
- domCode += ` ${id}.addEventListener('${eventName}', (e) => { ${val} });\n`;
426
+ // val is the handler expression (pre-processed from {expr}), pass directly
427
+ domCode += ` ${id}.addEventListener('${eventName}', ${val});\n`;
375
428
  } else if (key.startsWith('on')) {
376
429
  const eventName = key.slice(2).toLowerCase();
377
430
  if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(eventName)) continue;
378
- domCode += ` ${id}.addEventListener('${eventName}', (e) => { ${val} });\n`;
431
+ domCode += ` ${id}.addEventListener('${eventName}', ${val});\n`;
379
432
  } else if (key.startsWith('bind:')) {
380
433
  const prop = key.slice(5);
381
434
  if (prop === 'value') {
@@ -402,7 +455,7 @@ export function compile(source, options = {}) {
402
455
 
403
456
  // Assemble final output module
404
457
  let output = '';
405
- output += `// Compiled by @sola-air-ui/compiler v1.0.0\n`;
458
+ output += `// Compiled by @sola-air-ui/compiler v1.0.1\n`;
406
459
  output += `import { createSignal, createDerived, createEffect, createIntent, createData, onMount, onDestroy, pushContext, popContext, __flush_mounts, __flush_destroys } from '@sola-air-ui/core';\n`;
407
460
 
408
461
  for (const imp of componentImports) {