@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.
- package/README.md +57 -0
- package/package.json +1 -1
- 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
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
|
-
|
|
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
|
-
|
|
138
|
+
parts.push({ text: currentText });
|
|
139
|
+
currentText = '';
|
|
140
|
+
parts.push({ expr });
|
|
141
|
+
i = closeIdx + 1; continue;
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
}
|
|
140
|
-
|
|
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
|
-
|
|
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}="${
|
|
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`;
|