@sola-air-ui/compiler 1.5.4 → 1.6.0
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 +1 -1
- package/package.json +1 -1
- package/src/check.js +46 -1
- package/types/check.d.ts +2 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ npx sola build App.sola
|
|
|
36
36
|
npx sola-check src/
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
`sola-check` validates `.sola` files on disk (pass `--json` for machine-readable output), reporting syntax errors (`SOLA000`) and unbalanced or malformed `{#if}` / `{#each}` / `{:else}` blocks (`SOLA001`).
|
|
39
|
+
`sola-check` validates `.sola` files on disk (pass `--json` for machine-readable output), reporting syntax errors (`SOLA000`) and unbalanced or malformed `{#if}` / `{#each}` / `{:else}` blocks (`SOLA001`), and state written with `.set()` or `.update()` instead of assignment (`SOLA002`), which compiles but throws when it runs.
|
|
40
40
|
|
|
41
41
|
## What a `.sola` file looks like
|
|
42
42
|
|
package/package.json
CHANGED
package/src/check.js
CHANGED
|
@@ -11,9 +11,52 @@ import { compile } from './index.js';
|
|
|
11
11
|
|
|
12
12
|
export const RULES = {
|
|
13
13
|
SOLA000: 'compile error',
|
|
14
|
-
SOLA001: 'unbalanced block'
|
|
14
|
+
SOLA001: 'unbalanced block',
|
|
15
|
+
SOLA002: 'state written with .set() or .update()'
|
|
15
16
|
};
|
|
16
17
|
|
|
18
|
+
// Text of every <script> body. Plain index search, so `</script >` and odd
|
|
19
|
+
// casing are handled the same way the browser would end the element.
|
|
20
|
+
function scriptBodies(source) {
|
|
21
|
+
const lower = source.toLowerCase();
|
|
22
|
+
const bodies = [];
|
|
23
|
+
let from = 0;
|
|
24
|
+
for (;;) {
|
|
25
|
+
const open = lower.indexOf('<script', from);
|
|
26
|
+
if (open < 0) break;
|
|
27
|
+
const start = lower.indexOf('>', open);
|
|
28
|
+
if (start < 0) break;
|
|
29
|
+
const end = lower.indexOf('</script', start);
|
|
30
|
+
bodies.push(source.slice(start + 1, end < 0 ? source.length : end));
|
|
31
|
+
if (end < 0) break;
|
|
32
|
+
from = end + 8;
|
|
33
|
+
}
|
|
34
|
+
return bodies.join('\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A `$state` / `$persist` name is its signal getter, so `count.set(1)` compiles
|
|
38
|
+
// to `count().set(1)` and throws when it runs. State is written by assignment.
|
|
39
|
+
function checkSetterCalls(source, add) {
|
|
40
|
+
const scripts = scriptBodies(source);
|
|
41
|
+
const names = new Set();
|
|
42
|
+
for (const m of scripts.matchAll(/\b(?:let|var|const)\s+([\w$]+)\s*=\s*\$(?:state|persist)\s*\(/g)) names.add(m[1]);
|
|
43
|
+
if (!names.size) return;
|
|
44
|
+
const blank = (m) => m.replace(/[^\n]/g, ' ');
|
|
45
|
+
const code = source
|
|
46
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, blank)
|
|
47
|
+
.replace(/<!--[\s\S]*?-->/g, blank)
|
|
48
|
+
.replace(/\/\*[\s\S]*?\*\//g, blank)
|
|
49
|
+
.replace(/(^|[^:])\/\/[^\n]*/g, (m, pre) => pre + blank(m.slice(pre.length)));
|
|
50
|
+
const call = /(?<![\w$.])([\w$]+)(\(\))?\s*\.\s*(set|update)\s*\(/g;
|
|
51
|
+
let m;
|
|
52
|
+
while ((m = call.exec(code))) {
|
|
53
|
+
const [, name, , method] = m;
|
|
54
|
+
if (!names.has(name)) continue;
|
|
55
|
+
const fix = method === 'set' ? `${name} = value` : `${name} = fn(${name})`;
|
|
56
|
+
add(m.index, 'error', 'SOLA002', `\`${name}.${method}()\` throws at runtime: \`${name}\` is state, which is written by assignment. Use \`${fix}\`.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
17
60
|
// Blank out <script> and <style> bodies and HTML comments, keeping newlines so
|
|
18
61
|
// line numbers hold. A commented-out block is not a block.
|
|
19
62
|
function maskNonTemplate(source) {
|
|
@@ -68,6 +111,8 @@ export function checkSource(source, filename = 'component.sola') {
|
|
|
68
111
|
});
|
|
69
112
|
}
|
|
70
113
|
|
|
114
|
+
checkSetterCalls(source, add);
|
|
115
|
+
|
|
71
116
|
const template = maskNonTemplate(source);
|
|
72
117
|
const tag = /\{(#if|#each|\/if|\/each|:else)(?![\w])/g;
|
|
73
118
|
const stack = [];
|
package/types/check.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export interface Diagnostic {
|
|
|
7
7
|
/** 1-based. */
|
|
8
8
|
column: number;
|
|
9
9
|
severity: 'error' | 'warning';
|
|
10
|
-
/** SOLA000 compile error, SOLA001 unbalanced block. */
|
|
11
|
-
code: 'SOLA000' | 'SOLA001';
|
|
10
|
+
/** SOLA000 compile error, SOLA001 unbalanced block, SOLA002 state written with .set() or .update(). */
|
|
11
|
+
code: 'SOLA000' | 'SOLA001' | 'SOLA002';
|
|
12
12
|
message: string;
|
|
13
13
|
}
|
|
14
14
|
|