@sola-air-ui/compiler 1.0.7 → 1.0.8

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 (3) hide show
  1. package/README.md +57 -0
  2. package/package.json +1 -1
  3. package/src/index.js +26 -7
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @sola-air-ui/compiler
2
+
3
+ Compiler for [Sola AIR](https://sola-air.dev) single-file `.sola` components — transforms `<script>` + template + `<style>` into a dependency-free ES module (or IIFE) that mounts real DOM nodes with fine-grained reactivity. No virtual DOM, no diffing.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @sola-air-ui/compiler
9
+ ```
10
+
11
+ ## Programmatic use
12
+
13
+ ```js
14
+ import { compile } from '@sola-air-ui/compiler';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const source = readFileSync('App.sola', 'utf-8');
18
+ const result = compile(source, {
19
+ target: 'esm', // or 'iife'
20
+ exportName: 'App', // used for iife builds
21
+ filename: 'App.sola', // improves error messages
22
+ });
23
+
24
+ console.log(result.code); // compiled JS
25
+ console.log(result.css); // scoped CSS
26
+ ```
27
+
28
+ ## CLI
29
+
30
+ ```bash
31
+ npx sola build App.sola
32
+ ```
33
+
34
+ ## What a `.sola` file looks like
35
+
36
+ ```html
37
+ <script>
38
+ let count = $state(0);
39
+ let doubled = $derived(() => count() * 2);
40
+ </script>
41
+
42
+ <div>
43
+ <h1>Count: {count()}</h1>
44
+ <p>Doubled: {doubled()}</p>
45
+ <button onclick={() => count(count() + 1)}>+1</button>
46
+ </div>
47
+
48
+ <style>
49
+ h1 { color: #0ea5e9; font-weight: 800; }
50
+ </style>
51
+ ```
52
+
53
+ Most projects won't call this package directly — use `@sola-air-ui/vite-plugin-sola` for a normal Vite dev/build setup, or the `sola-air` meta-package to install everything at once.
54
+
55
+ ## License
56
+
57
+ MIT — see the [repo root](https://github.com/rbm3267/sola-air) for the full license and [changelog](https://github.com/rbm3267/sola-air/blob/main/CHANGELOG.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sola-air-ui/compiler",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
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
@@ -125,7 +125,9 @@ function preprocessTemplate(template, dynAttrs) {
125
125
  } else if (template[i] === '"' || template[i] === "'") {
126
126
  // Quoted attribute value — scan for {expr} interpolations
127
127
  const quote = template[i++];
128
- let inner = ''; let hasDyn = false;
128
+ const parts = [];
129
+ let currentText = '';
130
+ let hasDyn = false;
129
131
  while (i < template.length && template[i] !== quote) {
130
132
  if (template[i] === '{') {
131
133
  const closeIdx = matchBrace(template, i);
@@ -133,18 +135,35 @@ function preprocessTemplate(template, dynAttrs) {
133
135
  const expr = template.slice(i + 1, closeIdx).trim();
134
136
  if (!expr.startsWith('#') && !expr.startsWith('/') && !expr.startsWith(':')) {
135
137
  hasDyn = true;
136
- inner += `\${${expr}}`; i = closeIdx + 1; continue;
138
+ parts.push({ text: currentText });
139
+ currentText = '';
140
+ parts.push({ expr });
141
+ i = closeIdx + 1; continue;
137
142
  }
138
143
  }
139
144
  }
140
- inner += template[i++];
145
+ currentText += template[i++];
141
146
  }
147
+ parts.push({ text: currentText });
142
148
  if (i < template.length) i++; // skip closing quote
143
149
  if (hasDyn) {
144
- dynAttrs.push('`' + inner + '`');
150
+ // Build a string-concatenation expression instead of a runtime backtick
151
+ // template literal. Confirmed by driving a real browser against a live
152
+ // ServiceNow Service Portal instance: its widget-script delivery pipeline
153
+ // silently mangles `${expr}` interpolation inside a template literal down
154
+ // to bare, unevaluated expression text (e.g. `background: ${x}` becomes
155
+ // the literal string "background: x" — the function reference itself,
156
+ // never called). Every prior "the reactive binding doesn't do anything"
157
+ // report came from this exact code path. Concatenation is semantically
158
+ // identical and isn't affected by whatever that pipeline does to backticks.
159
+ const combined = parts
160
+ .filter((p) => p.expr !== undefined || p.text !== '')
161
+ .map((p) => (p.expr !== undefined ? `(${p.expr})` : JSON.stringify(p.text)))
162
+ .join(' + ');
163
+ dynAttrs.push(combined || `''`);
145
164
  result += `${attrName}="__soladyn_${dynAttrs.length - 1}__"`;
146
165
  } else {
147
- result += `${attrName}="${inner}"`;
166
+ result += `${attrName}="${currentText}"`;
148
167
  }
149
168
  } else {
150
169
  result += `${attrName}=`;
@@ -697,9 +716,9 @@ export function compile(source, options = {}) {
697
716
  output += ` // Reactive DOM graph\n`;
698
717
  output += domCode;
699
718
 
700
- output += `\n __flush_mounts();\n`;
719
+ output += `\n __flush_mounts(__ctx);\n`;
701
720
  output += `\n return () => {\n`;
702
- output += ` __flush_destroys();\n`;
721
+ output += ` __flush_destroys(__ctx);\n`;
703
722
  output += ` popContext(__ctx);\n`;
704
723
  output += ` __target.innerHTML = '';\n`;
705
724
  output += ` };\n`;