@gyral/core 0.2.0 → 0.3.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/CHANGELOG.md +87 -0
- package/README.md +7 -5
- package/dist/children.d.ts +5 -2
- package/dist/children.js +8 -3
- package/dist/children.js.map +1 -1
- package/dist/command.js +6 -0
- package/dist/command.js.map +1 -1
- package/dist/compiler/ast.d.ts +21 -0
- package/dist/compiler/ast.js +145 -0
- package/dist/compiler/ast.js.map +1 -0
- package/dist/compiler/compile.d.ts +38 -0
- package/dist/compiler/compile.js +129 -0
- package/dist/compiler/compile.js.map +1 -0
- package/dist/compiler/hooks.d.ts +18 -0
- package/dist/compiler/hooks.js +186 -0
- package/dist/compiler/hooks.js.map +1 -0
- package/dist/compiler/parse5-check.d.ts +21 -0
- package/dist/compiler/parse5-check.js +55 -0
- package/dist/compiler/parse5-check.js.map +1 -0
- package/dist/compiler/scan.d.ts +31 -0
- package/dist/compiler/scan.js +152 -0
- package/dist/compiler/scan.js.map +1 -0
- package/dist/compiler/source.d.ts +34 -0
- package/dist/compiler/source.js +114 -0
- package/dist/compiler/source.js.map +1 -0
- package/dist/define.d.ts +2 -2
- package/dist/define.js +35 -327
- package/dist/define.js.map +1 -1
- package/dist/devtools-events.d.ts +5 -0
- package/dist/devtools-events.js.map +1 -1
- package/dist/devtools-off.d.ts +1 -0
- package/dist/devtools-off.js +1 -0
- package/dist/devtools-off.js.map +1 -1
- package/dist/devtools.d.ts +1 -0
- package/dist/devtools.js +3 -2
- package/dist/devtools.js.map +1 -1
- package/dist/drivers-scope.d.ts +1 -1
- package/dist/drivers-scope.js +1 -8
- package/dist/drivers-scope.js.map +1 -1
- package/dist/element-types.d.ts +5 -3
- package/dist/element-types.js.map +1 -1
- package/dist/element.d.ts +3 -0
- package/dist/element.js +269 -0
- package/dist/element.js.map +1 -0
- package/dist/eslint/imports.d.ts +24 -0
- package/dist/eslint/imports.js +78 -0
- package/dist/eslint/imports.js.map +1 -0
- package/dist/eslint/index.d.ts +15 -0
- package/dist/eslint/index.js +15 -0
- package/dist/eslint/index.js.map +1 -0
- package/dist/eslint/locate.d.ts +33 -0
- package/dist/eslint/locate.js +132 -0
- package/dist/eslint/locate.js.map +1 -0
- package/dist/eslint/row-purity-rule.d.ts +2 -0
- package/dist/eslint/row-purity-rule.js +103 -0
- package/dist/eslint/row-purity-rule.js.map +1 -0
- package/dist/eslint/template-rule.d.ts +5 -0
- package/dist/eslint/template-rule.js +66 -0
- package/dist/eslint/template-rule.js.map +1 -0
- package/dist/features.d.ts +39 -0
- package/dist/features.js +2 -0
- package/dist/features.js.map +1 -0
- package/dist/focus.d.ts +7 -12
- package/dist/focus.js +13 -10
- package/dist/focus.js.map +1 -1
- package/dist/hooks/invalid.d.ts +14 -0
- package/dist/hooks/invalid.js +72 -0
- package/dist/hooks/invalid.js.map +1 -0
- package/dist/hooks/labelled-by.d.ts +13 -0
- package/dist/hooks/labelled-by.js +51 -0
- package/dist/hooks/labelled-by.js.map +1 -0
- package/dist/host-model.d.ts +48 -0
- package/dist/host-model.js +155 -0
- package/dist/host-model.js.map +1 -0
- package/dist/hydration-client.d.ts +9 -0
- package/dist/hydration-client.js +35 -0
- package/dist/hydration-client.js.map +1 -0
- package/dist/hydration.d.ts +13 -19
- package/dist/hydration.js +45 -45
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +9 -17
- package/dist/index.js +9 -18
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +31 -8
- package/dist/intent.js +82 -27
- package/dist/intent.js.map +1 -1
- package/dist/internal/interpreter.d.ts +7 -1
- package/dist/internal/interpreter.js +11 -4
- package/dist/internal/interpreter.js.map +1 -1
- package/dist/invokers-shim.d.ts +6 -0
- package/dist/invokers-shim.js +32 -0
- package/dist/invokers-shim.js.map +1 -0
- package/dist/invokers.d.ts +3 -5
- package/dist/invokers.js +11 -51
- package/dist/invokers.js.map +1 -1
- package/dist/islands.d.ts +0 -2
- package/dist/islands.js +4 -11
- package/dist/islands.js.map +1 -1
- package/dist/light-dom.d.ts +3 -52
- package/dist/light-dom.js +5 -100
- package/dist/light-dom.js.map +1 -1
- package/dist/prop.d.ts +66 -0
- package/dist/prop.js +22 -0
- package/dist/prop.js.map +1 -0
- package/dist/props.d.ts +52 -21
- package/dist/props.js +150 -34
- package/dist/props.js.map +1 -1
- package/dist/scheduler.d.ts +39 -0
- package/dist/scheduler.js +230 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/server-component.d.ts +10 -0
- package/dist/server-component.js +102 -0
- package/dist/server-component.js.map +1 -0
- package/dist/server-specs.d.ts +6 -0
- package/dist/server-specs.js +13 -0
- package/dist/server-specs.js.map +1 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.js +50 -0
- package/dist/server.js.map +1 -0
- package/dist/settled.d.ts +10 -0
- package/dist/settled.js +40 -0
- package/dist/settled.js.map +1 -0
- package/dist/states.d.ts +3 -3
- package/dist/states.js +4 -7
- package/dist/states.js.map +1 -1
- package/dist/store-binding.d.ts +6 -3
- package/dist/store-binding.js +5 -6
- package/dist/store-binding.js.map +1 -1
- package/dist/store-scope.d.ts +6 -7
- package/dist/store-scope.js +11 -26
- package/dist/store-scope.js.map +1 -1
- package/dist/store.d.ts +3 -0
- package/dist/store.js +9 -2
- package/dist/store.js.map +1 -1
- package/dist/stores-provider.d.ts +2 -6
- package/dist/stores-provider.js +21 -55
- package/dist/stores-provider.js.map +1 -1
- package/dist/transitions.d.ts +5 -6
- package/dist/transitions.js +11 -20
- package/dist/transitions.js.map +1 -1
- package/dist/types.d.ts +19 -23
- package/dist/types.js.map +1 -1
- package/dist/view/attributes.d.ts +6 -0
- package/dist/view/attributes.js +10 -0
- package/dist/view/attributes.js.map +1 -0
- package/dist/view/compiled.d.ts +1 -0
- package/dist/view/compiled.js +6 -0
- package/dist/view/compiled.js.map +1 -0
- package/dist/view/dev-off.d.ts +1 -0
- package/dist/view/dev-off.js +4 -0
- package/dist/view/dev-off.js.map +1 -0
- package/dist/view/dev.d.ts +1 -0
- package/dist/view/dev.js +5 -0
- package/dist/view/dev.js.map +1 -0
- package/dist/view/flags.d.ts +1 -0
- package/dist/view/flags.js +4 -0
- package/dist/view/flags.js.map +1 -0
- package/dist/view/index.d.ts +21 -0
- package/dist/view/index.js +21 -0
- package/dist/view/index.js.map +1 -0
- package/dist/view/normalize/check.d.ts +20 -0
- package/dist/view/normalize/check.js +78 -0
- package/dist/view/normalize/check.js.map +1 -0
- package/dist/view/normalize/emit.d.ts +13 -0
- package/dist/view/normalize/emit.js +136 -0
- package/dist/view/normalize/emit.js.map +1 -0
- package/dist/view/normalize/entities.d.ts +4 -0
- package/dist/view/normalize/entities.js +38 -0
- package/dist/view/normalize/entities.js.map +1 -0
- package/dist/view/normalize/errors.d.ts +9 -0
- package/dist/view/normalize/errors.js +19 -0
- package/dist/view/normalize/errors.js.map +1 -0
- package/dist/view/normalize/id.d.ts +7 -0
- package/dist/view/normalize/id.js +48 -0
- package/dist/view/normalize/id.js.map +1 -0
- package/dist/view/normalize/normalize.d.ts +15 -0
- package/dist/view/normalize/normalize.js +36 -0
- package/dist/view/normalize/normalize.js.map +1 -0
- package/dist/view/normalize/repairs.d.ts +24 -0
- package/dist/view/normalize/repairs.js +166 -0
- package/dist/view/normalize/repairs.js.map +1 -0
- package/dist/view/normalize/rules.d.ts +30 -0
- package/dist/view/normalize/rules.js +90 -0
- package/dist/view/normalize/rules.js.map +1 -0
- package/dist/view/normalize/shape.d.ts +5 -0
- package/dist/view/normalize/shape.js +38 -0
- package/dist/view/normalize/shape.js.map +1 -0
- package/dist/view/normalize/source.d.ts +13 -0
- package/dist/view/normalize/source.js +34 -0
- package/dist/view/normalize/source.js.map +1 -0
- package/dist/view/normalize/tag-states.d.ts +23 -0
- package/dist/view/normalize/tag-states.js +192 -0
- package/dist/view/normalize/tag-states.js.map +1 -0
- package/dist/view/normalize/tokenizer.d.ts +5 -0
- package/dist/view/normalize/tokenizer.js +233 -0
- package/dist/view/normalize/tokenizer.js.map +1 -0
- package/dist/view/normalize/tokens.d.ts +51 -0
- package/dist/view/normalize/tokens.js +24 -0
- package/dist/view/normalize/tokens.js.map +1 -0
- package/dist/view/normalize/tree.d.ts +59 -0
- package/dist/view/normalize/tree.js +190 -0
- package/dist/view/normalize/tree.js.map +1 -0
- package/dist/view/normalize/types.d.ts +126 -0
- package/dist/view/normalize/types.js +26 -0
- package/dist/view/normalize/types.js.map +1 -0
- package/dist/view/normalize/whitespace.d.ts +2 -0
- package/dist/{template-whitespace.js → view/normalize/whitespace.js} +24 -34
- package/dist/view/normalize/whitespace.js.map +1 -0
- package/dist/view/prepare-stub.d.ts +3 -0
- package/dist/view/prepare-stub.js +7 -0
- package/dist/view/prepare-stub.js.map +1 -0
- package/dist/view/prepare.d.ts +8 -0
- package/dist/view/prepare.js +57 -0
- package/dist/view/prepare.js.map +1 -0
- package/dist/view/registry.d.ts +63 -0
- package/dist/view/registry.js +19 -0
- package/dist/view/registry.js.map +1 -0
- package/dist/view/render/adopt-attr.d.ts +14 -0
- package/dist/view/render/adopt-attr.js +77 -0
- package/dist/view/render/adopt-attr.js.map +1 -0
- package/dist/view/render/adopt-plan.d.ts +10 -0
- package/dist/view/render/adopt-plan.js +26 -0
- package/dist/view/render/adopt-plan.js.map +1 -0
- package/dist/view/render/adopt.d.ts +3 -0
- package/dist/view/render/adopt.js +269 -0
- package/dist/view/render/adopt.js.map +1 -0
- package/dist/view/render/attr-parts.d.ts +61 -0
- package/dist/view/render/attr-parts.js +229 -0
- package/dist/view/render/attr-parts.js.map +1 -0
- package/dist/view/render/child-part.d.ts +51 -0
- package/dist/view/render/child-part.js +170 -0
- package/dist/view/render/child-part.js.map +1 -0
- package/dist/view/render/dev-check.d.ts +12 -0
- package/dist/view/render/dev-check.js +122 -0
- package/dist/view/render/dev-check.js.map +1 -0
- package/dist/view/render/hook-part.d.ts +3 -0
- package/dist/view/render/hook-part.js +20 -0
- package/dist/view/render/hook-part.js.map +1 -0
- package/dist/view/render/hooks.d.ts +40 -0
- package/dist/view/render/hooks.js +46 -0
- package/dist/view/render/hooks.js.map +1 -0
- package/dist/view/render/hydrate.d.ts +10 -0
- package/dist/view/render/hydrate.js +33 -0
- package/dist/view/render/hydrate.js.map +1 -0
- package/dist/view/render/instance.d.ts +33 -0
- package/dist/view/render/instance.js +107 -0
- package/dist/view/render/instance.js.map +1 -0
- package/dist/view/render/list.d.ts +20 -0
- package/dist/view/render/list.js +156 -0
- package/dist/view/render/list.js.map +1 -0
- package/dist/view/render/mismatch.d.ts +10 -0
- package/dist/view/render/mismatch.js +53 -0
- package/dist/view/render/mismatch.js.map +1 -0
- package/dist/view/render/nodes.d.ts +10 -0
- package/dist/view/render/nodes.js +37 -0
- package/dist/view/render/nodes.js.map +1 -0
- package/dist/view/render/place.d.ts +18 -0
- package/dist/view/render/place.js +69 -0
- package/dist/view/render/place.js.map +1 -0
- package/dist/view/render/plan.d.ts +26 -0
- package/dist/view/render/plan.js +124 -0
- package/dist/view/render/plan.js.map +1 -0
- package/dist/view/render/raw.d.ts +18 -0
- package/dist/view/render/raw.js +68 -0
- package/dist/view/render/raw.js.map +1 -0
- package/dist/view/render/render.d.ts +12 -0
- package/dist/view/render/render.js +60 -0
- package/dist/view/render/render.js.map +1 -0
- package/dist/view/render/reorder.d.ts +4 -0
- package/dist/view/render/reorder.js +101 -0
- package/dist/view/render/reorder.js.map +1 -0
- package/dist/view/render/rows.d.ts +23 -0
- package/dist/view/render/rows.js +95 -0
- package/dist/view/render/rows.js.map +1 -0
- package/dist/view/render/seen.d.ts +11 -0
- package/dist/view/render/seen.js +17 -0
- package/dist/view/render/seen.js.map +1 -0
- package/dist/view/render/values.d.ts +29 -0
- package/dist/view/render/values.js +13 -0
- package/dist/view/render/values.js.map +1 -0
- package/dist/view/render/warn.d.ts +14 -0
- package/dist/view/render/warn.js +69 -0
- package/dist/view/render/warn.js.map +1 -0
- package/dist/view/server/component.d.ts +9 -0
- package/dist/view/server/component.js +42 -0
- package/dist/view/server/component.js.map +1 -0
- package/dist/view/server/escape.d.ts +10 -0
- package/dist/view/server/escape.js +34 -0
- package/dist/view/server/escape.js.map +1 -0
- package/dist/view/server/index.d.ts +28 -0
- package/dist/view/server/index.js +72 -0
- package/dist/view/server/index.js.map +1 -0
- package/dist/view/server/sha256.d.ts +2 -0
- package/dist/view/server/sha256.js +75 -0
- package/dist/view/server/sha256.js.map +1 -0
- package/dist/view/server/tags.d.ts +57 -0
- package/dist/view/server/tags.js +121 -0
- package/dist/view/server/tags.js.map +1 -0
- package/dist/view/server/values.d.ts +16 -0
- package/dist/view/server/values.js +86 -0
- package/dist/view/server/values.js.map +1 -0
- package/dist/view/server/writer.d.ts +16 -0
- package/dist/view/server/writer.js +155 -0
- package/dist/view/server/writer.js.map +1 -0
- package/dist/view/styles.d.ts +25 -0
- package/dist/view/styles.js +58 -0
- package/dist/view/styles.js.map +1 -0
- package/dist/view/template-element.d.ts +3 -0
- package/dist/view/template-element.js +25 -0
- package/dist/view/template-element.js.map +1 -0
- package/dist/view/template.d.ts +21 -0
- package/dist/view/template.js +30 -0
- package/dist/view/template.js.map +1 -0
- package/dist/vite.d.ts +45 -6
- package/dist/vite.js +150 -25
- package/dist/vite.js.map +1 -1
- package/package.json +39 -4
- package/dist/accessible-name.d.ts +0 -19
- package/dist/accessible-name.js +0 -56
- package/dist/accessible-name.js.map +0 -1
- package/dist/element-directive.d.ts +0 -20
- package/dist/element-directive.js +0 -31
- package/dist/element-directive.js.map +0 -1
- package/dist/invalid.d.ts +0 -21
- package/dist/invalid.js +0 -70
- package/dist/invalid.js.map +0 -1
- package/dist/lit-html-check.d.ts +0 -6
- package/dist/lit-html-check.js +0 -48
- package/dist/lit-html-check.js.map +0 -1
- package/dist/live-boolean.d.ts +0 -22
- package/dist/live-boolean.js +0 -34
- package/dist/live-boolean.js.map +0 -1
- package/dist/styles.d.ts +0 -12
- package/dist/styles.js +0 -11
- package/dist/styles.js.map +0 -1
- package/dist/template-whitespace.d.ts +0 -7
- package/dist/template-whitespace.js.map +0 -1
- package/dist/templates.d.ts +0 -5
- package/dist/templates.js +0 -15
- package/dist/templates.js.map +0 -1
- package/dist/textarea.d.ts +0 -26
- package/dist/textarea.js +0 -85
- package/dist/textarea.js.map +0 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// HTML tokenizer over a template's static strings (view/01-templates.md "Normalization" step 1),
|
|
2
|
+
// after the WHATWG HTML tokenizer states: data, tag open, tag name, before/after attribute
|
|
3
|
+
// name, attribute value (double/single/unquoted), self-closing start tag, comment, bogus
|
|
4
|
+
// comment, DOCTYPE, RAWTEXT (<script>, <style>, …) and RCDATA (<textarea>, <title>). The state
|
|
5
|
+
// at each hole decides its kind (view/02-bindings.md) or a rule violation (rules 1, 2, 3, 5).
|
|
6
|
+
// Start-tag states live in tag-states.ts. The tree builder (tree.ts) receives the tokens and
|
|
7
|
+
// picks the content mode after each start tag.
|
|
8
|
+
import { msg } from './rules.js';
|
|
9
|
+
import { TagStates } from './tag-states.js';
|
|
10
|
+
import { isWs, S, TAG_NAME } from './tokens.js';
|
|
11
|
+
/** Tokenizes `src.strings`, sending tokens and holes to `sink`. */
|
|
12
|
+
export function tokenize(src, sink) {
|
|
13
|
+
new Tokenizer(src, sink).run();
|
|
14
|
+
}
|
|
15
|
+
class Tokenizer extends TagStates {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.text = '';
|
|
19
|
+
}
|
|
20
|
+
run() {
|
|
21
|
+
const { src } = this;
|
|
22
|
+
for (src.i = 0; src.i < src.strings.length; src.i++) {
|
|
23
|
+
const s = src.strings[src.i] ?? '';
|
|
24
|
+
src.pos = 0;
|
|
25
|
+
while (src.pos < s.length)
|
|
26
|
+
this.step(s);
|
|
27
|
+
if (!src.last)
|
|
28
|
+
this.hole();
|
|
29
|
+
}
|
|
30
|
+
this.finish();
|
|
31
|
+
}
|
|
32
|
+
flushText() {
|
|
33
|
+
if (this.text !== '')
|
|
34
|
+
this.sink.text(this.text);
|
|
35
|
+
this.text = '';
|
|
36
|
+
}
|
|
37
|
+
step(s) {
|
|
38
|
+
const { src } = this;
|
|
39
|
+
switch (this.state) {
|
|
40
|
+
case S.Data: {
|
|
41
|
+
this.data(s);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
case S.RawText:
|
|
45
|
+
case S.RcData: {
|
|
46
|
+
this.rawText(s);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
case S.Comment: {
|
|
50
|
+
this.comment(s);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
case S.Bogus:
|
|
54
|
+
case S.Doctype: {
|
|
55
|
+
const end = s.indexOf('>', src.pos);
|
|
56
|
+
this.buf += s.slice(src.pos, end < 0 ? s.length : end + 1);
|
|
57
|
+
src.pos = end < 0 ? s.length : end + 1;
|
|
58
|
+
if (end < 0)
|
|
59
|
+
return;
|
|
60
|
+
if (this.state === S.Doctype)
|
|
61
|
+
this.sink.doctype(this.buf);
|
|
62
|
+
else
|
|
63
|
+
this.sink.comment(this.buf);
|
|
64
|
+
this.state = S.Data;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
case S.EndTag: {
|
|
68
|
+
const end = s.indexOf('>', src.pos);
|
|
69
|
+
src.pos = end < 0 ? s.length : end + 1;
|
|
70
|
+
if (end < 0)
|
|
71
|
+
return;
|
|
72
|
+
this.state = S.Data;
|
|
73
|
+
this.sink.end(this.buf);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
this.tagChar(s.charAt(src.pos));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
data(s) {
|
|
81
|
+
const { src } = this;
|
|
82
|
+
const lt = s.indexOf('<', src.pos);
|
|
83
|
+
if (lt < 0) {
|
|
84
|
+
this.text += s.slice(src.pos);
|
|
85
|
+
src.pos = s.length;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.text += s.slice(src.pos, lt);
|
|
89
|
+
src.pos = lt;
|
|
90
|
+
const next = s.charAt(lt + 1);
|
|
91
|
+
const rest = s.slice(lt + 1);
|
|
92
|
+
if (next === '' || (next === '/' && lt + 2 === s.length)) {
|
|
93
|
+
if (!src.last)
|
|
94
|
+
src.fail(2, msg.dynamicTag());
|
|
95
|
+
this.literal(s.length - lt);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (rest.startsWith('!--')) {
|
|
99
|
+
this.open(S.Comment, '<!--', 4);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (/^!doctype/i.test(rest)) {
|
|
103
|
+
this.open(S.Doctype, '', 0);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (next === '!' || next === '?') {
|
|
107
|
+
this.open(S.Bogus, '', 0);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (next === '/') {
|
|
111
|
+
const name = TAG_NAME.exec(s.slice(lt + 2));
|
|
112
|
+
if (name === null)
|
|
113
|
+
return src.fail(7, msg.badEndTag());
|
|
114
|
+
this.flushText();
|
|
115
|
+
src.pos = lt + 2 + name[0].length;
|
|
116
|
+
if (src.pos === s.length && !src.last)
|
|
117
|
+
src.fail(2, msg.dynamicTag());
|
|
118
|
+
this.buf = name[0].toLowerCase();
|
|
119
|
+
this.state = S.EndTag;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const name = TAG_NAME.exec(rest);
|
|
123
|
+
if (name === null) {
|
|
124
|
+
this.literal(1);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.flushText();
|
|
128
|
+
src.pos = lt + 1 + name[0].length;
|
|
129
|
+
if (src.pos === s.length && !src.last)
|
|
130
|
+
src.fail(2, msg.dynamicTag());
|
|
131
|
+
this.tag = { name: name[0].toLowerCase(), raw: name[0], attrs: [], selfClosing: false };
|
|
132
|
+
this.state = S.BeforeName;
|
|
133
|
+
}
|
|
134
|
+
literal(length) {
|
|
135
|
+
const { src } = this;
|
|
136
|
+
const s = src.strings[src.i] ?? '';
|
|
137
|
+
this.text += s.slice(src.pos, src.pos + length);
|
|
138
|
+
src.pos += length;
|
|
139
|
+
}
|
|
140
|
+
open(state, buf, skip) {
|
|
141
|
+
this.flushText();
|
|
142
|
+
this.state = state;
|
|
143
|
+
this.buf = buf;
|
|
144
|
+
this.src.pos += skip;
|
|
145
|
+
}
|
|
146
|
+
comment(s) {
|
|
147
|
+
const { src } = this;
|
|
148
|
+
// `<!-->` and `<!--->` are complete (empty) comments.
|
|
149
|
+
if (this.buf === '<!--') {
|
|
150
|
+
const abrupt = s.startsWith('>', src.pos) ? 1 : s.startsWith('->', src.pos) ? 2 : 0;
|
|
151
|
+
if (abrupt > 0) {
|
|
152
|
+
this.endComment(s, src.pos + abrupt);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const a = s.indexOf('-->', src.pos);
|
|
157
|
+
const b = s.indexOf('--!>', src.pos);
|
|
158
|
+
if (a < 0 && b < 0) {
|
|
159
|
+
this.buf += s.slice(src.pos);
|
|
160
|
+
src.pos = s.length;
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const end = b < 0 || (a >= 0 && a < b) ? a + 3 : b + 4;
|
|
164
|
+
this.endComment(s, end);
|
|
165
|
+
}
|
|
166
|
+
endComment(s, end) {
|
|
167
|
+
this.buf += s.slice(this.src.pos, end);
|
|
168
|
+
this.src.pos = end;
|
|
169
|
+
this.sink.comment(this.buf);
|
|
170
|
+
this.state = S.Data;
|
|
171
|
+
}
|
|
172
|
+
rawText(s) {
|
|
173
|
+
const { src } = this;
|
|
174
|
+
const lower = s.toLowerCase();
|
|
175
|
+
let from = src.pos;
|
|
176
|
+
for (;;) {
|
|
177
|
+
const at = lower.indexOf(`</${this.rawEnd}`, from);
|
|
178
|
+
const after = at < 0 ? '' : s.charAt(at + 2 + this.rawEnd.length);
|
|
179
|
+
if (at < 0 || after === '') {
|
|
180
|
+
this.text += s.slice(src.pos);
|
|
181
|
+
src.pos = s.length;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (isWs(after) || after === '/' || after === '>') {
|
|
185
|
+
this.text += s.slice(src.pos, at);
|
|
186
|
+
src.pos = at;
|
|
187
|
+
this.flushText();
|
|
188
|
+
this.state = S.Data;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
from = at + 1;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** The hole after string `src.i`: its kind depends on the state. */
|
|
195
|
+
hole() {
|
|
196
|
+
const { src } = this;
|
|
197
|
+
switch (this.state) {
|
|
198
|
+
case S.Data:
|
|
199
|
+
case S.RcData:
|
|
200
|
+
this.flushText();
|
|
201
|
+
this.sink.hole(this.state === S.Data ? 'child' : 'text');
|
|
202
|
+
break;
|
|
203
|
+
case S.RawText:
|
|
204
|
+
src.fail(3, msg.inRawText(this.rawEnd));
|
|
205
|
+
break;
|
|
206
|
+
case S.Comment:
|
|
207
|
+
case S.Bogus:
|
|
208
|
+
src.fail(3, msg.inComment());
|
|
209
|
+
break;
|
|
210
|
+
case S.Doctype:
|
|
211
|
+
src.fail(3, msg.inDoctype());
|
|
212
|
+
break;
|
|
213
|
+
case S.EndTag:
|
|
214
|
+
src.fail(2, msg.dynamicTag());
|
|
215
|
+
break;
|
|
216
|
+
default:
|
|
217
|
+
this.holeInTag();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
finish() {
|
|
221
|
+
const { src } = this;
|
|
222
|
+
src.i = src.strings.length - 1;
|
|
223
|
+
src.pos = (src.strings[src.i] ?? '').length;
|
|
224
|
+
if (this.state === S.Comment || this.state === S.Bogus || this.state === S.Doctype) {
|
|
225
|
+
src.fail(7, msg.unterminated('comment'));
|
|
226
|
+
}
|
|
227
|
+
if (this.state > S.Doctype)
|
|
228
|
+
src.fail(7, msg.unterminated('tag'));
|
|
229
|
+
this.flushText();
|
|
230
|
+
this.sink.finish();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../../src/view/normalize/tokenizer.ts"],"names":[],"mappings":"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","sourcesContent":["// HTML tokenizer over a template's static strings (view/01-templates.md \"Normalization\" step 1),\n// after the WHATWG HTML tokenizer states: data, tag open, tag name, before/after attribute\n// name, attribute value (double/single/unquoted), self-closing start tag, comment, bogus\n// comment, DOCTYPE, RAWTEXT (<script>, <style>, …) and RCDATA (<textarea>, <title>). The state\n// at each hole decides its kind (view/02-bindings.md) or a rule violation (rules 1, 2, 3, 5).\n// Start-tag states live in tag-states.ts. The tree builder (tree.ts) receives the tokens and\n// picks the content mode after each start tag.\nimport { msg } from './rules.js';\nimport type { Source } from './source.js';\nimport { TagStates } from './tag-states.js';\nimport { isWs, S, TAG_NAME, type Sink, type State } from './tokens.js';\n\nexport type { AttrToken, ContentMode, Sink, StartTag } from './tokens.js';\n\n/** Tokenizes `src.strings`, sending tokens and holes to `sink`. */\nexport function tokenize(src: Source, sink: Sink): void {\n new Tokenizer(src, sink).run();\n}\n\nclass Tokenizer extends TagStates {\n private text = '';\n\n run(): void {\n const { src } = this;\n for (src.i = 0; src.i < src.strings.length; src.i++) {\n const s = src.strings[src.i] ?? '';\n src.pos = 0;\n while (src.pos < s.length) this.step(s);\n if (!src.last) this.hole();\n }\n this.finish();\n }\n\n private flushText(): void {\n if (this.text !== '') this.sink.text(this.text);\n this.text = '';\n }\n\n private step(s: string): void {\n const { src } = this;\n switch (this.state) {\n case S.Data: {\n this.data(s);\n return;\n }\n case S.RawText:\n case S.RcData: {\n this.rawText(s);\n return;\n }\n case S.Comment: {\n this.comment(s);\n return;\n }\n case S.Bogus:\n case S.Doctype: {\n const end = s.indexOf('>', src.pos);\n this.buf += s.slice(src.pos, end < 0 ? s.length : end + 1);\n src.pos = end < 0 ? s.length : end + 1;\n if (end < 0) return;\n if (this.state === S.Doctype) this.sink.doctype(this.buf);\n else this.sink.comment(this.buf);\n this.state = S.Data;\n return;\n }\n case S.EndTag: {\n const end = s.indexOf('>', src.pos);\n src.pos = end < 0 ? s.length : end + 1;\n if (end < 0) return;\n this.state = S.Data;\n this.sink.end(this.buf);\n return;\n }\n default:\n this.tagChar(s.charAt(src.pos));\n }\n }\n\n private data(s: string): void {\n const { src } = this;\n const lt = s.indexOf('<', src.pos);\n if (lt < 0) {\n this.text += s.slice(src.pos);\n src.pos = s.length;\n return;\n }\n this.text += s.slice(src.pos, lt);\n src.pos = lt;\n const next = s.charAt(lt + 1);\n const rest = s.slice(lt + 1);\n if (next === '' || (next === '/' && lt + 2 === s.length)) {\n if (!src.last) src.fail(2, msg.dynamicTag());\n this.literal(s.length - lt);\n return;\n }\n if (rest.startsWith('!--')) {\n this.open(S.Comment, '<!--', 4);\n return;\n }\n if (/^!doctype/i.test(rest)) {\n this.open(S.Doctype, '', 0);\n return;\n }\n if (next === '!' || next === '?') {\n this.open(S.Bogus, '', 0);\n return;\n }\n if (next === '/') {\n const name = TAG_NAME.exec(s.slice(lt + 2));\n if (name === null) return src.fail(7, msg.badEndTag());\n this.flushText();\n src.pos = lt + 2 + name[0].length;\n if (src.pos === s.length && !src.last) src.fail(2, msg.dynamicTag());\n this.buf = name[0].toLowerCase();\n this.state = S.EndTag;\n return;\n }\n const name = TAG_NAME.exec(rest);\n if (name === null) {\n this.literal(1);\n return;\n }\n this.flushText();\n src.pos = lt + 1 + name[0].length;\n if (src.pos === s.length && !src.last) src.fail(2, msg.dynamicTag());\n this.tag = { name: name[0].toLowerCase(), raw: name[0], attrs: [], selfClosing: false };\n this.state = S.BeforeName;\n }\n\n private literal(length: number): void {\n const { src } = this;\n const s = src.strings[src.i] ?? '';\n this.text += s.slice(src.pos, src.pos + length);\n src.pos += length;\n }\n\n private open(state: State, buf: string, skip: number): void {\n this.flushText();\n this.state = state;\n this.buf = buf;\n this.src.pos += skip;\n }\n\n private comment(s: string): void {\n const { src } = this;\n // `<!-->` and `<!--->` are complete (empty) comments.\n if (this.buf === '<!--') {\n const abrupt = s.startsWith('>', src.pos) ? 1 : s.startsWith('->', src.pos) ? 2 : 0;\n if (abrupt > 0) {\n this.endComment(s, src.pos + abrupt);\n return;\n }\n }\n const a = s.indexOf('-->', src.pos);\n const b = s.indexOf('--!>', src.pos);\n if (a < 0 && b < 0) {\n this.buf += s.slice(src.pos);\n src.pos = s.length;\n return;\n }\n const end = b < 0 || (a >= 0 && a < b) ? a + 3 : b + 4;\n this.endComment(s, end);\n }\n\n private endComment(s: string, end: number): void {\n this.buf += s.slice(this.src.pos, end);\n this.src.pos = end;\n this.sink.comment(this.buf);\n this.state = S.Data;\n }\n\n private rawText(s: string): void {\n const { src } = this;\n const lower = s.toLowerCase();\n let from = src.pos;\n for (;;) {\n const at = lower.indexOf(`</${this.rawEnd}`, from);\n const after = at < 0 ? '' : s.charAt(at + 2 + this.rawEnd.length);\n if (at < 0 || after === '') {\n this.text += s.slice(src.pos);\n src.pos = s.length;\n return;\n }\n if (isWs(after) || after === '/' || after === '>') {\n this.text += s.slice(src.pos, at);\n src.pos = at;\n this.flushText();\n this.state = S.Data;\n return;\n }\n from = at + 1;\n }\n }\n\n /** The hole after string `src.i`: its kind depends on the state. */\n private hole(): void {\n const { src } = this;\n switch (this.state) {\n case S.Data:\n case S.RcData:\n this.flushText();\n this.sink.hole(this.state === S.Data ? 'child' : 'text');\n break;\n case S.RawText:\n src.fail(3, msg.inRawText(this.rawEnd));\n break;\n case S.Comment:\n case S.Bogus:\n src.fail(3, msg.inComment());\n break;\n case S.Doctype:\n src.fail(3, msg.inDoctype());\n break;\n case S.EndTag:\n src.fail(2, msg.dynamicTag());\n break;\n default:\n this.holeInTag();\n }\n }\n\n private finish(): void {\n const { src } = this;\n src.i = src.strings.length - 1;\n src.pos = (src.strings[src.i] ?? '').length;\n if (this.state === S.Comment || this.state === S.Bogus || this.state === S.Doctype) {\n src.fail(7, msg.unterminated('comment'));\n }\n if (this.state > S.Doctype) src.fail(7, msg.unterminated('tag'));\n this.flushText();\n this.sink.finish();\n }\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type ContentMode = 'data' | 'rawtext' | 'rcdata';
|
|
2
|
+
/** A static attribute as written: `value` is the raw source, quotes included, or null. */
|
|
3
|
+
export interface StaticAttr {
|
|
4
|
+
readonly kind: 'static';
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly value: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** A bound attribute. `strings` (raw static pieces) only for multi-attributes. */
|
|
9
|
+
export interface BoundAttr {
|
|
10
|
+
readonly kind: 'attr' | 'bool' | 'prop' | 'hook';
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly strings?: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
export type AttrToken = StaticAttr | BoundAttr;
|
|
15
|
+
export interface StartTag {
|
|
16
|
+
/** Lower-case name, and the name as written. */
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly raw: string;
|
|
19
|
+
readonly attrs: AttrToken[];
|
|
20
|
+
selfClosing: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface Sink {
|
|
23
|
+
text(raw: string): void;
|
|
24
|
+
comment(raw: string): void;
|
|
25
|
+
doctype(raw: string): void;
|
|
26
|
+
start(tag: StartTag): ContentMode;
|
|
27
|
+
end(name: string): void;
|
|
28
|
+
hole(kind: 'child' | 'text'): void;
|
|
29
|
+
finish(): void;
|
|
30
|
+
}
|
|
31
|
+
export declare const S: {
|
|
32
|
+
readonly Data: 0;
|
|
33
|
+
readonly RawText: 1;
|
|
34
|
+
readonly RcData: 2;
|
|
35
|
+
readonly Comment: 3;
|
|
36
|
+
readonly Bogus: 4;
|
|
37
|
+
readonly Doctype: 5;
|
|
38
|
+
readonly BeforeName: 6;
|
|
39
|
+
readonly Name: 7;
|
|
40
|
+
readonly AfterName: 8;
|
|
41
|
+
readonly BeforeValue: 9;
|
|
42
|
+
readonly ValueDq: 10;
|
|
43
|
+
readonly ValueSq: 11;
|
|
44
|
+
readonly ValueUnq: 12;
|
|
45
|
+
readonly AfterValueQ: 13;
|
|
46
|
+
readonly SelfClosing: 14;
|
|
47
|
+
readonly EndTag: 15;
|
|
48
|
+
};
|
|
49
|
+
export type State = (typeof S)[keyof typeof S];
|
|
50
|
+
export declare const TAG_NAME: RegExp;
|
|
51
|
+
export declare const isWs: (c: string | undefined) => boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Token types shared by the tokenizer (tokenizer.ts, tag-states.ts) and the tree builder
|
|
2
|
+
// (tree.ts), plus the tokenizer's states (WHATWG HTML "Tokenization", the subset templates use).
|
|
3
|
+
export const S = {
|
|
4
|
+
Data: 0,
|
|
5
|
+
RawText: 1,
|
|
6
|
+
RcData: 2,
|
|
7
|
+
Comment: 3,
|
|
8
|
+
Bogus: 4,
|
|
9
|
+
Doctype: 5,
|
|
10
|
+
BeforeName: 6,
|
|
11
|
+
Name: 7,
|
|
12
|
+
AfterName: 8,
|
|
13
|
+
BeforeValue: 9,
|
|
14
|
+
ValueDq: 10,
|
|
15
|
+
ValueSq: 11,
|
|
16
|
+
ValueUnq: 12,
|
|
17
|
+
AfterValueQ: 13,
|
|
18
|
+
SelfClosing: 14,
|
|
19
|
+
EndTag: 15,
|
|
20
|
+
};
|
|
21
|
+
const WS = /^[\t\n\f\r ]$/;
|
|
22
|
+
export const TAG_NAME = /^[A-Za-z][^\t\n\f\r />]*/;
|
|
23
|
+
export const isWs = (c) => c !== undefined && WS.test(c);
|
|
24
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/view/normalize/tokens.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,iGAAiG;AAsCjG,MAAM,CAAC,MAAM,CAAC,GAAG;IACf,IAAI,EAAE,CAAC;IACP,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,IAAI,EAAE,CAAC;IACP,SAAS,EAAE,CAAC;IACZ,WAAW,EAAE,CAAC;IACd,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,EAAE;IACZ,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,EAAE;IACf,MAAM,EAAE,EAAE;CACF,CAAC;AAGX,MAAM,EAAE,GAAG,eAAe,CAAC;AAC3B,MAAM,CAAC,MAAM,QAAQ,GAAG,0BAA0B,CAAC;AACnD,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAqB,EAAW,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC","sourcesContent":["// Token types shared by the tokenizer (tokenizer.ts, tag-states.ts) and the tree builder\n// (tree.ts), plus the tokenizer's states (WHATWG HTML \"Tokenization\", the subset templates use).\n\nexport type ContentMode = 'data' | 'rawtext' | 'rcdata';\n\n/** A static attribute as written: `value` is the raw source, quotes included, or null. */\nexport interface StaticAttr {\n readonly kind: 'static';\n readonly name: string;\n readonly value: string | null;\n}\n\n/** A bound attribute. `strings` (raw static pieces) only for multi-attributes. */\nexport interface BoundAttr {\n readonly kind: 'attr' | 'bool' | 'prop' | 'hook';\n readonly name: string;\n readonly strings?: readonly string[];\n}\n\nexport type AttrToken = StaticAttr | BoundAttr;\n\nexport interface StartTag {\n /** Lower-case name, and the name as written. */\n readonly name: string;\n readonly raw: string;\n readonly attrs: AttrToken[];\n selfClosing: boolean;\n}\n\nexport interface Sink {\n text(raw: string): void;\n comment(raw: string): void;\n doctype(raw: string): void;\n start(tag: StartTag): ContentMode;\n end(name: string): void;\n hole(kind: 'child' | 'text'): void;\n finish(): void;\n}\n\nexport const S = {\n Data: 0,\n RawText: 1,\n RcData: 2,\n Comment: 3,\n Bogus: 4,\n Doctype: 5,\n BeforeName: 6,\n Name: 7,\n AfterName: 8,\n BeforeValue: 9,\n ValueDq: 10,\n ValueSq: 11,\n ValueUnq: 12,\n AfterValueQ: 13,\n SelfClosing: 14,\n EndTag: 15,\n} as const;\nexport type State = (typeof S)[keyof typeof S];\n\nconst WS = /^[\\t\\n\\f\\r ]$/;\nexport const TAG_NAME = /^[A-Za-z][^\\t\\n\\f\\r />]*/;\nexport const isWs = (c: string | undefined): boolean => c !== undefined && WS.test(c);\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Context, type Ns } from './repairs.js';
|
|
2
|
+
import type { Source } from './source.js';
|
|
3
|
+
import type { AttrToken, ContentMode, Sink, StartTag } from './tokenizer.js';
|
|
4
|
+
export interface ElementNode {
|
|
5
|
+
readonly type: 'el';
|
|
6
|
+
/** Lower-case name, and the name as written. */
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly raw: string;
|
|
9
|
+
readonly ns: Ns;
|
|
10
|
+
/** Static and bound attributes; bound names normalized, multi strings decoded. */
|
|
11
|
+
readonly attrs: readonly AttrToken[];
|
|
12
|
+
/** A custom element's static attributes, decoded (server props); undefined otherwise. */
|
|
13
|
+
readonly props: readonly (readonly [string, string])[] | undefined;
|
|
14
|
+
/** No end tag: a void element, or a self-closed SVG/MathML element. */
|
|
15
|
+
readonly empty: boolean;
|
|
16
|
+
readonly children: TreeNode[];
|
|
17
|
+
/** For <template>: the context its first start tag chose. */
|
|
18
|
+
inner?: Context;
|
|
19
|
+
}
|
|
20
|
+
export type TreeNode = ElementNode | {
|
|
21
|
+
readonly type: 'text';
|
|
22
|
+
readonly raw: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly type: 'comment';
|
|
25
|
+
readonly raw: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly type: 'doctype';
|
|
28
|
+
readonly raw: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly type: 'hole';
|
|
31
|
+
readonly kind: 'child' | 'text';
|
|
32
|
+
};
|
|
33
|
+
export declare class TreeBuilder implements Sink {
|
|
34
|
+
private readonly src;
|
|
35
|
+
readonly root: TreeNode[];
|
|
36
|
+
server: boolean;
|
|
37
|
+
private readonly stack;
|
|
38
|
+
private rootCtx;
|
|
39
|
+
/** The <pre>/<listing>/<textarea> whose first newline the parser drops, if just opened. */
|
|
40
|
+
private fresh;
|
|
41
|
+
constructor(src: Source);
|
|
42
|
+
private get children();
|
|
43
|
+
private context;
|
|
44
|
+
private inTemplate;
|
|
45
|
+
text(raw: string): void;
|
|
46
|
+
comment(raw: string): void;
|
|
47
|
+
doctype(raw: string): void;
|
|
48
|
+
hole(kind: 'child' | 'text'): void;
|
|
49
|
+
start(tag: StartTag): ContentMode;
|
|
50
|
+
private namespace;
|
|
51
|
+
private checkHtml;
|
|
52
|
+
/** Bound names lower-cased on HTML elements (not properties); rules 4, 7 (duplicates), 13. */
|
|
53
|
+
private attributes;
|
|
54
|
+
private props;
|
|
55
|
+
private decode;
|
|
56
|
+
end(name: string): void;
|
|
57
|
+
private close;
|
|
58
|
+
finish(): void;
|
|
59
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Tree builder for the normalizer (view/01-templates.md "Normalization"): turns the tokenizer's
|
|
2
|
+
// tokens into the tree the HTML parser builds for valid markup (WHATWG "tree construction":
|
|
3
|
+
// namespaces, void elements, raw text, the leading newline of <pre>/<listing>/<textarea>), so
|
|
4
|
+
// part paths match the browser's DOM. Markup the parser would repair is an error instead of
|
|
5
|
+
// being modelled (rule 7, repairs.ts), as are rules 3, 4, 6, 10, 12 and 13.
|
|
6
|
+
import { decodeRefs, unquote } from './entities.js';
|
|
7
|
+
import { breaksOutOfForeign, contextOf, rootContext, startRepair, textRepair, } from './repairs.js';
|
|
8
|
+
import { formStateFix, msg, SVG_ONLY, VOID } from './rules.js';
|
|
9
|
+
const RAWTEXT = new Set('script style xmp iframe noembed noframes noscript plaintext'.split(' '));
|
|
10
|
+
const RCDATA = new Set(['textarea', 'title']);
|
|
11
|
+
const LEADING_NEWLINE = new Set(['pre', 'listing', 'textarea']);
|
|
12
|
+
const DOCUMENT = new Set(['html', 'head', 'body']);
|
|
13
|
+
function integrationPoint(el) {
|
|
14
|
+
if (el.ns === 'svg')
|
|
15
|
+
return /^(foreignobject|desc|title)$/.test(el.name);
|
|
16
|
+
return el.ns === 'math' && /^(mi|mo|mn|ms|mtext)$/.test(el.name);
|
|
17
|
+
}
|
|
18
|
+
export class TreeBuilder {
|
|
19
|
+
constructor(src) {
|
|
20
|
+
this.src = src;
|
|
21
|
+
this.root = [];
|
|
22
|
+
this.server = false;
|
|
23
|
+
this.stack = [];
|
|
24
|
+
}
|
|
25
|
+
get children() {
|
|
26
|
+
return this.stack.at(-1)?.children ?? this.root;
|
|
27
|
+
}
|
|
28
|
+
context() {
|
|
29
|
+
const top = this.stack.at(-1);
|
|
30
|
+
if (top === undefined)
|
|
31
|
+
return { ctx: this.rootCtx ?? 'body', atRoot: true };
|
|
32
|
+
if (top.ns !== 'html')
|
|
33
|
+
return { ctx: 'body', atRoot: false };
|
|
34
|
+
if (top.name === 'template')
|
|
35
|
+
return { ctx: top.inner ?? 'body', atRoot: true };
|
|
36
|
+
return { ctx: contextOf(top.name), atRoot: false };
|
|
37
|
+
}
|
|
38
|
+
inTemplate() {
|
|
39
|
+
return this.stack.some((el) => el.ns === 'html' && el.name === 'template');
|
|
40
|
+
}
|
|
41
|
+
text(raw) {
|
|
42
|
+
const text = this.fresh !== undefined && raw.startsWith('\n') ? raw.slice(1) : raw;
|
|
43
|
+
this.fresh = undefined;
|
|
44
|
+
if (text === '')
|
|
45
|
+
return;
|
|
46
|
+
const { ctx, atRoot } = this.context();
|
|
47
|
+
const repair = textRepair(text, ctx, atRoot);
|
|
48
|
+
if (repair !== undefined)
|
|
49
|
+
this.src.fail(7, repair);
|
|
50
|
+
this.children.push({ type: 'text', raw: text });
|
|
51
|
+
}
|
|
52
|
+
comment(raw) {
|
|
53
|
+
this.fresh = undefined;
|
|
54
|
+
this.children.push({ type: 'comment', raw });
|
|
55
|
+
}
|
|
56
|
+
doctype(raw) {
|
|
57
|
+
this.fresh = undefined;
|
|
58
|
+
this.server = true;
|
|
59
|
+
this.children.push({ type: 'doctype', raw });
|
|
60
|
+
}
|
|
61
|
+
hole(kind) {
|
|
62
|
+
this.fresh = undefined;
|
|
63
|
+
if (this.inTemplate())
|
|
64
|
+
this.src.fail(3, msg.inTemplate());
|
|
65
|
+
this.children.push({ type: 'hole', kind });
|
|
66
|
+
}
|
|
67
|
+
start(tag) {
|
|
68
|
+
this.fresh = undefined;
|
|
69
|
+
const { name } = tag;
|
|
70
|
+
const ns = this.namespace(tag);
|
|
71
|
+
if (ns === 'html')
|
|
72
|
+
this.checkHtml(tag);
|
|
73
|
+
if (this.inTemplate() && tag.attrs.some((a) => a.kind !== 'static')) {
|
|
74
|
+
this.src.fail(3, msg.inTemplate());
|
|
75
|
+
}
|
|
76
|
+
const empty = ns === 'html' ? VOID.has(name) : tag.selfClosing;
|
|
77
|
+
const custom = ns === 'html' && name.includes('-');
|
|
78
|
+
const el = {
|
|
79
|
+
type: 'el',
|
|
80
|
+
name,
|
|
81
|
+
raw: tag.raw,
|
|
82
|
+
ns,
|
|
83
|
+
attrs: this.attributes(tag, ns),
|
|
84
|
+
props: custom ? this.props(tag) : undefined,
|
|
85
|
+
empty,
|
|
86
|
+
children: [],
|
|
87
|
+
};
|
|
88
|
+
this.children.push(el);
|
|
89
|
+
if (empty)
|
|
90
|
+
return 'data';
|
|
91
|
+
this.stack.push(el);
|
|
92
|
+
if (ns !== 'html')
|
|
93
|
+
return 'data';
|
|
94
|
+
if (LEADING_NEWLINE.has(name))
|
|
95
|
+
this.fresh = el;
|
|
96
|
+
return RAWTEXT.has(name) ? 'rawtext' : RCDATA.has(name) ? 'rcdata' : 'data';
|
|
97
|
+
}
|
|
98
|
+
namespace(tag) {
|
|
99
|
+
const parent = this.stack.at(-1);
|
|
100
|
+
if (parent === undefined || parent.ns === 'html' || integrationPoint(parent)) {
|
|
101
|
+
return tag.name === 'svg' ? 'svg' : tag.name === 'math' ? 'math' : 'html';
|
|
102
|
+
}
|
|
103
|
+
if (parent.name === 'annotation-xml' && tag.name === 'svg')
|
|
104
|
+
return 'svg';
|
|
105
|
+
const fontAttr = tag.attrs.some((a) => /^(color|face|size)$/i.test(a.name));
|
|
106
|
+
if (breaksOutOfForeign(tag.name, fontAttr)) {
|
|
107
|
+
this.src.fail(7, msg.repaired(`<${tag.name}> inside <${parent.ns}> ends the <${parent.ns}> element`, `Put HTML inside <foreignObject>, or move it out of the <${parent.ns}>.`));
|
|
108
|
+
}
|
|
109
|
+
return parent.ns;
|
|
110
|
+
}
|
|
111
|
+
checkHtml(tag) {
|
|
112
|
+
const { name } = tag;
|
|
113
|
+
if (SVG_ONLY.has(name))
|
|
114
|
+
this.src.fail(10, msg.svgOnly(tag.raw));
|
|
115
|
+
if (DOCUMENT.has(name))
|
|
116
|
+
this.server = true;
|
|
117
|
+
if (tag.selfClosing && !VOID.has(name))
|
|
118
|
+
this.src.fail(6, msg.selfClosing(tag.raw));
|
|
119
|
+
const top = this.stack.at(-1);
|
|
120
|
+
if (top === undefined)
|
|
121
|
+
this.rootCtx ??= rootContext(name);
|
|
122
|
+
else if (top.ns === 'html' && top.name === 'template')
|
|
123
|
+
top.inner ??= rootContext(name);
|
|
124
|
+
const { ctx, atRoot } = this.context();
|
|
125
|
+
const hidden = tag.attrs.some((a) => a.kind === 'static' &&
|
|
126
|
+
a.name.toLowerCase() === 'type' &&
|
|
127
|
+
/^["']?hidden["']?$/i.test(a.value ?? ''));
|
|
128
|
+
const repair = startRepair(name, ctx, atRoot, this.stack, hidden);
|
|
129
|
+
if (repair !== undefined)
|
|
130
|
+
this.src.fail(7, repair);
|
|
131
|
+
}
|
|
132
|
+
/** Bound names lower-cased on HTML elements (not properties); rules 4, 7 (duplicates), 13. */
|
|
133
|
+
attributes(tag, ns) {
|
|
134
|
+
const seen = new Set();
|
|
135
|
+
return tag.attrs.map((a) => {
|
|
136
|
+
const lower = ns === 'html' && a.kind !== 'prop' ? a.name.toLowerCase() : a.name;
|
|
137
|
+
if (a.kind === 'prop' && ns === 'html') {
|
|
138
|
+
const fix = formStateFix(tag.name, a.name);
|
|
139
|
+
if (fix !== undefined)
|
|
140
|
+
this.src.fail(4, msg.formState(tag.name, a.name, fix));
|
|
141
|
+
}
|
|
142
|
+
if (a.kind !== 'prop' && a.kind !== 'hook') {
|
|
143
|
+
const key = ns === 'html' ? lower : a.name;
|
|
144
|
+
if (seen.has(key)) {
|
|
145
|
+
this.src.fail(7, msg.repaired(`${key} appears twice on <${tag.raw}>, and the parser keeps only the first`, `Write it once; to mix static and dynamic text, quote one value: ${key}="a \${…}".`));
|
|
146
|
+
}
|
|
147
|
+
seen.add(key);
|
|
148
|
+
}
|
|
149
|
+
if (a.kind === 'static')
|
|
150
|
+
return a;
|
|
151
|
+
const strings = a.strings?.map((s) => this.decode(s));
|
|
152
|
+
return strings === undefined
|
|
153
|
+
? { kind: a.kind, name: lower }
|
|
154
|
+
: { kind: a.kind, name: lower, strings };
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
props(tag) {
|
|
158
|
+
return tag.attrs.flatMap((a) => a.kind === 'static'
|
|
159
|
+
? [[a.name.toLowerCase(), a.value === null ? '' : this.decode(unquote(a.value))]]
|
|
160
|
+
: []);
|
|
161
|
+
}
|
|
162
|
+
decode(raw) {
|
|
163
|
+
return decodeRefs(raw, (ref) => this.src.fail(13, msg.charRef(ref)));
|
|
164
|
+
}
|
|
165
|
+
end(name) {
|
|
166
|
+
this.fresh = undefined;
|
|
167
|
+
const top = this.stack.at(-1);
|
|
168
|
+
if (top?.name === name) {
|
|
169
|
+
this.close(top);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (VOID.has(name) && name !== 'br')
|
|
173
|
+
return; // ignored by the parser
|
|
174
|
+
const open = this.stack.some((el) => el.name === name);
|
|
175
|
+
this.src.fail(7, open && top !== undefined
|
|
176
|
+
? msg.repaired(`</${name}> while <${top.raw}> is still open`, `Close </${top.raw}> first (end tags must match their start tags).`)
|
|
177
|
+
: msg.repaired(`</${name}> has no matching start tag${name === 'br' || name === 'p' ? `, so the parser creates an empty <${name}>` : ''}`, name === 'br' ? 'Write <br>.' : 'Remove it.'));
|
|
178
|
+
}
|
|
179
|
+
close(el) {
|
|
180
|
+
this.stack.pop();
|
|
181
|
+
const hasText = el.children.some((c) => c.type === 'hole' && c.kind === 'text');
|
|
182
|
+
if (hasText && el.children.length !== 1)
|
|
183
|
+
this.src.fail(12, msg.textContent(el.raw));
|
|
184
|
+
}
|
|
185
|
+
finish() {
|
|
186
|
+
for (let el = this.stack.at(-1); el !== undefined; el = this.stack.at(-1))
|
|
187
|
+
this.close(el);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../../src/view/normalize/tree.ts"],"names":[],"mappings":"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","sourcesContent":["// Tree builder for the normalizer (view/01-templates.md \"Normalization\"): turns the tokenizer's\n// tokens into the tree the HTML parser builds for valid markup (WHATWG \"tree construction\":\n// namespaces, void elements, raw text, the leading newline of <pre>/<listing>/<textarea>), so\n// part paths match the browser's DOM. Markup the parser would repair is an error instead of\n// being modelled (rule 7, repairs.ts), as are rules 3, 4, 6, 10, 12 and 13.\nimport { decodeRefs, unquote } from './entities.js';\nimport {\n breaksOutOfForeign,\n contextOf,\n rootContext,\n startRepair,\n textRepair,\n type Context,\n type Ns,\n} from './repairs.js';\nimport { formStateFix, msg, SVG_ONLY, VOID } from './rules.js';\nimport type { Source } from './source.js';\nimport type { AttrToken, ContentMode, Sink, StartTag } from './tokenizer.js';\n\nexport interface ElementNode {\n readonly type: 'el';\n /** Lower-case name, and the name as written. */\n readonly name: string;\n readonly raw: string;\n readonly ns: Ns;\n /** Static and bound attributes; bound names normalized, multi strings decoded. */\n readonly attrs: readonly AttrToken[];\n /** A custom element's static attributes, decoded (server props); undefined otherwise. */\n readonly props: readonly (readonly [string, string])[] | undefined;\n /** No end tag: a void element, or a self-closed SVG/MathML element. */\n readonly empty: boolean;\n readonly children: TreeNode[];\n /** For <template>: the context its first start tag chose. */\n inner?: Context;\n}\n\nexport type TreeNode =\n | ElementNode\n | { readonly type: 'text'; readonly raw: string }\n | { readonly type: 'comment'; readonly raw: string }\n | { readonly type: 'doctype'; readonly raw: string }\n | { readonly type: 'hole'; readonly kind: 'child' | 'text' };\n\nconst RAWTEXT = new Set('script style xmp iframe noembed noframes noscript plaintext'.split(' '));\nconst RCDATA = new Set(['textarea', 'title']);\nconst LEADING_NEWLINE = new Set(['pre', 'listing', 'textarea']);\nconst DOCUMENT = new Set(['html', 'head', 'body']);\n\nfunction integrationPoint(el: ElementNode): boolean {\n if (el.ns === 'svg') return /^(foreignobject|desc|title)$/.test(el.name);\n return el.ns === 'math' && /^(mi|mo|mn|ms|mtext)$/.test(el.name);\n}\n\nexport class TreeBuilder implements Sink {\n readonly root: TreeNode[] = [];\n server = false;\n private readonly stack: ElementNode[] = [];\n private rootCtx: Context | undefined;\n /** The <pre>/<listing>/<textarea> whose first newline the parser drops, if just opened. */\n private fresh: ElementNode | undefined;\n\n constructor(private readonly src: Source) {}\n\n private get children(): TreeNode[] {\n return this.stack.at(-1)?.children ?? this.root;\n }\n\n private context(): { ctx: Context; atRoot: boolean } {\n const top = this.stack.at(-1);\n if (top === undefined) return { ctx: this.rootCtx ?? 'body', atRoot: true };\n if (top.ns !== 'html') return { ctx: 'body', atRoot: false };\n if (top.name === 'template') return { ctx: top.inner ?? 'body', atRoot: true };\n return { ctx: contextOf(top.name), atRoot: false };\n }\n\n private inTemplate(): boolean {\n return this.stack.some((el) => el.ns === 'html' && el.name === 'template');\n }\n\n text(raw: string): void {\n const text = this.fresh !== undefined && raw.startsWith('\\n') ? raw.slice(1) : raw;\n this.fresh = undefined;\n if (text === '') return;\n const { ctx, atRoot } = this.context();\n const repair = textRepair(text, ctx, atRoot);\n if (repair !== undefined) this.src.fail(7, repair);\n this.children.push({ type: 'text', raw: text });\n }\n\n comment(raw: string): void {\n this.fresh = undefined;\n this.children.push({ type: 'comment', raw });\n }\n\n doctype(raw: string): void {\n this.fresh = undefined;\n this.server = true;\n this.children.push({ type: 'doctype', raw });\n }\n\n hole(kind: 'child' | 'text'): void {\n this.fresh = undefined;\n if (this.inTemplate()) this.src.fail(3, msg.inTemplate());\n this.children.push({ type: 'hole', kind });\n }\n\n start(tag: StartTag): ContentMode {\n this.fresh = undefined;\n const { name } = tag;\n const ns = this.namespace(tag);\n if (ns === 'html') this.checkHtml(tag);\n if (this.inTemplate() && tag.attrs.some((a) => a.kind !== 'static')) {\n this.src.fail(3, msg.inTemplate());\n }\n const empty = ns === 'html' ? VOID.has(name) : tag.selfClosing;\n const custom = ns === 'html' && name.includes('-');\n const el: ElementNode = {\n type: 'el',\n name,\n raw: tag.raw,\n ns,\n attrs: this.attributes(tag, ns),\n props: custom ? this.props(tag) : undefined,\n empty,\n children: [],\n };\n this.children.push(el);\n if (empty) return 'data';\n this.stack.push(el);\n if (ns !== 'html') return 'data';\n if (LEADING_NEWLINE.has(name)) this.fresh = el;\n return RAWTEXT.has(name) ? 'rawtext' : RCDATA.has(name) ? 'rcdata' : 'data';\n }\n\n private namespace(tag: StartTag): Ns {\n const parent = this.stack.at(-1);\n if (parent === undefined || parent.ns === 'html' || integrationPoint(parent)) {\n return tag.name === 'svg' ? 'svg' : tag.name === 'math' ? 'math' : 'html';\n }\n if (parent.name === 'annotation-xml' && tag.name === 'svg') return 'svg';\n const fontAttr = tag.attrs.some((a) => /^(color|face|size)$/i.test(a.name));\n if (breaksOutOfForeign(tag.name, fontAttr)) {\n this.src.fail(\n 7,\n msg.repaired(\n `<${tag.name}> inside <${parent.ns}> ends the <${parent.ns}> element`,\n `Put HTML inside <foreignObject>, or move it out of the <${parent.ns}>.`,\n ),\n );\n }\n return parent.ns;\n }\n\n private checkHtml(tag: StartTag): void {\n const { name } = tag;\n if (SVG_ONLY.has(name)) this.src.fail(10, msg.svgOnly(tag.raw));\n if (DOCUMENT.has(name)) this.server = true;\n if (tag.selfClosing && !VOID.has(name)) this.src.fail(6, msg.selfClosing(tag.raw));\n const top = this.stack.at(-1);\n if (top === undefined) this.rootCtx ??= rootContext(name);\n else if (top.ns === 'html' && top.name === 'template') top.inner ??= rootContext(name);\n const { ctx, atRoot } = this.context();\n const hidden = tag.attrs.some(\n (a) =>\n a.kind === 'static' &&\n a.name.toLowerCase() === 'type' &&\n /^[\"']?hidden[\"']?$/i.test(a.value ?? ''),\n );\n const repair = startRepair(name, ctx, atRoot, this.stack, hidden);\n if (repair !== undefined) this.src.fail(7, repair);\n }\n\n /** Bound names lower-cased on HTML elements (not properties); rules 4, 7 (duplicates), 13. */\n private attributes(tag: StartTag, ns: Ns): AttrToken[] {\n const seen = new Set<string>();\n return tag.attrs.map((a) => {\n const lower = ns === 'html' && a.kind !== 'prop' ? a.name.toLowerCase() : a.name;\n if (a.kind === 'prop' && ns === 'html') {\n const fix = formStateFix(tag.name, a.name);\n if (fix !== undefined) this.src.fail(4, msg.formState(tag.name, a.name, fix));\n }\n if (a.kind !== 'prop' && a.kind !== 'hook') {\n const key = ns === 'html' ? lower : a.name;\n if (seen.has(key)) {\n this.src.fail(\n 7,\n msg.repaired(\n `${key} appears twice on <${tag.raw}>, and the parser keeps only the first`,\n `Write it once; to mix static and dynamic text, quote one value: ${key}=\"a \\${…}\".`,\n ),\n );\n }\n seen.add(key);\n }\n if (a.kind === 'static') return a;\n const strings = a.strings?.map((s) => this.decode(s));\n return strings === undefined\n ? { kind: a.kind, name: lower }\n : { kind: a.kind, name: lower, strings };\n });\n }\n\n private props(tag: StartTag): [string, string][] {\n return tag.attrs.flatMap((a) =>\n a.kind === 'static'\n ? [[a.name.toLowerCase(), a.value === null ? '' : this.decode(unquote(a.value))]]\n : [],\n );\n }\n\n private decode(raw: string): string {\n return decodeRefs(raw, (ref) => this.src.fail(13, msg.charRef(ref)));\n }\n\n end(name: string): void {\n this.fresh = undefined;\n const top = this.stack.at(-1);\n if (top?.name === name) {\n this.close(top);\n return;\n }\n if (VOID.has(name) && name !== 'br') return; // ignored by the parser\n const open = this.stack.some((el) => el.name === name);\n this.src.fail(\n 7,\n open && top !== undefined\n ? msg.repaired(\n `</${name}> while <${top.raw}> is still open`,\n `Close </${top.raw}> first (end tags must match their start tags).`,\n )\n : msg.repaired(\n `</${name}> has no matching start tag${name === 'br' || name === 'p' ? `, so the parser creates an empty <${name}>` : ''}`,\n name === 'br' ? 'Write <br>.' : 'Remove it.',\n ),\n );\n }\n\n private close(el: ElementNode): void {\n this.stack.pop();\n const hasText = el.children.some((c) => c.type === 'hole' && c.kind === 'text');\n if (hasText && el.children.length !== 1) this.src.fail(12, msg.textContent(el.raw));\n }\n\n finish(): void {\n for (let el = this.stack.at(-1); el !== undefined; el = this.stack.at(-1)) this.close(el);\n }\n}\n"]}
|