ucode-agent 1.19.1 → 1.20.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/package.json +1 -1
- package/src/core/htmlcheck.js +59 -0
- package/src/core/loop.js +2289 -2277
package/package.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* htmlcheck.js — the JavaScript inside an HTML file is still JavaScript.
|
|
3
|
+
*
|
|
4
|
+
* A single-file app keeps everything in one <script>, and nothing was looking
|
|
5
|
+
* at it: the checks run `node --check` on .js files and tsc on TypeScript
|
|
6
|
+
* projects, so an index.html whose script does not parse passed every one of
|
|
7
|
+
* them. The page renders, the CSS is right, and not one button works.
|
|
8
|
+
*
|
|
9
|
+
* The failure that prompted this: a model writing an HTML-escaping map inside
|
|
10
|
+
* an HTML file produced `"'":'''` — three quotes, a syntax error, the whole
|
|
11
|
+
* script dead. Everything looked finished.
|
|
12
|
+
*
|
|
13
|
+
* Parsing is Babel's, which ucode already carries. Only syntax is checked:
|
|
14
|
+
* an undefined variable is a runtime problem and this is not the place for it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { parse } from '@babel/parser';
|
|
18
|
+
|
|
19
|
+
/** Inline scripts worth parsing: not src=, not JSON, not a template. */
|
|
20
|
+
const SCRIPTS = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
|
|
21
|
+
|
|
22
|
+
const runnable = (attrs) => {
|
|
23
|
+
if (/\bsrc\s*=/i.test(attrs)) return false; // a separate file, checked on its own
|
|
24
|
+
const type = /\btype\s*=\s*["']?([^"'\s>]+)/i.exec(attrs)?.[1]?.toLowerCase();
|
|
25
|
+
if (!type) return true;
|
|
26
|
+
return type === 'module' || type === 'text/javascript' || type === 'application/javascript';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Syntax errors in a file's inline scripts, with lines counted in the HTML
|
|
31
|
+
* rather than in the extracted fragment — a line number that does not match
|
|
32
|
+
* the file is worse than none.
|
|
33
|
+
*/
|
|
34
|
+
export function checkHtml(text) {
|
|
35
|
+
const html = String(text ?? '');
|
|
36
|
+
const problems = [];
|
|
37
|
+
|
|
38
|
+
for (const match of html.matchAll(SCRIPTS)) {
|
|
39
|
+
const [whole, attrs, body] = match;
|
|
40
|
+
if (!runnable(attrs) || !body.trim()) continue;
|
|
41
|
+
|
|
42
|
+
const before = html.slice(0, match.index + whole.indexOf(body));
|
|
43
|
+
const offset = before.split('\n').length - 1;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
parse(body, {
|
|
47
|
+
sourceType: 'module', // accepts a plain script too
|
|
48
|
+
allowReturnOutsideFunction: true,
|
|
49
|
+
errorRecovery: false,
|
|
50
|
+
plugins: ['topLevelAwait'],
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const line = (err.loc?.line ?? 1) + offset;
|
|
54
|
+
problems.push({ line, message: String(err.message ?? err).replace(/\s*\(\d+:\d+\)$/, '') });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return problems;
|
|
59
|
+
}
|