@fluixi/template-parser 0.1.0-alpha.3 → 1.0.0-alpha.100
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/README.md +9 -7
- 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 +6 -5
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
|
+
};
|
package/dist/directives.cjs
CHANGED
|
@@ -1 +1,206 @@
|
|
|
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/directives.ts
|
|
21
|
+
var directives_exports = {};
|
|
22
|
+
__export(directives_exports, {
|
|
23
|
+
CORE_DIRECTIVES: () => CORE_DIRECTIVES,
|
|
24
|
+
EVENT_MODIFIERS: () => EVENT_MODIFIERS,
|
|
25
|
+
resolveAttribute: () => resolveAttribute
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(directives_exports);
|
|
28
|
+
var EVENT_MODIFIERS = /* @__PURE__ */ new Set(["capture", "once", "passive", "prevent", "stop", "self"]);
|
|
29
|
+
function requireHole(raw, what) {
|
|
30
|
+
if (raw.value && raw.value.kind === "hole") return raw.value.hole;
|
|
31
|
+
raw.report("invalid-binding", `${what} requires an expression value: ${raw.name}=\${…}`);
|
|
32
|
+
return -1;
|
|
33
|
+
}
|
|
34
|
+
var attribute = (raw, boolean) => ({
|
|
35
|
+
kind: "Attribute",
|
|
36
|
+
name: boolean ? raw.name.slice(1) : raw.name,
|
|
37
|
+
value: raw.value,
|
|
38
|
+
boolean,
|
|
39
|
+
loc: raw.loc
|
|
40
|
+
});
|
|
41
|
+
var CORE_DIRECTIVES = [
|
|
42
|
+
// Events: `@click=${h}`, `on:click=${h}` (native), `onClick=${h}`, each with
|
|
43
|
+
// optional dotted modifiers (`@click.capture.once`, `on:input.passive`).
|
|
44
|
+
{
|
|
45
|
+
id: "event",
|
|
46
|
+
match: (n) => n[0] === "@" || n.startsWith("on:") || /^on[A-Z]/.test(n),
|
|
47
|
+
build: (raw) => {
|
|
48
|
+
let syntax;
|
|
49
|
+
let rest;
|
|
50
|
+
if (raw.name[0] === "@") {
|
|
51
|
+
syntax = "at";
|
|
52
|
+
rest = raw.name.slice(1);
|
|
53
|
+
} else if (raw.name.startsWith("on:")) {
|
|
54
|
+
syntax = "colon";
|
|
55
|
+
rest = raw.name.slice(3);
|
|
56
|
+
} else {
|
|
57
|
+
syntax = "on";
|
|
58
|
+
rest = raw.name.slice(2);
|
|
59
|
+
}
|
|
60
|
+
const [raw_name, ...modifiers] = rest.split(".");
|
|
61
|
+
const name = raw_name.toLowerCase();
|
|
62
|
+
for (const m of modifiers) {
|
|
63
|
+
if (!EVENT_MODIFIERS.has(m)) {
|
|
64
|
+
raw.report("invalid-directive", `Unknown event modifier ".${m}" — expected one of ${[...EVENT_MODIFIERS].join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
kind: "EventBinding",
|
|
69
|
+
name,
|
|
70
|
+
syntax,
|
|
71
|
+
modifiers,
|
|
72
|
+
hole: requireHole(raw, "event binding"),
|
|
73
|
+
loc: raw.loc
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
// Two-way binding: `bind:value=${signal}`.
|
|
78
|
+
{
|
|
79
|
+
id: "bind",
|
|
80
|
+
elementOnly: true,
|
|
81
|
+
match: (n) => n.startsWith("bind:"),
|
|
82
|
+
build: (raw) => ({
|
|
83
|
+
kind: "BindDirective",
|
|
84
|
+
name: raw.name.slice("bind:".length),
|
|
85
|
+
hole: requireHole(raw, "two-way binding"),
|
|
86
|
+
loc: raw.loc
|
|
87
|
+
})
|
|
88
|
+
},
|
|
89
|
+
// Dynamic class toggle: `class:active=${flag}`.
|
|
90
|
+
{
|
|
91
|
+
id: "class-directive",
|
|
92
|
+
elementOnly: true,
|
|
93
|
+
match: (n) => n.startsWith("class:"),
|
|
94
|
+
build: (raw) => ({
|
|
95
|
+
kind: "ClassDirective",
|
|
96
|
+
name: raw.name.slice("class:".length),
|
|
97
|
+
hole: requireHole(raw, "class directive"),
|
|
98
|
+
loc: raw.loc
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
// Dynamic style: `style:color=${c}`.
|
|
102
|
+
{
|
|
103
|
+
id: "style-directive",
|
|
104
|
+
elementOnly: true,
|
|
105
|
+
match: (n) => n.startsWith("style:"),
|
|
106
|
+
build: (raw) => ({
|
|
107
|
+
kind: "StyleDirective",
|
|
108
|
+
name: raw.name.slice("style:".length),
|
|
109
|
+
hole: requireHole(raw, "style directive"),
|
|
110
|
+
loc: raw.loc
|
|
111
|
+
})
|
|
112
|
+
},
|
|
113
|
+
// Load strategy: `load:visible[="200px"]`. Components only, an intrinsic element
|
|
114
|
+
// has no module to defer.
|
|
115
|
+
{
|
|
116
|
+
id: "load",
|
|
117
|
+
match: (n) => n.startsWith("load:"),
|
|
118
|
+
build: (raw) => ({
|
|
119
|
+
kind: "LoadDirective",
|
|
120
|
+
strategy: raw.name.slice("load:".length),
|
|
121
|
+
modifier: raw.value && raw.value.kind === "static" ? raw.value.value : null,
|
|
122
|
+
loc: raw.loc
|
|
123
|
+
})
|
|
124
|
+
},
|
|
125
|
+
// Custom directive: `use=${d}` or `use:tooltip[=${opts}]`.
|
|
126
|
+
{
|
|
127
|
+
id: "use",
|
|
128
|
+
match: (n) => n === "use" || n.startsWith("use:"),
|
|
129
|
+
build: (raw) => {
|
|
130
|
+
const named = raw.name.startsWith("use:");
|
|
131
|
+
return {
|
|
132
|
+
kind: "UseDirective",
|
|
133
|
+
name: named ? raw.name.slice("use:".length) : null,
|
|
134
|
+
hole: raw.value && raw.value.kind === "hole" ? raw.value.hole : null,
|
|
135
|
+
loc: raw.loc
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
// Conditional rendering: `if=${cond}`.
|
|
140
|
+
{
|
|
141
|
+
id: "if",
|
|
142
|
+
elementOnly: true,
|
|
143
|
+
match: (n) => n === "if",
|
|
144
|
+
build: (raw) => ({
|
|
145
|
+
kind: "IfDirective",
|
|
146
|
+
hole: requireHole(raw, "if directive"),
|
|
147
|
+
loc: raw.loc
|
|
148
|
+
})
|
|
149
|
+
},
|
|
150
|
+
// Loop rendering: `each=${items}` (a sibling `key` folds in at parse time).
|
|
151
|
+
{
|
|
152
|
+
id: "each",
|
|
153
|
+
elementOnly: true,
|
|
154
|
+
match: (n) => n === "each",
|
|
155
|
+
build: (raw) => ({
|
|
156
|
+
kind: "EachDirective",
|
|
157
|
+
hole: requireHole(raw, "each directive"),
|
|
158
|
+
key: null,
|
|
159
|
+
loc: raw.loc
|
|
160
|
+
})
|
|
161
|
+
},
|
|
162
|
+
// `else` (valueless).
|
|
163
|
+
{
|
|
164
|
+
id: "else",
|
|
165
|
+
elementOnly: true,
|
|
166
|
+
match: (n) => n === "else",
|
|
167
|
+
build: (raw) => {
|
|
168
|
+
if (raw.value !== null) raw.report("invalid-directive", "`else` takes no value");
|
|
169
|
+
return { kind: "ElseDirective", loc: raw.loc };
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
// Ref binding: `ref=${el}`.
|
|
173
|
+
{
|
|
174
|
+
id: "ref",
|
|
175
|
+
match: (n) => n === "ref",
|
|
176
|
+
build: (raw) => ({
|
|
177
|
+
kind: "RefBinding",
|
|
178
|
+
hole: requireHole(raw, "ref binding"),
|
|
179
|
+
loc: raw.loc
|
|
180
|
+
})
|
|
181
|
+
},
|
|
182
|
+
// Property binding: `.currentTime=${t}`.
|
|
183
|
+
{
|
|
184
|
+
id: "property",
|
|
185
|
+
match: (n) => n[0] === ".",
|
|
186
|
+
build: (raw) => ({
|
|
187
|
+
kind: "PropertyBinding",
|
|
188
|
+
name: raw.name.slice(1),
|
|
189
|
+
hole: requireHole(raw, "property binding"),
|
|
190
|
+
loc: raw.loc
|
|
191
|
+
})
|
|
192
|
+
},
|
|
193
|
+
// Boolean attribute (lit `?disabled`): a plain attribute flagged boolean.
|
|
194
|
+
{
|
|
195
|
+
id: "boolean",
|
|
196
|
+
match: (n) => n[0] === "?",
|
|
197
|
+
build: (raw) => attribute(raw, true)
|
|
198
|
+
}
|
|
199
|
+
];
|
|
200
|
+
function resolveAttribute(raw, directives = CORE_DIRECTIVES, ctx = { component: false }) {
|
|
201
|
+
for (const d of directives) {
|
|
202
|
+
if (d.elementOnly && ctx.component) continue;
|
|
203
|
+
if (d.match(raw.name)) return d.build(raw);
|
|
204
|
+
}
|
|
205
|
+
return attribute(raw, false);
|
|
206
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directive registry: the extensibility seam.
|
|
3
|
+
*
|
|
4
|
+
* The core parser never hard-codes what `if`, `each`, `bind:value`, `@click`,
|
|
5
|
+
* `.prop` etc. mean. It parses a raw `name[=value]` pair, then asks the registry
|
|
6
|
+
* to turn it into a typed node. Adding future syntax is therefore a new
|
|
7
|
+
* descriptor here (or registered by a downstream tool), never a change to the
|
|
8
|
+
* parser's control flow. Descriptors are tried in order; the first whose
|
|
9
|
+
* `match` returns true owns the attribute. Anything unmatched falls back to a
|
|
10
|
+
* plain {@link AttributeNode}.
|
|
11
|
+
*/
|
|
12
|
+
import type { AttributeLikeNode, AttrValue, Diagnostic, DiagnosticCode, SourceSpan } from './ast.cjs';
|
|
13
|
+
/** Recognized event modifiers (`@click.capture.once`, `on:input.passive`, …). */
|
|
14
|
+
export declare const EVENT_MODIFIERS: Set<string>;
|
|
15
|
+
/** A parsed-but-unclassified attribute handed to the registry. */
|
|
16
|
+
export interface RawAttribute {
|
|
17
|
+
/** Name exactly as written (`class`, `@click`, `bind:value`, `?disabled`). */
|
|
18
|
+
name: string;
|
|
19
|
+
nameLoc: SourceSpan;
|
|
20
|
+
/** null = valueless attribute. */
|
|
21
|
+
value: AttrValue | null;
|
|
22
|
+
loc: SourceSpan;
|
|
23
|
+
/** Emit a located diagnostic from inside a descriptor. */
|
|
24
|
+
report(code: DiagnosticCode, message: string): void;
|
|
25
|
+
}
|
|
26
|
+
export interface DirectiveDescriptor {
|
|
27
|
+
/** Stable id (for docs/debugging). */
|
|
28
|
+
id: string;
|
|
29
|
+
/** DOM-only sugar (`if`/`each`/`class:`…) is ignored on component tags. */
|
|
30
|
+
elementOnly?: boolean;
|
|
31
|
+
match(name: string): boolean;
|
|
32
|
+
build(raw: RawAttribute): AttributeLikeNode;
|
|
33
|
+
}
|
|
34
|
+
/** Where an attribute sits: a native element or a component tag. */
|
|
35
|
+
export interface AttrContext {
|
|
36
|
+
component: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The built-in directives. Order matters: prefix forms are matched before the
|
|
40
|
+
* plain-attribute fallback. Downstream tools can prepend their own.
|
|
41
|
+
*/
|
|
42
|
+
export declare const CORE_DIRECTIVES: DirectiveDescriptor[];
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a raw attribute to a typed node using `directives` (defaults to the
|
|
45
|
+
* core set). `each`/`key` are handled by the parser afterward because `key` is
|
|
46
|
+
* a sibling attribute that folds into the preceding `each` node.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveAttribute(raw: RawAttribute, directives?: DirectiveDescriptor[], ctx?: AttrContext): AttributeLikeNode;
|
|
49
|
+
export type { Diagnostic };
|
package/dist/directives.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The directive registry
|
|
2
|
+
* The directive registry: the extensibility seam.
|
|
3
3
|
*
|
|
4
4
|
* The core parser never hard-codes what `if`, `each`, `bind:value`, `@click`,
|
|
5
5
|
* `.prop` etc. mean. It parses a raw `name[=value]` pair, then asks the registry
|
|
@@ -31,7 +31,7 @@ export interface DirectiveDescriptor {
|
|
|
31
31
|
match(name: string): boolean;
|
|
32
32
|
build(raw: RawAttribute): AttributeLikeNode;
|
|
33
33
|
}
|
|
34
|
-
/** Where an attribute sits
|
|
34
|
+
/** Where an attribute sits: a native element or a component tag. */
|
|
35
35
|
export interface AttrContext {
|
|
36
36
|
component: boolean;
|
|
37
37
|
}
|
package/dist/directives.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directives.d.ts","sourceRoot":"","sources":["../src/directives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EACV,iBAAiB,EAEjB,SAAS,EAGT,UAAU,EACV,cAAc,EAOd,UAAU,EAIX,MAAM,UAAU,CAAC;AAElB,iFAAiF;AACjF,eAAO,MAAM,eAAe,aAAqE,CAAC;AAElG,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC;IACpB,kCAAkC;IAClC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,UAAU,CAAC;IAChB,0DAA0D;IAC1D,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAAC;CAC7C;AAED,
|
|
1
|
+
{"version":3,"file":"directives.d.ts","sourceRoot":"","sources":["../src/directives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EACV,iBAAiB,EAEjB,SAAS,EAGT,UAAU,EACV,cAAc,EAOd,UAAU,EAIX,MAAM,UAAU,CAAC;AAElB,iFAAiF;AACjF,eAAO,MAAM,eAAe,aAAqE,CAAC;AAElG,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC;IACpB,kCAAkC;IAClC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,UAAU,CAAC;IAChB,0DAA0D;IAC1D,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAAC;CAC7C;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAkBD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,mBAAmB,EA4JhD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,YAAY,EACjB,UAAU,GAAE,mBAAmB,EAAoB,EACnD,GAAG,GAAE,WAAkC,GACtC,iBAAiB,CAMnB;AAED,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/directives.js
CHANGED
|
@@ -42,7 +42,7 @@ export const CORE_DIRECTIVES = [
|
|
|
42
42
|
}
|
|
43
43
|
// Dotted modifiers apply to the @/on: forms; the camelCase name has no dots.
|
|
44
44
|
// Lowercase the name so `onClick`, `@click` and `on:click` all yield `click`
|
|
45
|
-
|
|
45
|
+
//: the AST documents this field as the DOM event name, and a consumer that
|
|
46
46
|
// trusts that shouldn't have to know which spelling was written. (`syntax`
|
|
47
47
|
// still records the spelling; the ts-plugin reads the written form from source.)
|
|
48
48
|
const [raw_name, ...modifiers] = rest.split('.');
|
|
@@ -98,7 +98,7 @@ export const CORE_DIRECTIVES = [
|
|
|
98
98
|
loc: raw.loc,
|
|
99
99
|
}),
|
|
100
100
|
},
|
|
101
|
-
// Load strategy: `load:visible[="200px"]`. Components only
|
|
101
|
+
// Load strategy: `load:visible[="200px"]`. Components only, an intrinsic element
|
|
102
102
|
// has no module to defer.
|
|
103
103
|
{
|
|
104
104
|
id: 'load',
|
package/dist/directives.mjs
CHANGED
|
@@ -1 +1,185 @@
|
|
|
1
|
-
|
|
1
|
+
// src/directives.ts
|
|
2
|
+
var EVENT_MODIFIERS = /* @__PURE__ */ new Set(["capture", "once", "passive", "prevent", "stop", "self"]);
|
|
3
|
+
function requireHole(raw, what) {
|
|
4
|
+
if (raw.value && raw.value.kind === "hole") return raw.value.hole;
|
|
5
|
+
raw.report("invalid-binding", `${what} requires an expression value: ${raw.name}=\${…}`);
|
|
6
|
+
return -1;
|
|
7
|
+
}
|
|
8
|
+
var attribute = (raw, boolean) => ({
|
|
9
|
+
kind: "Attribute",
|
|
10
|
+
name: boolean ? raw.name.slice(1) : raw.name,
|
|
11
|
+
value: raw.value,
|
|
12
|
+
boolean,
|
|
13
|
+
loc: raw.loc
|
|
14
|
+
});
|
|
15
|
+
var CORE_DIRECTIVES = [
|
|
16
|
+
// Events: `@click=${h}`, `on:click=${h}` (native), `onClick=${h}`, each with
|
|
17
|
+
// optional dotted modifiers (`@click.capture.once`, `on:input.passive`).
|
|
18
|
+
{
|
|
19
|
+
id: "event",
|
|
20
|
+
match: (n) => n[0] === "@" || n.startsWith("on:") || /^on[A-Z]/.test(n),
|
|
21
|
+
build: (raw) => {
|
|
22
|
+
let syntax;
|
|
23
|
+
let rest;
|
|
24
|
+
if (raw.name[0] === "@") {
|
|
25
|
+
syntax = "at";
|
|
26
|
+
rest = raw.name.slice(1);
|
|
27
|
+
} else if (raw.name.startsWith("on:")) {
|
|
28
|
+
syntax = "colon";
|
|
29
|
+
rest = raw.name.slice(3);
|
|
30
|
+
} else {
|
|
31
|
+
syntax = "on";
|
|
32
|
+
rest = raw.name.slice(2);
|
|
33
|
+
}
|
|
34
|
+
const [raw_name, ...modifiers] = rest.split(".");
|
|
35
|
+
const name = raw_name.toLowerCase();
|
|
36
|
+
for (const m of modifiers) {
|
|
37
|
+
if (!EVENT_MODIFIERS.has(m)) {
|
|
38
|
+
raw.report("invalid-directive", `Unknown event modifier ".${m}" — expected one of ${[...EVENT_MODIFIERS].join(", ")}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
kind: "EventBinding",
|
|
43
|
+
name,
|
|
44
|
+
syntax,
|
|
45
|
+
modifiers,
|
|
46
|
+
hole: requireHole(raw, "event binding"),
|
|
47
|
+
loc: raw.loc
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
// Two-way binding: `bind:value=${signal}`.
|
|
52
|
+
{
|
|
53
|
+
id: "bind",
|
|
54
|
+
elementOnly: true,
|
|
55
|
+
match: (n) => n.startsWith("bind:"),
|
|
56
|
+
build: (raw) => ({
|
|
57
|
+
kind: "BindDirective",
|
|
58
|
+
name: raw.name.slice("bind:".length),
|
|
59
|
+
hole: requireHole(raw, "two-way binding"),
|
|
60
|
+
loc: raw.loc
|
|
61
|
+
})
|
|
62
|
+
},
|
|
63
|
+
// Dynamic class toggle: `class:active=${flag}`.
|
|
64
|
+
{
|
|
65
|
+
id: "class-directive",
|
|
66
|
+
elementOnly: true,
|
|
67
|
+
match: (n) => n.startsWith("class:"),
|
|
68
|
+
build: (raw) => ({
|
|
69
|
+
kind: "ClassDirective",
|
|
70
|
+
name: raw.name.slice("class:".length),
|
|
71
|
+
hole: requireHole(raw, "class directive"),
|
|
72
|
+
loc: raw.loc
|
|
73
|
+
})
|
|
74
|
+
},
|
|
75
|
+
// Dynamic style: `style:color=${c}`.
|
|
76
|
+
{
|
|
77
|
+
id: "style-directive",
|
|
78
|
+
elementOnly: true,
|
|
79
|
+
match: (n) => n.startsWith("style:"),
|
|
80
|
+
build: (raw) => ({
|
|
81
|
+
kind: "StyleDirective",
|
|
82
|
+
name: raw.name.slice("style:".length),
|
|
83
|
+
hole: requireHole(raw, "style directive"),
|
|
84
|
+
loc: raw.loc
|
|
85
|
+
})
|
|
86
|
+
},
|
|
87
|
+
// Load strategy: `load:visible[="200px"]`. Components only, an intrinsic element
|
|
88
|
+
// has no module to defer.
|
|
89
|
+
{
|
|
90
|
+
id: "load",
|
|
91
|
+
match: (n) => n.startsWith("load:"),
|
|
92
|
+
build: (raw) => ({
|
|
93
|
+
kind: "LoadDirective",
|
|
94
|
+
strategy: raw.name.slice("load:".length),
|
|
95
|
+
modifier: raw.value && raw.value.kind === "static" ? raw.value.value : null,
|
|
96
|
+
loc: raw.loc
|
|
97
|
+
})
|
|
98
|
+
},
|
|
99
|
+
// Custom directive: `use=${d}` or `use:tooltip[=${opts}]`.
|
|
100
|
+
{
|
|
101
|
+
id: "use",
|
|
102
|
+
match: (n) => n === "use" || n.startsWith("use:"),
|
|
103
|
+
build: (raw) => {
|
|
104
|
+
const named = raw.name.startsWith("use:");
|
|
105
|
+
return {
|
|
106
|
+
kind: "UseDirective",
|
|
107
|
+
name: named ? raw.name.slice("use:".length) : null,
|
|
108
|
+
hole: raw.value && raw.value.kind === "hole" ? raw.value.hole : null,
|
|
109
|
+
loc: raw.loc
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
// Conditional rendering: `if=${cond}`.
|
|
114
|
+
{
|
|
115
|
+
id: "if",
|
|
116
|
+
elementOnly: true,
|
|
117
|
+
match: (n) => n === "if",
|
|
118
|
+
build: (raw) => ({
|
|
119
|
+
kind: "IfDirective",
|
|
120
|
+
hole: requireHole(raw, "if directive"),
|
|
121
|
+
loc: raw.loc
|
|
122
|
+
})
|
|
123
|
+
},
|
|
124
|
+
// Loop rendering: `each=${items}` (a sibling `key` folds in at parse time).
|
|
125
|
+
{
|
|
126
|
+
id: "each",
|
|
127
|
+
elementOnly: true,
|
|
128
|
+
match: (n) => n === "each",
|
|
129
|
+
build: (raw) => ({
|
|
130
|
+
kind: "EachDirective",
|
|
131
|
+
hole: requireHole(raw, "each directive"),
|
|
132
|
+
key: null,
|
|
133
|
+
loc: raw.loc
|
|
134
|
+
})
|
|
135
|
+
},
|
|
136
|
+
// `else` (valueless).
|
|
137
|
+
{
|
|
138
|
+
id: "else",
|
|
139
|
+
elementOnly: true,
|
|
140
|
+
match: (n) => n === "else",
|
|
141
|
+
build: (raw) => {
|
|
142
|
+
if (raw.value !== null) raw.report("invalid-directive", "`else` takes no value");
|
|
143
|
+
return { kind: "ElseDirective", loc: raw.loc };
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
// Ref binding: `ref=${el}`.
|
|
147
|
+
{
|
|
148
|
+
id: "ref",
|
|
149
|
+
match: (n) => n === "ref",
|
|
150
|
+
build: (raw) => ({
|
|
151
|
+
kind: "RefBinding",
|
|
152
|
+
hole: requireHole(raw, "ref binding"),
|
|
153
|
+
loc: raw.loc
|
|
154
|
+
})
|
|
155
|
+
},
|
|
156
|
+
// Property binding: `.currentTime=${t}`.
|
|
157
|
+
{
|
|
158
|
+
id: "property",
|
|
159
|
+
match: (n) => n[0] === ".",
|
|
160
|
+
build: (raw) => ({
|
|
161
|
+
kind: "PropertyBinding",
|
|
162
|
+
name: raw.name.slice(1),
|
|
163
|
+
hole: requireHole(raw, "property binding"),
|
|
164
|
+
loc: raw.loc
|
|
165
|
+
})
|
|
166
|
+
},
|
|
167
|
+
// Boolean attribute (lit `?disabled`): a plain attribute flagged boolean.
|
|
168
|
+
{
|
|
169
|
+
id: "boolean",
|
|
170
|
+
match: (n) => n[0] === "?",
|
|
171
|
+
build: (raw) => attribute(raw, true)
|
|
172
|
+
}
|
|
173
|
+
];
|
|
174
|
+
function resolveAttribute(raw, directives = CORE_DIRECTIVES, ctx = { component: false }) {
|
|
175
|
+
for (const d of directives) {
|
|
176
|
+
if (d.elementOnly && ctx.component) continue;
|
|
177
|
+
if (d.match(raw.name)) return d.build(raw);
|
|
178
|
+
}
|
|
179
|
+
return attribute(raw, false);
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
CORE_DIRECTIVES,
|
|
183
|
+
EVENT_MODIFIERS,
|
|
184
|
+
resolveAttribute
|
|
185
|
+
};
|