@fluixi/template-parser 0.1.0-alpha.4 → 1.0.0-alpha.99
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/LICENSE +1 -1
- package/dist/a11y.cjs +100 -1
- package/dist/a11y.d.cts +21 -0
- package/dist/a11y.d.ts +2 -2
- package/dist/a11y.js +1 -1
- package/dist/a11y.mjs +79 -1
- package/dist/ast.cjs +46 -1
- package/dist/ast.d.cts +223 -0
- package/dist/ast.d.ts +9 -9
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js +2 -2
- package/dist/ast.mjs +25 -1
- package/dist/directives.cjs +206 -1
- package/dist/directives.d.cts +49 -0
- package/dist/directives.d.ts +2 -2
- package/dist/directives.d.ts.map +1 -1
- package/dist/directives.js +2 -2
- package/dist/directives.mjs +185 -1
- package/dist/index.cjs +857 -1
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.mjs +835 -1
- package/dist/parser.cjs +832 -1
- package/dist/parser.d.cts +27 -0
- package/dist/parser.d.ts +2 -2
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +0 -0
- package/dist/parser.mjs +809 -1
- package/dist/reader.cjs +122 -1
- package/dist/reader.d.cts +60 -0
- package/dist/reader.d.ts +2 -2
- package/dist/reader.mjs +101 -1
- package/dist/tags.cjs +77 -1
- package/dist/tags.d.cts +15 -0
- package/dist/tags.d.ts +1 -1
- package/dist/tags.d.ts.map +1 -1
- package/dist/tags.js +1 -1
- package/dist/tags.mjs +56 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +5 -4
package/LICENSE
CHANGED
package/dist/a11y.cjs
CHANGED
|
@@ -1 +1,100 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/a11y.ts
|
|
21
|
+
var a11y_exports = {};
|
|
22
|
+
__export(a11y_exports, {
|
|
23
|
+
checkAccessibility: () => checkAccessibility
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(a11y_exports);
|
|
26
|
+
var INTERACTIVE = /* @__PURE__ */ new Set([
|
|
27
|
+
"a",
|
|
28
|
+
"button",
|
|
29
|
+
"input",
|
|
30
|
+
"select",
|
|
31
|
+
"textarea",
|
|
32
|
+
"summary",
|
|
33
|
+
"details",
|
|
34
|
+
"option",
|
|
35
|
+
"label"
|
|
36
|
+
]);
|
|
37
|
+
var KEY_EVENTS = /* @__PURE__ */ new Set(["keydown", "keyup", "keypress"]);
|
|
38
|
+
function attributeNamed(node, name) {
|
|
39
|
+
return node.attributes.find((a) => "name" in a && a.name === name);
|
|
40
|
+
}
|
|
41
|
+
function hasSpread(node) {
|
|
42
|
+
return node.attributes.some((a) => a.kind === "Spread");
|
|
43
|
+
}
|
|
44
|
+
function staticValue(node, name) {
|
|
45
|
+
const attribute = attributeNamed(node, name);
|
|
46
|
+
if (!attribute || attribute.kind !== "Attribute" || !attribute.value) return void 0;
|
|
47
|
+
const value = attribute.value;
|
|
48
|
+
return value.kind === "static" ? value.value : void 0;
|
|
49
|
+
}
|
|
50
|
+
function isPresentational(node) {
|
|
51
|
+
const role = staticValue(node, "role");
|
|
52
|
+
if (role === "presentation" || role === "none") return true;
|
|
53
|
+
return staticValue(node, "aria-hidden") === "true";
|
|
54
|
+
}
|
|
55
|
+
function hasEvent(node, predicate) {
|
|
56
|
+
return node.attributes.some((a) => a.kind === "EventBinding" && predicate(a.name));
|
|
57
|
+
}
|
|
58
|
+
function checkAccessibility(root) {
|
|
59
|
+
const out = [];
|
|
60
|
+
const report = (node, code, message) => {
|
|
61
|
+
out.push({ code, message, severity: "warning", loc: node.loc });
|
|
62
|
+
};
|
|
63
|
+
const visit = (nodes) => {
|
|
64
|
+
for (const node of nodes) {
|
|
65
|
+
if (node.kind === "Element") check(node, report);
|
|
66
|
+
const children = "children" in node ? node.children : void 0;
|
|
67
|
+
if (children) visit(children);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
visit(root.children);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function check(node, report) {
|
|
74
|
+
if (hasSpread(node)) return;
|
|
75
|
+
const tag = node.tag.toLowerCase();
|
|
76
|
+
if (tag === "img" && !attributeNamed(node, "alt") && !isPresentational(node)) {
|
|
77
|
+
report(node, "a11y-img-alt", 'An `img` needs an `alt`. Use `alt=""` when the image is decorative.');
|
|
78
|
+
}
|
|
79
|
+
if (tag === "a" && !attributeNamed(node, "href") && !attributeNamed(node, "role")) {
|
|
80
|
+
report(node, "a11y-anchor-href", "An `a` without `href` is not a link. Use a `button` for an action.");
|
|
81
|
+
}
|
|
82
|
+
if (tag === "iframe" && !attributeNamed(node, "title")) {
|
|
83
|
+
report(node, "a11y-iframe-title", "An `iframe` needs a `title` describing its content.");
|
|
84
|
+
}
|
|
85
|
+
const tabindex = staticValue(node, "tabindex") ?? staticValue(node, "tabIndex");
|
|
86
|
+
if (tabindex !== void 0 && Number(tabindex) > 0) {
|
|
87
|
+
report(
|
|
88
|
+
node,
|
|
89
|
+
"a11y-positive-tabindex",
|
|
90
|
+
"A positive `tabindex` reorders tab navigation for the entire page. Use `0`, or restructure the markup."
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (!INTERACTIVE.has(tag) && hasEvent(node, (name) => name === "click") && !attributeNamed(node, "role") && !hasEvent(node, (name) => KEY_EVENTS.has(name))) {
|
|
94
|
+
report(
|
|
95
|
+
node,
|
|
96
|
+
"a11y-click-without-keyboard",
|
|
97
|
+
`A click handler on \`${tag}\` is unreachable by keyboard. Use a \`button\`, or add a \`role\` and a key handler.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
package/dist/a11y.d.cts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility rules over the template AST.
|
|
3
|
+
*
|
|
4
|
+
* These live in the parser, not in a consumer, because both the language service
|
|
5
|
+
* and the compiler already parse templates: a rule written here reaches the
|
|
6
|
+
* editor and the build from one implementation.
|
|
7
|
+
*
|
|
8
|
+
* Every rule is structural: no types, no scope analysis, no guessing. A rule that
|
|
9
|
+
* cannot be certain stays silent, because an accessibility warning that cries wolf
|
|
10
|
+
* gets switched off and then the real ones are lost too. That is why anything
|
|
11
|
+
* carrying a spread is skipped: the spread may supply the very attribute the rule
|
|
12
|
+
* is looking for, and there is no way to know from the template alone.
|
|
13
|
+
*/
|
|
14
|
+
import type { Diagnostic, RootNode } from './ast.cjs';
|
|
15
|
+
/**
|
|
16
|
+
* Collect accessibility diagnostics for a parsed template.
|
|
17
|
+
*
|
|
18
|
+
* Reported as warnings: they describe a real barrier, but none of them make the
|
|
19
|
+
* template invalid, and failing a build on them would be the wrong trade.
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkAccessibility(root: RootNode): Diagnostic[];
|
package/dist/a11y.d.ts
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Accessibility rules over the template AST.
|
|
3
3
|
*
|
|
4
4
|
* These live in the parser, not in a consumer, because both the language service
|
|
5
|
-
* and the compiler already parse templates
|
|
5
|
+
* and the compiler already parse templates: a rule written here reaches the
|
|
6
6
|
* editor and the build from one implementation.
|
|
7
7
|
*
|
|
8
8
|
* Every rule is structural: no types, no scope analysis, no guessing. A rule that
|
|
9
9
|
* cannot be certain stays silent, because an accessibility warning that cries wolf
|
|
10
10
|
* gets switched off and then the real ones are lost too. That is why anything
|
|
11
|
-
* carrying a spread is skipped
|
|
11
|
+
* carrying a spread is skipped: the spread may supply the very attribute the rule
|
|
12
12
|
* is looking for, and there is no way to know from the template alone.
|
|
13
13
|
*/
|
|
14
14
|
import type { Diagnostic, RootNode } from './ast.js';
|
package/dist/a11y.js
CHANGED
|
@@ -70,7 +70,7 @@ function check(node, report) {
|
|
|
70
70
|
report(node, 'a11y-iframe-title', 'An `iframe` needs a `title` describing its content.');
|
|
71
71
|
}
|
|
72
72
|
// A positive tabindex overrides document order for the whole page, not just
|
|
73
|
-
// this element
|
|
73
|
+
// this element: the classic way to make keyboard navigation unpredictable.
|
|
74
74
|
const tabindex = staticValue(node, 'tabindex') ?? staticValue(node, 'tabIndex');
|
|
75
75
|
if (tabindex !== undefined && Number(tabindex) > 0) {
|
|
76
76
|
report(node, 'a11y-positive-tabindex', 'A positive `tabindex` reorders tab navigation for the entire page. Use `0`, or restructure the markup.');
|
package/dist/a11y.mjs
CHANGED
|
@@ -1 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
// src/a11y.ts
|
|
2
|
+
var INTERACTIVE = /* @__PURE__ */ new Set([
|
|
3
|
+
"a",
|
|
4
|
+
"button",
|
|
5
|
+
"input",
|
|
6
|
+
"select",
|
|
7
|
+
"textarea",
|
|
8
|
+
"summary",
|
|
9
|
+
"details",
|
|
10
|
+
"option",
|
|
11
|
+
"label"
|
|
12
|
+
]);
|
|
13
|
+
var KEY_EVENTS = /* @__PURE__ */ new Set(["keydown", "keyup", "keypress"]);
|
|
14
|
+
function attributeNamed(node, name) {
|
|
15
|
+
return node.attributes.find((a) => "name" in a && a.name === name);
|
|
16
|
+
}
|
|
17
|
+
function hasSpread(node) {
|
|
18
|
+
return node.attributes.some((a) => a.kind === "Spread");
|
|
19
|
+
}
|
|
20
|
+
function staticValue(node, name) {
|
|
21
|
+
const attribute = attributeNamed(node, name);
|
|
22
|
+
if (!attribute || attribute.kind !== "Attribute" || !attribute.value) return void 0;
|
|
23
|
+
const value = attribute.value;
|
|
24
|
+
return value.kind === "static" ? value.value : void 0;
|
|
25
|
+
}
|
|
26
|
+
function isPresentational(node) {
|
|
27
|
+
const role = staticValue(node, "role");
|
|
28
|
+
if (role === "presentation" || role === "none") return true;
|
|
29
|
+
return staticValue(node, "aria-hidden") === "true";
|
|
30
|
+
}
|
|
31
|
+
function hasEvent(node, predicate) {
|
|
32
|
+
return node.attributes.some((a) => a.kind === "EventBinding" && predicate(a.name));
|
|
33
|
+
}
|
|
34
|
+
function checkAccessibility(root) {
|
|
35
|
+
const out = [];
|
|
36
|
+
const report = (node, code, message) => {
|
|
37
|
+
out.push({ code, message, severity: "warning", loc: node.loc });
|
|
38
|
+
};
|
|
39
|
+
const visit = (nodes) => {
|
|
40
|
+
for (const node of nodes) {
|
|
41
|
+
if (node.kind === "Element") check(node, report);
|
|
42
|
+
const children = "children" in node ? node.children : void 0;
|
|
43
|
+
if (children) visit(children);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
visit(root.children);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function check(node, report) {
|
|
50
|
+
if (hasSpread(node)) return;
|
|
51
|
+
const tag = node.tag.toLowerCase();
|
|
52
|
+
if (tag === "img" && !attributeNamed(node, "alt") && !isPresentational(node)) {
|
|
53
|
+
report(node, "a11y-img-alt", 'An `img` needs an `alt`. Use `alt=""` when the image is decorative.');
|
|
54
|
+
}
|
|
55
|
+
if (tag === "a" && !attributeNamed(node, "href") && !attributeNamed(node, "role")) {
|
|
56
|
+
report(node, "a11y-anchor-href", "An `a` without `href` is not a link. Use a `button` for an action.");
|
|
57
|
+
}
|
|
58
|
+
if (tag === "iframe" && !attributeNamed(node, "title")) {
|
|
59
|
+
report(node, "a11y-iframe-title", "An `iframe` needs a `title` describing its content.");
|
|
60
|
+
}
|
|
61
|
+
const tabindex = staticValue(node, "tabindex") ?? staticValue(node, "tabIndex");
|
|
62
|
+
if (tabindex !== void 0 && Number(tabindex) > 0) {
|
|
63
|
+
report(
|
|
64
|
+
node,
|
|
65
|
+
"a11y-positive-tabindex",
|
|
66
|
+
"A positive `tabindex` reorders tab navigation for the entire page. Use `0`, or restructure the markup."
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (!INTERACTIVE.has(tag) && hasEvent(node, (name) => name === "click") && !attributeNamed(node, "role") && !hasEvent(node, (name) => KEY_EVENTS.has(name))) {
|
|
70
|
+
report(
|
|
71
|
+
node,
|
|
72
|
+
"a11y-click-without-keyboard",
|
|
73
|
+
`A click handler on \`${tag}\` is unreachable by keyboard. Use a \`button\`, or add a \`role\` and a key handler.`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
checkAccessibility
|
|
79
|
+
};
|
package/dist/ast.cjs
CHANGED
|
@@ -1 +1,46 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ast.ts
|
|
21
|
+
var ast_exports = {};
|
|
22
|
+
__export(ast_exports, {
|
|
23
|
+
childrenOf: () => childrenOf,
|
|
24
|
+
setParents: () => setParents
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(ast_exports);
|
|
27
|
+
function setParents(root) {
|
|
28
|
+
const visit = (node, parent) => {
|
|
29
|
+
node.parent = parent;
|
|
30
|
+
const kids = childrenOf(node);
|
|
31
|
+
for (const child of kids) visit(child, node);
|
|
32
|
+
};
|
|
33
|
+
visit(root, null);
|
|
34
|
+
return root;
|
|
35
|
+
}
|
|
36
|
+
function childrenOf(node) {
|
|
37
|
+
switch (node.kind) {
|
|
38
|
+
case "Root":
|
|
39
|
+
case "Element":
|
|
40
|
+
case "Component":
|
|
41
|
+
case "Fragment":
|
|
42
|
+
return node.children;
|
|
43
|
+
default:
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/ast.d.cts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Template AST: the stable, host-agnostic contract this package exists to
|
|
3
|
+
* produce. It is deliberately NOT the compiler's codegen IR: it models the
|
|
4
|
+
* template *as written* (every directive is an explicit node, every node keeps
|
|
5
|
+
* a source span, whitespace is preserved) so it can power the compiler AND the
|
|
6
|
+
* language server, TypeScript plugin, and Biome/formatter alike.
|
|
7
|
+
*
|
|
8
|
+
* Design rules baked into these types:
|
|
9
|
+
* - Expression holes (`${…}`) are OPAQUE SLOTS. A node stores the hole's
|
|
10
|
+
* integer `index` (its position in the template's `expressions[]`) and its
|
|
11
|
+
* source span: never resolved JS. Each consumer binds a slot to its own
|
|
12
|
+
* representation afterward (a Babel node in the compiler, a `ts.Expression`
|
|
13
|
+
* in the LSP, raw text in Biome). This single seam is what lets one parser
|
|
14
|
+
* serve every downstream tool.
|
|
15
|
+
* - Directives are explicit node kinds, never stringly-typed attributes.
|
|
16
|
+
* - Every node carries `loc`, `kind`, and (via the tree walk) a `parent`.
|
|
17
|
+
*/
|
|
18
|
+
/** A half-open source span `[start, end)` in template-coordinate space. */
|
|
19
|
+
export interface SourceSpan {
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
}
|
|
23
|
+
/** Namespaces the parser tracks so SVG (and foreign content) lower correctly. */
|
|
24
|
+
export type Namespace = 'html' | 'svg' | 'mathml';
|
|
25
|
+
export type NodeKind = 'Root' | 'Element' | 'Component' | 'Fragment' | 'Text' | 'Comment' | 'Expression';
|
|
26
|
+
export type AttrKind = 'Attribute' | 'PropertyBinding' | 'EventBinding' | 'RefBinding' | 'Spread' | 'IfDirective' | 'ElseDirective' | 'EachDirective' | 'BindDirective' | 'ClassDirective' | 'StyleDirective' | 'UseDirective' | 'LoadDirective' | 'Slot';
|
|
27
|
+
/** Fields shared by every node. `parent` is wired by {@link setParents}. */
|
|
28
|
+
export interface NodeBase {
|
|
29
|
+
loc: SourceSpan;
|
|
30
|
+
parent?: TemplateNode | null;
|
|
31
|
+
/** Free-form bag for downstream passes (binding results, type info, etc.). */
|
|
32
|
+
metadata?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
export type TemplateNode = RootNode | ElementNode | ComponentNode | FragmentNode | TextNode | CommentNode | ExpressionNode;
|
|
35
|
+
/** Anything that can attach to an element/component opening tag. */
|
|
36
|
+
export type AttributeLikeNode = AttributeNode | PropertyBindingNode | EventBindingNode | RefBindingNode | SpreadNode | IfDirectiveNode | ElseDirectiveNode | EachDirectiveNode | BindDirectiveNode | ClassDirectiveNode | StyleDirectiveNode | UseDirectiveNode | LoadDirectiveNode | SlotNode;
|
|
37
|
+
export interface RootNode extends NodeBase {
|
|
38
|
+
kind: 'Root';
|
|
39
|
+
children: TemplateNode[];
|
|
40
|
+
}
|
|
41
|
+
export interface ElementNode extends NodeBase {
|
|
42
|
+
kind: 'Element';
|
|
43
|
+
tag: string;
|
|
44
|
+
namespace: Namespace;
|
|
45
|
+
attributes: AttributeLikeNode[];
|
|
46
|
+
children: TemplateNode[];
|
|
47
|
+
selfClosing: boolean;
|
|
48
|
+
/** Raw-text element (`script`/`style`/`textarea`/`title`), children kept verbatim. */
|
|
49
|
+
rawText: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ComponentNode extends NodeBase {
|
|
52
|
+
kind: 'Component';
|
|
53
|
+
/** Capitalized name or member path (`Ctx.Provider`) as written; '' for a dynamic tag. */
|
|
54
|
+
tag: string;
|
|
55
|
+
/**
|
|
56
|
+
* Dynamic tag `<${Comp} />`: the hole index of the component expression. When
|
|
57
|
+
* set, `tag` is '' and the component identity comes from this hole, which
|
|
58
|
+
* also means the referenced binding is visible to TypeScript (no "unused").
|
|
59
|
+
*/
|
|
60
|
+
tagHole: number | null;
|
|
61
|
+
attributes: AttributeLikeNode[];
|
|
62
|
+
children: TemplateNode[];
|
|
63
|
+
selfClosing: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface FragmentNode extends NodeBase {
|
|
66
|
+
kind: 'Fragment';
|
|
67
|
+
children: TemplateNode[];
|
|
68
|
+
}
|
|
69
|
+
/** Preserved verbatim (raw); whitespace normalization is a lowering concern. */
|
|
70
|
+
export interface TextNode extends NodeBase {
|
|
71
|
+
kind: 'Text';
|
|
72
|
+
value: string;
|
|
73
|
+
/** Inside a raw-text element: do not whitespace-normalize when lowering. */
|
|
74
|
+
raw: boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface CommentNode extends NodeBase {
|
|
77
|
+
kind: 'Comment';
|
|
78
|
+
value: string;
|
|
79
|
+
}
|
|
80
|
+
/** A `${…}` in child/text position. */
|
|
81
|
+
export interface ExpressionNode extends NodeBase {
|
|
82
|
+
kind: 'Expression';
|
|
83
|
+
hole: number;
|
|
84
|
+
}
|
|
85
|
+
/** A single interpolation part of an attribute value. */
|
|
86
|
+
export type ValuePart = {
|
|
87
|
+
text: string;
|
|
88
|
+
} | {
|
|
89
|
+
hole: number;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* `name`, `name="static"`, `name=${x}`, `name="a ${x} b"`, `?bool`, `?bool=${x}`.
|
|
93
|
+
* `value === null` is a valueless attribute (boolean true). `boolean` marks the
|
|
94
|
+
* lit `?name` prefix.
|
|
95
|
+
*/
|
|
96
|
+
export interface AttributeNode extends NodeBase {
|
|
97
|
+
kind: 'Attribute';
|
|
98
|
+
name: string;
|
|
99
|
+
value: AttrValue | null;
|
|
100
|
+
boolean: boolean;
|
|
101
|
+
}
|
|
102
|
+
export type AttrValue = {
|
|
103
|
+
kind: 'static';
|
|
104
|
+
value: string;
|
|
105
|
+
} | {
|
|
106
|
+
kind: 'hole';
|
|
107
|
+
hole: number;
|
|
108
|
+
} | {
|
|
109
|
+
kind: 'mixed';
|
|
110
|
+
parts: ValuePart[];
|
|
111
|
+
};
|
|
112
|
+
/** `.prop=${x}`: DOM property binding. */
|
|
113
|
+
export interface PropertyBindingNode extends NodeBase {
|
|
114
|
+
kind: 'PropertyBinding';
|
|
115
|
+
name: string;
|
|
116
|
+
hole: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* `@click=${h}`, `onClick=${h}`, or `on:click=${h}`, with optional dotted
|
|
120
|
+
* modifiers (`@click.capture.once`, `on:input.passive`). `syntax` is `'colon'`
|
|
121
|
+
* for the `on:name` form (a native listener), `'at'` for `@name`, `'on'` for the
|
|
122
|
+
* `onName` camelCase form.
|
|
123
|
+
*/
|
|
124
|
+
export interface EventBindingNode extends NodeBase {
|
|
125
|
+
kind: 'EventBinding';
|
|
126
|
+
/** Lowercased DOM event name (`click`). */
|
|
127
|
+
name: string;
|
|
128
|
+
syntax: 'at' | 'on' | 'colon';
|
|
129
|
+
/** Dotted modifiers: capture/once/passive (listener options) + prevent/stop/self. */
|
|
130
|
+
modifiers: string[];
|
|
131
|
+
hole: number;
|
|
132
|
+
}
|
|
133
|
+
/** `ref=${el}`. */
|
|
134
|
+
export interface RefBindingNode extends NodeBase {
|
|
135
|
+
kind: 'RefBinding';
|
|
136
|
+
hole: number;
|
|
137
|
+
}
|
|
138
|
+
/** `...${props}`. */
|
|
139
|
+
export interface SpreadNode extends NodeBase {
|
|
140
|
+
kind: 'Spread';
|
|
141
|
+
hole: number;
|
|
142
|
+
}
|
|
143
|
+
/** `if=${cond}`. */
|
|
144
|
+
export interface IfDirectiveNode extends NodeBase {
|
|
145
|
+
kind: 'IfDirective';
|
|
146
|
+
hole: number;
|
|
147
|
+
}
|
|
148
|
+
/** `else` (valueless). */
|
|
149
|
+
export interface ElseDirectiveNode extends NodeBase {
|
|
150
|
+
kind: 'ElseDirective';
|
|
151
|
+
}
|
|
152
|
+
/** `each=${items}` with optional `key="id"` / `key=${…}`. */
|
|
153
|
+
export interface EachDirectiveNode extends NodeBase {
|
|
154
|
+
kind: 'EachDirective';
|
|
155
|
+
hole: number;
|
|
156
|
+
key: {
|
|
157
|
+
static: string;
|
|
158
|
+
} | {
|
|
159
|
+
hole: number;
|
|
160
|
+
} | null;
|
|
161
|
+
}
|
|
162
|
+
/** `bind:value=${signal}`. */
|
|
163
|
+
export interface BindDirectiveNode extends NodeBase {
|
|
164
|
+
kind: 'BindDirective';
|
|
165
|
+
name: string;
|
|
166
|
+
hole: number;
|
|
167
|
+
}
|
|
168
|
+
/** `class:active=${flag}`. */
|
|
169
|
+
export interface ClassDirectiveNode extends NodeBase {
|
|
170
|
+
kind: 'ClassDirective';
|
|
171
|
+
name: string;
|
|
172
|
+
hole: number;
|
|
173
|
+
}
|
|
174
|
+
/** `style:color=${c}`. */
|
|
175
|
+
export interface StyleDirectiveNode extends NodeBase {
|
|
176
|
+
kind: 'StyleDirective';
|
|
177
|
+
name: string;
|
|
178
|
+
hole: number;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* `load:visible`, `load:interaction="click"`, …: when the component's module is
|
|
182
|
+
* fetched.
|
|
183
|
+
*
|
|
184
|
+
* The node carries the strategy and its literal modifier as written; turning that into
|
|
185
|
+
* a load plan is the compiler's job, so the parser stays free of module semantics ,
|
|
186
|
+
* the same reason resolution has no syntax here either.
|
|
187
|
+
*/
|
|
188
|
+
export interface LoadDirectiveNode extends NodeBase {
|
|
189
|
+
kind: 'LoadDirective';
|
|
190
|
+
/** The part after `load:`, unvalidated: the compiler reports unknown strategies. */
|
|
191
|
+
strategy: string;
|
|
192
|
+
/** `load:visible="200px"` → `'200px'`. Null for a bare directive. */
|
|
193
|
+
modifier: string | null;
|
|
194
|
+
}
|
|
195
|
+
/** `use=${directive}` or `use:tooltip` (optionally `use:tooltip=${opts}`). */
|
|
196
|
+
export interface UseDirectiveNode extends NodeBase {
|
|
197
|
+
kind: 'UseDirective';
|
|
198
|
+
/** Named form `use:tooltip` carries the name; bare `use=${d}` leaves it null. */
|
|
199
|
+
name: string | null;
|
|
200
|
+
hole: number | null;
|
|
201
|
+
}
|
|
202
|
+
/** `<slot>` element or `slot="name"` marker (reserved; wired in a later phase). */
|
|
203
|
+
export interface SlotNode extends NodeBase {
|
|
204
|
+
kind: 'Slot';
|
|
205
|
+
name: string | null;
|
|
206
|
+
}
|
|
207
|
+
export type Severity = 'error' | 'warning';
|
|
208
|
+
export interface Diagnostic {
|
|
209
|
+
code: DiagnosticCode;
|
|
210
|
+
message: string;
|
|
211
|
+
severity: Severity;
|
|
212
|
+
loc: SourceSpan;
|
|
213
|
+
}
|
|
214
|
+
export type DiagnosticCode = 'unclosed-tag' | 'mismatched-close-tag' | 'stray-close-tag' | 'unclosed-comment' | 'duplicate-directive' | 'else-without-if' | 'invalid-directive' | 'invalid-binding' | 'unsupported-combination' | 'a11y-img-alt' | 'a11y-anchor-href' | 'a11y-iframe-title' | 'a11y-positive-tabindex' | 'a11y-click-without-keyboard';
|
|
215
|
+
/** The full result of a parse: the tree plus any recovered-from problems. */
|
|
216
|
+
export interface ParseResult {
|
|
217
|
+
root: RootNode;
|
|
218
|
+
diagnostics: Diagnostic[];
|
|
219
|
+
}
|
|
220
|
+
/** Wire every node's `parent` pointer by walking the tree once. */
|
|
221
|
+
export declare function setParents(root: RootNode): RootNode;
|
|
222
|
+
/** The content children of a node (attributes are not content children). */
|
|
223
|
+
export declare function childrenOf(node: TemplateNode): TemplateNode[];
|
package/dist/ast.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Template AST
|
|
2
|
+
* The Template AST: the stable, host-agnostic contract this package exists to
|
|
3
3
|
* produce. It is deliberately NOT the compiler's codegen IR: it models the
|
|
4
4
|
* template *as written* (every directive is an explicit node, every node keeps
|
|
5
5
|
* a source span, whitespace is preserved) so it can power the compiler AND the
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Design rules baked into these types:
|
|
9
9
|
* - Expression holes (`${…}`) are OPAQUE SLOTS. A node stores the hole's
|
|
10
10
|
* integer `index` (its position in the template's `expressions[]`) and its
|
|
11
|
-
* source span
|
|
11
|
+
* source span: never resolved JS. Each consumer binds a slot to its own
|
|
12
12
|
* representation afterward (a Babel node in the compiler, a `ts.Expression`
|
|
13
13
|
* in the LSP, raw text in Biome). This single seam is what lets one parser
|
|
14
14
|
* serve every downstream tool.
|
|
@@ -45,7 +45,7 @@ export interface ElementNode extends NodeBase {
|
|
|
45
45
|
attributes: AttributeLikeNode[];
|
|
46
46
|
children: TemplateNode[];
|
|
47
47
|
selfClosing: boolean;
|
|
48
|
-
/** Raw-text element (`script`/`style`/`textarea`/`title`)
|
|
48
|
+
/** Raw-text element (`script`/`style`/`textarea`/`title`), children kept verbatim. */
|
|
49
49
|
rawText: boolean;
|
|
50
50
|
}
|
|
51
51
|
export interface ComponentNode extends NodeBase {
|
|
@@ -54,7 +54,7 @@ export interface ComponentNode extends NodeBase {
|
|
|
54
54
|
tag: string;
|
|
55
55
|
/**
|
|
56
56
|
* Dynamic tag `<${Comp} />`: the hole index of the component expression. When
|
|
57
|
-
* set, `tag` is '' and the component identity comes from this hole
|
|
57
|
+
* set, `tag` is '' and the component identity comes from this hole, which
|
|
58
58
|
* also means the referenced binding is visible to TypeScript (no "unused").
|
|
59
59
|
*/
|
|
60
60
|
tagHole: number | null;
|
|
@@ -70,7 +70,7 @@ export interface FragmentNode extends NodeBase {
|
|
|
70
70
|
export interface TextNode extends NodeBase {
|
|
71
71
|
kind: 'Text';
|
|
72
72
|
value: string;
|
|
73
|
-
/** Inside a raw-text element
|
|
73
|
+
/** Inside a raw-text element: do not whitespace-normalize when lowering. */
|
|
74
74
|
raw: boolean;
|
|
75
75
|
}
|
|
76
76
|
export interface CommentNode extends NodeBase {
|
|
@@ -109,7 +109,7 @@ export type AttrValue = {
|
|
|
109
109
|
kind: 'mixed';
|
|
110
110
|
parts: ValuePart[];
|
|
111
111
|
};
|
|
112
|
-
/** `.prop=${x}
|
|
112
|
+
/** `.prop=${x}`: DOM property binding. */
|
|
113
113
|
export interface PropertyBindingNode extends NodeBase {
|
|
114
114
|
kind: 'PropertyBinding';
|
|
115
115
|
name: string;
|
|
@@ -178,16 +178,16 @@ export interface StyleDirectiveNode extends NodeBase {
|
|
|
178
178
|
hole: number;
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
181
|
-
* `load:visible`, `load:interaction="click"`,
|
|
181
|
+
* `load:visible`, `load:interaction="click"`, …: when the component's module is
|
|
182
182
|
* fetched.
|
|
183
183
|
*
|
|
184
184
|
* The node carries the strategy and its literal modifier as written; turning that into
|
|
185
|
-
* a load plan is the compiler's job, so the parser stays free of module semantics
|
|
185
|
+
* a load plan is the compiler's job, so the parser stays free of module semantics ,
|
|
186
186
|
* the same reason resolution has no syntax here either.
|
|
187
187
|
*/
|
|
188
188
|
export interface LoadDirectiveNode extends NodeBase {
|
|
189
189
|
kind: 'LoadDirective';
|
|
190
|
-
/** The part after `load:`, unvalidated
|
|
190
|
+
/** The part after `load:`, unvalidated: the compiler reports unknown strategies. */
|
|
191
191
|
strategy: string;
|
|
192
192
|
/** `load:visible="200px"` → `'200px'`. Null for a bare directive. */
|
|
193
193
|
modifier: string | null;
|
package/dist/ast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,iFAAiF;AACjF,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAMlD,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,SAAS,GACT,WAAW,GACX,UAAU,GACV,MAAM,GACN,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,MAAM,QAAQ,GAChB,WAAW,GACX,iBAAiB,GACjB,cAAc,GACd,YAAY,GACZ,QAAQ,GACR,aAAa,GACb,eAAe,GACf,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,MAAM,CAAC;AAEX,4EAA4E;AAC5E,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,UAAU,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,WAAW,GACX,aAAa,GACb,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,cAAc,CAAC;AAEnB,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GACzB,aAAa,GACb,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,UAAU,GACV,eAAe,GACf,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,QAAQ,CAAC;AAMb,MAAM,WAAW,QAAS,SAAQ,QAAQ;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ;IAC3C,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
|
package/dist/ast.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Template AST
|
|
2
|
+
* The Template AST: the stable, host-agnostic contract this package exists to
|
|
3
3
|
* produce. It is deliberately NOT the compiler's codegen IR: it models the
|
|
4
4
|
* template *as written* (every directive is an explicit node, every node keeps
|
|
5
5
|
* a source span, whitespace is preserved) so it can power the compiler AND the
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Design rules baked into these types:
|
|
9
9
|
* - Expression holes (`${…}`) are OPAQUE SLOTS. A node stores the hole's
|
|
10
10
|
* integer `index` (its position in the template's `expressions[]`) and its
|
|
11
|
-
* source span
|
|
11
|
+
* source span: never resolved JS. Each consumer binds a slot to its own
|
|
12
12
|
* representation afterward (a Babel node in the compiler, a `ts.Expression`
|
|
13
13
|
* in the LSP, raw text in Biome). This single seam is what lets one parser
|
|
14
14
|
* serve every downstream tool.
|
package/dist/ast.mjs
CHANGED
|
@@ -1 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
// src/ast.ts
|
|
2
|
+
function setParents(root) {
|
|
3
|
+
const visit = (node, parent) => {
|
|
4
|
+
node.parent = parent;
|
|
5
|
+
const kids = childrenOf(node);
|
|
6
|
+
for (const child of kids) visit(child, node);
|
|
7
|
+
};
|
|
8
|
+
visit(root, null);
|
|
9
|
+
return root;
|
|
10
|
+
}
|
|
11
|
+
function childrenOf(node) {
|
|
12
|
+
switch (node.kind) {
|
|
13
|
+
case "Root":
|
|
14
|
+
case "Element":
|
|
15
|
+
case "Component":
|
|
16
|
+
case "Fragment":
|
|
17
|
+
return node.children;
|
|
18
|
+
default:
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
childrenOf,
|
|
24
|
+
setParents
|
|
25
|
+
};
|