@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,136 @@
|
|
|
1
|
+
import { ATTR_PART, BOOL_PART, CHILD_BEFORE, CHILD_END, CHILD_SOLE, HOOK_PART, MULTI_PART, PROP_PART, TEXT_PART, } from './types.js';
|
|
2
|
+
const ANCHOR = '<!---->';
|
|
3
|
+
class Emitter {
|
|
4
|
+
/** A page shell (`server`): never hydrated, so it needs no anchors (06). */
|
|
5
|
+
constructor(server) {
|
|
6
|
+
this.server = server;
|
|
7
|
+
this.html = '';
|
|
8
|
+
this.parts = [];
|
|
9
|
+
this.segments = [];
|
|
10
|
+
}
|
|
11
|
+
/** Static markup: appended to the HTML and to the last string segment. */
|
|
12
|
+
out(text) {
|
|
13
|
+
if (text === '')
|
|
14
|
+
return;
|
|
15
|
+
this.html += text;
|
|
16
|
+
const last = this.segments.length - 1;
|
|
17
|
+
const prev = this.segments[last];
|
|
18
|
+
if (typeof prev === 'string')
|
|
19
|
+
this.segments[last] = prev + text;
|
|
20
|
+
else
|
|
21
|
+
this.segments.push(text);
|
|
22
|
+
}
|
|
23
|
+
op(segment) {
|
|
24
|
+
this.segments.push(segment);
|
|
25
|
+
}
|
|
26
|
+
/** `parent`: the parent element's name; undefined at the template root. */
|
|
27
|
+
children(nodes, path, parent) {
|
|
28
|
+
const shape = [];
|
|
29
|
+
nodes.forEach((node, k) => {
|
|
30
|
+
switch (node.type) {
|
|
31
|
+
case 'text':
|
|
32
|
+
this.out(node.raw);
|
|
33
|
+
shape.push('#text');
|
|
34
|
+
return;
|
|
35
|
+
case 'comment':
|
|
36
|
+
this.out(node.raw);
|
|
37
|
+
shape.push('#comment');
|
|
38
|
+
return;
|
|
39
|
+
case 'doctype':
|
|
40
|
+
// Server templates only; the browser drops a doctype inside a fragment.
|
|
41
|
+
this.out(node.raw);
|
|
42
|
+
return;
|
|
43
|
+
case 'el':
|
|
44
|
+
shape.push(this.element(node, [...path, shape.length]));
|
|
45
|
+
return;
|
|
46
|
+
case 'hole':
|
|
47
|
+
if (node.kind === 'text') {
|
|
48
|
+
this.parts.push([TEXT_PART, path]);
|
|
49
|
+
this.op({ k: 'text' });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.child(nodes, k, path, shape, parent);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return shape;
|
|
56
|
+
}
|
|
57
|
+
/** A child hole: inserts before the next static element or comment, the end, or an anchor. */
|
|
58
|
+
child(nodes, k, path, shape, parent) {
|
|
59
|
+
const next = nodes[k + 1];
|
|
60
|
+
this.op(parent === undefined ? { k: 'child' } : { k: 'child', in: parent });
|
|
61
|
+
if (next === undefined) {
|
|
62
|
+
this.parts.push([nodes.length === 1 ? CHILD_SOLE : CHILD_END, path]);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.parts.push([CHILD_BEFORE, path, shape.length]);
|
|
66
|
+
if (next.type === 'el' || next.type === 'comment' || this.server)
|
|
67
|
+
return;
|
|
68
|
+
this.out(ANCHOR);
|
|
69
|
+
shape.push('#comment');
|
|
70
|
+
}
|
|
71
|
+
element(el, path) {
|
|
72
|
+
let open = `<${el.raw}`;
|
|
73
|
+
for (const a of el.attrs) {
|
|
74
|
+
if (a.kind === 'static')
|
|
75
|
+
open += a.value === null ? ` ${a.name}` : ` ${a.name}=${a.value}`;
|
|
76
|
+
}
|
|
77
|
+
if (el.props !== undefined) {
|
|
78
|
+
this.html += open;
|
|
79
|
+
this.op({ k: 'open', tag: el.name, html: open, attrs: el.props });
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
this.out(open);
|
|
83
|
+
for (const a of el.attrs) {
|
|
84
|
+
if (a.kind === 'static')
|
|
85
|
+
continue;
|
|
86
|
+
if (a.kind === 'hook') {
|
|
87
|
+
this.parts.push([HOOK_PART, path]);
|
|
88
|
+
this.op({ k: 'hook' });
|
|
89
|
+
}
|
|
90
|
+
else if (a.kind === 'attr' && a.strings !== undefined) {
|
|
91
|
+
this.parts.push([MULTI_PART, path, a.name, a.strings]);
|
|
92
|
+
this.op({ k: 'attr', name: a.name, strings: a.strings });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const kind = a.kind === 'attr' ? ATTR_PART : a.kind === 'bool' ? BOOL_PART : PROP_PART;
|
|
96
|
+
this.parts.push([kind, path, a.name]);
|
|
97
|
+
this.op({ k: a.kind, name: a.name });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const end = el.empty && el.ns !== 'html' ? '/>' : '>';
|
|
101
|
+
if (el.props !== undefined) {
|
|
102
|
+
this.html += end;
|
|
103
|
+
this.op({ k: 'openEnd' });
|
|
104
|
+
}
|
|
105
|
+
else
|
|
106
|
+
this.out(end);
|
|
107
|
+
if (el.empty)
|
|
108
|
+
return [el.name, []];
|
|
109
|
+
// The parser drops a newline right after <pre>, <listing> and <textarea>: give it one to
|
|
110
|
+
// drop, so a hole or text starting with a newline keeps its own.
|
|
111
|
+
const first = el.children[0];
|
|
112
|
+
const newline = el.ns === 'html' && /^(pre|listing|textarea)$/.test(el.name);
|
|
113
|
+
if (newline &&
|
|
114
|
+
(first?.type === 'hole' || (first?.type === 'text' && first.raw.startsWith('\n')))) {
|
|
115
|
+
this.out('\n');
|
|
116
|
+
}
|
|
117
|
+
const shape = this.children(el.children, path, el.name);
|
|
118
|
+
if (el.props !== undefined) {
|
|
119
|
+
this.html += `</${el.raw}>`;
|
|
120
|
+
this.op({ k: 'close', tag: el.name });
|
|
121
|
+
}
|
|
122
|
+
else
|
|
123
|
+
this.out(`</${el.raw}>`);
|
|
124
|
+
return [el.name, shape];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Emits the HTML, part table, segments and shape for a template's tree. A `server` template
|
|
129
|
+
* (a page shell) gets no anchors: hydration never walks it, and the browser never renders it.
|
|
130
|
+
*/
|
|
131
|
+
export function emit(root, server) {
|
|
132
|
+
const e = new Emitter(server);
|
|
133
|
+
const shape = e.children(root, []);
|
|
134
|
+
return { html: e.html, parts: e.parts, segments: e.segments, shape };
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=emit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../../../src/view/normalize/emit.ts"],"names":[],"mappings":"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","sourcesContent":["// Emits the template object's markup from the normalizer's tree (view/01-templates.md steps 3\n// and 4): the client HTML (bound attributes removed, anchors added by view/02-bindings.md's\n// anchor rule), the server segments (types.ts), the part table with child-index paths, and the\n// shape of the DOM the browser must build (checked by prepare.ts).\nimport type { ElementNode, TreeNode } from './tree.js';\nimport {\n ATTR_PART,\n BOOL_PART,\n CHILD_BEFORE,\n CHILD_END,\n CHILD_SOLE,\n HOOK_PART,\n MULTI_PART,\n PROP_PART,\n TEXT_PART,\n type PartSpec,\n type Path,\n type Segment,\n type Shape,\n type ShapeNode,\n} from './types.js';\n\nconst ANCHOR = '<!---->';\n\nexport interface Emitted {\n readonly html: string;\n readonly parts: PartSpec[];\n readonly segments: Segment[];\n readonly shape: Shape;\n}\n\nclass Emitter {\n /** A page shell (`server`): never hydrated, so it needs no anchors (06). */\n constructor(private readonly server: boolean) {}\n\n html = '';\n readonly parts: PartSpec[] = [];\n readonly segments: Segment[] = [];\n\n /** Static markup: appended to the HTML and to the last string segment. */\n out(text: string): void {\n if (text === '') return;\n this.html += text;\n const last = this.segments.length - 1;\n const prev = this.segments[last];\n if (typeof prev === 'string') this.segments[last] = prev + text;\n else this.segments.push(text);\n }\n\n op(segment: Segment): void {\n this.segments.push(segment);\n }\n\n /** `parent`: the parent element's name; undefined at the template root. */\n children(nodes: readonly TreeNode[], path: Path, parent?: string): Shape {\n const shape: ShapeNode[] = [];\n nodes.forEach((node, k) => {\n switch (node.type) {\n case 'text':\n this.out(node.raw);\n shape.push('#text');\n return;\n case 'comment':\n this.out(node.raw);\n shape.push('#comment');\n return;\n case 'doctype':\n // Server templates only; the browser drops a doctype inside a fragment.\n this.out(node.raw);\n return;\n case 'el':\n shape.push(this.element(node, [...path, shape.length]));\n return;\n case 'hole':\n if (node.kind === 'text') {\n this.parts.push([TEXT_PART, path]);\n this.op({ k: 'text' });\n return;\n }\n this.child(nodes, k, path, shape, parent);\n }\n });\n return shape;\n }\n\n /** A child hole: inserts before the next static element or comment, the end, or an anchor. */\n private child(\n nodes: readonly TreeNode[],\n k: number,\n path: Path,\n shape: ShapeNode[],\n parent: string | undefined,\n ): void {\n const next = nodes[k + 1];\n this.op(parent === undefined ? { k: 'child' } : { k: 'child', in: parent });\n if (next === undefined) {\n this.parts.push([nodes.length === 1 ? CHILD_SOLE : CHILD_END, path]);\n return;\n }\n this.parts.push([CHILD_BEFORE, path, shape.length]);\n if (next.type === 'el' || next.type === 'comment' || this.server) return;\n this.out(ANCHOR);\n shape.push('#comment');\n }\n\n private element(el: ElementNode, path: Path): ShapeNode {\n let open = `<${el.raw}`;\n for (const a of el.attrs) {\n if (a.kind === 'static') open += a.value === null ? ` ${a.name}` : ` ${a.name}=${a.value}`;\n }\n if (el.props !== undefined) {\n this.html += open;\n this.op({ k: 'open', tag: el.name, html: open, attrs: el.props });\n } else this.out(open);\n for (const a of el.attrs) {\n if (a.kind === 'static') continue;\n if (a.kind === 'hook') {\n this.parts.push([HOOK_PART, path]);\n this.op({ k: 'hook' });\n } else if (a.kind === 'attr' && a.strings !== undefined) {\n this.parts.push([MULTI_PART, path, a.name, a.strings]);\n this.op({ k: 'attr', name: a.name, strings: a.strings });\n } else {\n const kind = a.kind === 'attr' ? ATTR_PART : a.kind === 'bool' ? BOOL_PART : PROP_PART;\n this.parts.push([kind, path, a.name]);\n this.op({ k: a.kind, name: a.name });\n }\n }\n const end = el.empty && el.ns !== 'html' ? '/>' : '>';\n if (el.props !== undefined) {\n this.html += end;\n this.op({ k: 'openEnd' });\n } else this.out(end);\n if (el.empty) return [el.name, []];\n // The parser drops a newline right after <pre>, <listing> and <textarea>: give it one to\n // drop, so a hole or text starting with a newline keeps its own.\n const first = el.children[0];\n const newline = el.ns === 'html' && /^(pre|listing|textarea)$/.test(el.name);\n if (\n newline &&\n (first?.type === 'hole' || (first?.type === 'text' && first.raw.startsWith('\\n')))\n ) {\n this.out('\\n');\n }\n const shape = this.children(el.children, path, el.name);\n if (el.props !== undefined) {\n this.html += `</${el.raw}>`;\n this.op({ k: 'close', tag: el.name });\n } else this.out(`</${el.raw}>`);\n return [el.name, shape];\n }\n}\n\n/**\n * Emits the HTML, part table, segments and shape for a template's tree. A `server` template\n * (a page shell) gets no anchors: hydration never walks it, and the browser never renders it.\n */\nexport function emit(root: readonly TreeNode[], server: boolean): Emitted {\n const e = new Emitter(server);\n const shape = e.children(root, []);\n return { html: e.html, parts: e.parts, segments: e.segments, shape };\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Decodes `raw`; `unknown(name)` is called (and must throw) for an unsupported name. */
|
|
2
|
+
export declare function decodeRefs(raw: string, unknown: (name: string) => never): string;
|
|
3
|
+
/** The value of a static attribute as written (`"a"`, `'a'`, `a`), without its quotes. */
|
|
4
|
+
export declare function unquote(value: string): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Character references in attribute text the client sets itself: the static pieces of a
|
|
2
|
+
// multi-attribute and a custom element's static attributes (its props on the server). The
|
|
3
|
+
// WHATWG named-reference table has over 2,000 entries, so only numeric references and the
|
|
4
|
+
// common names are decoded; any other `&name;` is rule 13 (proposed). Text and plain static
|
|
5
|
+
// attributes are never decoded here: they stay in the template HTML for the browser to parse.
|
|
6
|
+
const NAMED = {
|
|
7
|
+
amp: '&',
|
|
8
|
+
lt: '<',
|
|
9
|
+
gt: '>',
|
|
10
|
+
quot: '"',
|
|
11
|
+
apos: "'",
|
|
12
|
+
nbsp: ' ',
|
|
13
|
+
};
|
|
14
|
+
const REF = /&(#[0-9]+|#[xX][0-9a-fA-F]+|[A-Za-z][A-Za-z0-9]*);/g;
|
|
15
|
+
function codePoint(n) {
|
|
16
|
+
const invalid = n === 0 || n > 0x10ffff || (n >= 0xd800 && n <= 0xdfff);
|
|
17
|
+
return String.fromCodePoint(invalid ? 0xfffd : n);
|
|
18
|
+
}
|
|
19
|
+
/** Decodes `raw`; `unknown(name)` is called (and must throw) for an unsupported name. */
|
|
20
|
+
export function decodeRefs(raw, unknown) {
|
|
21
|
+
if (!raw.includes('&'))
|
|
22
|
+
return raw;
|
|
23
|
+
return raw.replace(REF, (_, ref) => {
|
|
24
|
+
if (ref.startsWith('#x') || ref.startsWith('#X'))
|
|
25
|
+
return codePoint(parseInt(ref.slice(2), 16));
|
|
26
|
+
if (ref.startsWith('#'))
|
|
27
|
+
return codePoint(parseInt(ref.slice(1), 10));
|
|
28
|
+
return NAMED[ref] ?? unknown(ref);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** The value of a static attribute as written (`"a"`, `'a'`, `a`), without its quotes. */
|
|
32
|
+
export function unquote(value) {
|
|
33
|
+
const q = value.charAt(0);
|
|
34
|
+
return (q === '"' || q === "'") && value.endsWith(q) && value.length >= 2
|
|
35
|
+
? value.slice(1, -1)
|
|
36
|
+
: value;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=entities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities.js","sourceRoot":"","sources":["../../../src/view/normalize/entities.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,0FAA0F;AAC1F,0FAA0F;AAC1F,4FAA4F;AAC5F,8FAA8F;AAE9F,MAAM,KAAK,GAAuC;IAChD,GAAG,EAAE,GAAG;IACR,EAAE,EAAE,GAAG;IACP,EAAE,EAAE,GAAG;IACP,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,MAAM,GAAG,GAAG,qDAAqD,CAAC;AAElE,SAAS,SAAS,CAAC,CAAS;IAC1B,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,OAAgC;IACtE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACnC,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE;QACzC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/F,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QACvE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC","sourcesContent":["// Character references in attribute text the client sets itself: the static pieces of a\n// multi-attribute and a custom element's static attributes (its props on the server). The\n// WHATWG named-reference table has over 2,000 entries, so only numeric references and the\n// common names are decoded; any other `&name;` is rule 13 (proposed). Text and plain static\n// attributes are never decoded here: they stay in the template HTML for the browser to parse.\n\nconst NAMED: Record<string, string | undefined> = {\n amp: '&',\n lt: '<',\n gt: '>',\n quot: '\"',\n apos: \"'\",\n nbsp: ' ',\n};\n\nconst REF = /&(#[0-9]+|#[xX][0-9a-fA-F]+|[A-Za-z][A-Za-z0-9]*);/g;\n\nfunction codePoint(n: number): string {\n const invalid = n === 0 || n > 0x10ffff || (n >= 0xd800 && n <= 0xdfff);\n return String.fromCodePoint(invalid ? 0xfffd : n);\n}\n\n/** Decodes `raw`; `unknown(name)` is called (and must throw) for an unsupported name. */\nexport function decodeRefs(raw: string, unknown: (name: string) => never): string {\n if (!raw.includes('&')) return raw;\n return raw.replace(REF, (_, ref: string) => {\n if (ref.startsWith('#x') || ref.startsWith('#X')) return codePoint(parseInt(ref.slice(2), 16));\n if (ref.startsWith('#')) return codePoint(parseInt(ref.slice(1), 10));\n return NAMED[ref] ?? unknown(ref);\n });\n}\n\n/** The value of a static attribute as written (`\"a\"`, `'a'`, `a`), without its quotes. */\nexport function unquote(value: string): string {\n const q = value.charAt(0);\n return (q === '\"' || q === \"'\") && value.endsWith(q) && value.length >= 2\n ? value.slice(1, -1)\n : value;\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const RULES_DOC = "docs/design-docs/view/09-template-rules.md";
|
|
2
|
+
/** Rule 11's message, here so the client's template cache needn't load the rule messages. */
|
|
3
|
+
export declare const SERVER_ONLY: string;
|
|
4
|
+
/** A template breaks rule `rule` of view/09-template-rules.md. */
|
|
5
|
+
export declare class TemplateError extends Error {
|
|
6
|
+
readonly rule: number;
|
|
7
|
+
readonly loc: string | undefined;
|
|
8
|
+
constructor(rule: number, message: string, loc?: string, near?: string);
|
|
9
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// TemplateError: what the template rules (view/09-template-rules.md) throw. Kept apart from the
|
|
2
|
+
// rule checks so code that only needs the class (template-element.ts) stays small.
|
|
3
|
+
export const RULES_DOC = 'docs/design-docs/view/09-template-rules.md';
|
|
4
|
+
/** Rule 11's message, here so the client's template cache needn't load the rule messages. */
|
|
5
|
+
export const SERVER_ONLY = 'This template has document-level tags (<!doctype>, <html>, <head> or <body>): it is a page ' +
|
|
6
|
+
'shell, and only the server renders those. Render it with @gyral/core/server, and render ' +
|
|
7
|
+
'components (not the page) in the browser.';
|
|
8
|
+
/** A template breaks rule `rule` of view/09-template-rules.md. */
|
|
9
|
+
export class TemplateError extends Error {
|
|
10
|
+
constructor(rule, message, loc, near) {
|
|
11
|
+
const doc = ` (${RULES_DOC}, rule ${String(rule)})`;
|
|
12
|
+
const context = (near === undefined ? '' : `\n near: ${near}`) + (loc === undefined ? '' : `\n at ${loc}`);
|
|
13
|
+
super(`[gyral template rule ${String(rule)}] ${message}${doc}${context}`);
|
|
14
|
+
this.name = 'TemplateError';
|
|
15
|
+
this.rule = rule;
|
|
16
|
+
this.loc = loc;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/view/normalize/errors.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,mFAAmF;AAEnF,MAAM,CAAC,MAAM,SAAS,GAAG,4CAA4C,CAAC;AAEtE,6FAA6F;AAC7F,MAAM,CAAC,MAAM,WAAW,GACtB,6FAA6F;IAC7F,0FAA0F;IAC1F,2CAA2C,CAAC;AAE9C,kEAAkE;AAClE,MAAM,OAAO,aAAc,SAAQ,KAAK;IAItC,YAAY,IAAY,EAAE,OAAe,EAAE,GAAY,EAAE,IAAa;QACpE,MAAM,GAAG,GAAG,KAAK,SAAS,UAAU,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACpD,MAAM,OAAO,GACX,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;QAC/F,KAAK,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,KAAK,OAAO,GAAG,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF","sourcesContent":["// TemplateError: what the template rules (view/09-template-rules.md) throw. Kept apart from the\n// rule checks so code that only needs the class (template-element.ts) stays small.\n\nexport const RULES_DOC = 'docs/design-docs/view/09-template-rules.md';\n\n/** Rule 11's message, here so the client's template cache needn't load the rule messages. */\nexport const SERVER_ONLY =\n 'This template has document-level tags (<!doctype>, <html>, <head> or <body>): it is a page ' +\n 'shell, and only the server renders those. Render it with @gyral/core/server, and render ' +\n 'components (not the page) in the browser.';\n\n/** A template breaks rule `rule` of view/09-template-rules.md. */\nexport class TemplateError extends Error {\n readonly rule: number;\n readonly loc: string | undefined;\n\n constructor(rule: number, message: string, loc?: string, near?: string) {\n const doc = ` (${RULES_DOC}, rule ${String(rule)})`;\n const context =\n (near === undefined ? '' : `\\n near: ${near}`) + (loc === undefined ? '' : `\\n at ${loc}`);\n super(`[gyral template rule ${String(rule)}] ${message}${doc}${context}`);\n this.name = 'TemplateError';\n this.rule = rule;\n this.loc = loc;\n }\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The id of a template's normalized strings. */
|
|
2
|
+
export declare function templateId(strings: readonly string[]): string;
|
|
3
|
+
/**
|
|
4
|
+
* Development: records `id` → strings and warns once when two different normalized templates
|
|
5
|
+
* share an id (the compiler fails the build instead).
|
|
6
|
+
*/
|
|
7
|
+
export declare function recordTemplateId(id: string, strings: readonly string[]): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Template ids (view/01-templates.md "Template ids"): a deterministic 53-bit hash of the
|
|
2
|
+
// normalized strings, printed in base 36. Two independent 32-bit multiply-xorshift lanes over
|
|
3
|
+
// the UTF-16 code units, with each string's length mixed in first so ["a", "b"] and ["ab"]
|
|
4
|
+
// differ; a final avalanche, then 21 bits of one lane and 32 of the other. Only Math.imul and
|
|
5
|
+
// integer ops: the same result in Node, browsers and the compiler.
|
|
6
|
+
function mix(h, k, m) {
|
|
7
|
+
return Math.imul(h ^ k, m);
|
|
8
|
+
}
|
|
9
|
+
function avalanche(h, a, b) {
|
|
10
|
+
let x = Math.imul(h ^ (h >>> 16), a);
|
|
11
|
+
x = Math.imul(x ^ (x >>> 13), b);
|
|
12
|
+
return x ^ (x >>> 16);
|
|
13
|
+
}
|
|
14
|
+
/** The id of a template's normalized strings. */
|
|
15
|
+
export function templateId(strings) {
|
|
16
|
+
let h1 = 0x9e3779b9 ^ strings.length;
|
|
17
|
+
let h2 = 0x85ebca6b ^ strings.length;
|
|
18
|
+
for (const s of strings) {
|
|
19
|
+
h1 = mix(h1, s.length, 0x2d51);
|
|
20
|
+
h2 = mix(h2, s.length, 0x1b873593);
|
|
21
|
+
for (let k = 0; k < s.length; k++) {
|
|
22
|
+
const c = s.charCodeAt(k);
|
|
23
|
+
h1 = mix(h1, c, 0x9e3779b1);
|
|
24
|
+
h2 = mix(h2, c, 0x5bd1e995);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const lo = avalanche(h1 ^ Math.imul(h2, 0x27d4eb2f), 0x85ebca6b, 0xc2b2ae35) >>> 0;
|
|
28
|
+
const hi = avalanche(h2 ^ lo, 0x2c1b3c6d, 0x297a2d39) & 0x1fffff;
|
|
29
|
+
return (hi * 0x100000000 + lo).toString(36);
|
|
30
|
+
}
|
|
31
|
+
const seen = new Map();
|
|
32
|
+
/**
|
|
33
|
+
* Development: records `id` → strings and warns once when two different normalized templates
|
|
34
|
+
* share an id (the compiler fails the build instead).
|
|
35
|
+
*/
|
|
36
|
+
export function recordTemplateId(id, strings) {
|
|
37
|
+
const key = JSON.stringify(strings);
|
|
38
|
+
const known = seen.get(id);
|
|
39
|
+
if (known === undefined)
|
|
40
|
+
seen.set(id, key);
|
|
41
|
+
else if (known !== key) {
|
|
42
|
+
console.warn(`gyral: two different templates share the id ${id}; server output and hydration may ` +
|
|
43
|
+
`confuse them. Change one template slightly (e.g. add a class) and report this ` +
|
|
44
|
+
`collision.\n ${known}\n ${key}`);
|
|
45
|
+
seen.set(id, key);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../../src/view/normalize/id.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,8FAA8F;AAC9F,2FAA2F;AAC3F,8FAA8F;AAC9F,mEAAmE;AAEnE,SAAS,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IAChD,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,UAAU,CAAC,OAA0B;IACnD,IAAI,EAAE,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,EAAE,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,EAAE,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAC1B,EAAE,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;YAC5B,EAAE,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IACnF,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC;IACjE,OAAO,CAAC,EAAE,GAAG,WAAW,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,OAA0B;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3B,IAAI,KAAK,KAAK,SAAS;QAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;SACtC,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CACV,+CAA+C,EAAE,oCAAoC;YACnF,gFAAgF;YAChF,iBAAiB,KAAK,OAAO,GAAG,EAAE,CACrC,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACpB,CAAC;AACH,CAAC","sourcesContent":["// Template ids (view/01-templates.md \"Template ids\"): a deterministic 53-bit hash of the\n// normalized strings, printed in base 36. Two independent 32-bit multiply-xorshift lanes over\n// the UTF-16 code units, with each string's length mixed in first so [\"a\", \"b\"] and [\"ab\"]\n// differ; a final avalanche, then 21 bits of one lane and 32 of the other. Only Math.imul and\n// integer ops: the same result in Node, browsers and the compiler.\n\nfunction mix(h: number, k: number, m: number): number {\n return Math.imul(h ^ k, m);\n}\n\nfunction avalanche(h: number, a: number, b: number): number {\n let x = Math.imul(h ^ (h >>> 16), a);\n x = Math.imul(x ^ (x >>> 13), b);\n return x ^ (x >>> 16);\n}\n\n/** The id of a template's normalized strings. */\nexport function templateId(strings: readonly string[]): string {\n let h1 = 0x9e3779b9 ^ strings.length;\n let h2 = 0x85ebca6b ^ strings.length;\n for (const s of strings) {\n h1 = mix(h1, s.length, 0x2d51);\n h2 = mix(h2, s.length, 0x1b873593);\n for (let k = 0; k < s.length; k++) {\n const c = s.charCodeAt(k);\n h1 = mix(h1, c, 0x9e3779b1);\n h2 = mix(h2, c, 0x5bd1e995);\n }\n }\n const lo = avalanche(h1 ^ Math.imul(h2, 0x27d4eb2f), 0x85ebca6b, 0xc2b2ae35) >>> 0;\n const hi = avalanche(h2 ^ lo, 0x2c1b3c6d, 0x297a2d39) & 0x1fffff;\n return (hi * 0x100000000 + lo).toString(36);\n}\n\nconst seen = new Map<string, string>();\n\n/**\n * Development: records `id` → strings and warns once when two different normalized templates\n * share an id (the compiler fails the build instead).\n */\nexport function recordTemplateId(id: string, strings: readonly string[]): void {\n const key = JSON.stringify(strings);\n const known = seen.get(id);\n if (known === undefined) seen.set(id, key);\n else if (known !== key) {\n console.warn(\n `gyral: two different templates share the id ${id}; server output and hydration may ` +\n `confuse them. Change one template slightly (e.g. add a class) and report this ` +\n `collision.\\n ${known}\\n ${key}`,\n );\n seen.set(id, key);\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NormalizedTemplate, Shape } from './types.js';
|
|
2
|
+
export interface Analysis {
|
|
3
|
+
readonly template: NormalizedTemplate;
|
|
4
|
+
/** The whitespace-normalized strings the id was computed from. */
|
|
5
|
+
readonly strings: readonly string[];
|
|
6
|
+
/** The DOM the browser must build from `template.html` (checked by the runtime preparer). */
|
|
7
|
+
readonly shape: Shape;
|
|
8
|
+
}
|
|
9
|
+
/** The template object plus what the runtime preparer checks it against. */
|
|
10
|
+
export declare function analyze(strings: readonly string[], loc?: string): Analysis;
|
|
11
|
+
/**
|
|
12
|
+
* Normalizes a template's static strings into its template object (JSON data). Throws a
|
|
13
|
+
* TemplateError for markup that breaks a template rule.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalize(strings: readonly string[], loc?: string): NormalizedTemplate;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The normalizer (view/01-templates.md "Normalization"): one pure function from a call site's
|
|
2
|
+
// static strings to its template object, shared by the Vite compiler, the browser's runtime
|
|
3
|
+
// preparer and the server renderer. Steps: minify whitespace (holes never move), tokenize and
|
|
4
|
+
// build the tree (rules of view/09-template-rules.md), emit HTML, segments and parts, and hash
|
|
5
|
+
// the normalized strings into the id. No DOM: it runs in Node and in the browser.
|
|
6
|
+
import { emit } from './emit.js';
|
|
7
|
+
import { templateId } from './id.js';
|
|
8
|
+
import { Source } from './source.js';
|
|
9
|
+
import { tokenize } from './tokenizer.js';
|
|
10
|
+
import { TreeBuilder } from './tree.js';
|
|
11
|
+
import { minifyStrings } from './whitespace.js';
|
|
12
|
+
/** The template object plus what the runtime preparer checks it against. */
|
|
13
|
+
export function analyze(strings, loc) {
|
|
14
|
+
const normalized = minifyStrings(strings);
|
|
15
|
+
const src = new Source(normalized, loc);
|
|
16
|
+
const tree = new TreeBuilder(src);
|
|
17
|
+
tokenize(src, tree);
|
|
18
|
+
const { html, parts, segments, shape } = emit(tree.root, tree.server);
|
|
19
|
+
const template = {
|
|
20
|
+
id: templateId(normalized),
|
|
21
|
+
html,
|
|
22
|
+
parts,
|
|
23
|
+
...(tree.server ? { server: true } : {}),
|
|
24
|
+
segments,
|
|
25
|
+
...(loc === undefined ? {} : { loc }),
|
|
26
|
+
};
|
|
27
|
+
return { template, strings: normalized, shape };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Normalizes a template's static strings into its template object (JSON data). Throws a
|
|
31
|
+
* TemplateError for markup that breaks a template rule.
|
|
32
|
+
*/
|
|
33
|
+
export function normalize(strings, loc) {
|
|
34
|
+
return analyze(strings, loc).template;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/view/normalize/normalize.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,4FAA4F;AAC5F,8FAA8F;AAC9F,+FAA+F;AAC/F,kFAAkF;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAUhD,4EAA4E;AAC5E,MAAM,UAAU,OAAO,CAAC,OAA0B,EAAE,GAAY;IAC9D,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACpB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAuB;QACnC,EAAE,EAAE,UAAU,CAAC,UAAU,CAAC;QAC1B,IAAI;QACJ,KAAK;QACL,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,QAAQ;QACR,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;KACtC,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAA0B,EAAE,GAAY;IAChE,OAAO,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC;AACxC,CAAC","sourcesContent":["// The normalizer (view/01-templates.md \"Normalization\"): one pure function from a call site's\n// static strings to its template object, shared by the Vite compiler, the browser's runtime\n// preparer and the server renderer. Steps: minify whitespace (holes never move), tokenize and\n// build the tree (rules of view/09-template-rules.md), emit HTML, segments and parts, and hash\n// the normalized strings into the id. No DOM: it runs in Node and in the browser.\nimport { emit } from './emit.js';\nimport { templateId } from './id.js';\nimport { Source } from './source.js';\nimport { tokenize } from './tokenizer.js';\nimport { TreeBuilder } from './tree.js';\nimport type { NormalizedTemplate, Shape } from './types.js';\nimport { minifyStrings } from './whitespace.js';\n\nexport interface Analysis {\n readonly template: NormalizedTemplate;\n /** The whitespace-normalized strings the id was computed from. */\n readonly strings: readonly string[];\n /** The DOM the browser must build from `template.html` (checked by the runtime preparer). */\n readonly shape: Shape;\n}\n\n/** The template object plus what the runtime preparer checks it against. */\nexport function analyze(strings: readonly string[], loc?: string): Analysis {\n const normalized = minifyStrings(strings);\n const src = new Source(normalized, loc);\n const tree = new TreeBuilder(src);\n tokenize(src, tree);\n const { html, parts, segments, shape } = emit(tree.root, tree.server);\n const template: NormalizedTemplate = {\n id: templateId(normalized),\n html,\n parts,\n ...(tree.server ? { server: true as const } : {}),\n segments,\n ...(loc === undefined ? {} : { loc }),\n };\n return { template, strings: normalized, shape };\n}\n\n/**\n * Normalizes a template's static strings into its template object (JSON data). Throws a\n * TemplateError for markup that breaks a template rule.\n */\nexport function normalize(strings: readonly string[], loc?: string): NormalizedTemplate {\n return analyze(strings, loc).template;\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Ns = 'html' | 'svg' | 'math';
|
|
2
|
+
export interface OpenElement {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly ns: Ns;
|
|
5
|
+
}
|
|
6
|
+
/** How the tree builder treats content of a container (its insertion mode, roughly). */
|
|
7
|
+
export type Context = 'body' | 'table' | 'tbody' | 'tr' | 'colgroup';
|
|
8
|
+
/** The context an element's children are parsed in. */
|
|
9
|
+
export declare function contextOf(name: string): Context;
|
|
10
|
+
/**
|
|
11
|
+
* The context of a template root or <template> content: decided by its first start tag
|
|
12
|
+
* (the "in template" insertion mode).
|
|
13
|
+
*/
|
|
14
|
+
export declare function rootContext(firstTag: string): Context;
|
|
15
|
+
/**
|
|
16
|
+
* Rule 7 for a start tag in the HTML namespace. `atRoot`: the container is the template root
|
|
17
|
+
* or a <template>'s content, where content the parser would foster-parent stays in place.
|
|
18
|
+
* Returns the error message, or undefined when the parser keeps the tag where it is written.
|
|
19
|
+
*/
|
|
20
|
+
export declare function startRepair(name: string, ctx: Context, atRoot: boolean, stack: readonly OpenElement[], hiddenInput: boolean): string | undefined;
|
|
21
|
+
/** Rule 7 for static text: non-whitespace text in table structure is moved out of it. */
|
|
22
|
+
export declare function textRepair(raw: string, ctx: Context, atRoot: boolean): string | undefined;
|
|
23
|
+
/** Rule 7 in SVG/MathML: HTML tags that end the foreign element. */
|
|
24
|
+
export declare function breaksOutOfForeign(name: string, hasFontAttr: boolean): boolean;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Rule 7 by structure (view/09-template-rules.md "How rule 7 is checked"): start tags and text
|
|
2
|
+
// that the WHATWG HTML tree builder would move, drop or implicitly close something for, checked
|
|
3
|
+
// against the open-element stack. Only real repairs are flagged; the browser's own parse
|
|
4
|
+
// (prepare.ts) and the compiler's parse5 check catch anything this misses.
|
|
5
|
+
import { msg } from './rules.js';
|
|
6
|
+
const set = (names) => new Set(names.split(' '));
|
|
7
|
+
const TABLE_PARTS = set('caption col colgroup tbody td tfoot th thead tr');
|
|
8
|
+
const ALLOWED = {
|
|
9
|
+
table: set('caption colgroup thead tbody tfoot script style template'),
|
|
10
|
+
tbody: set('tr script style template'),
|
|
11
|
+
tr: set('td th script style template'),
|
|
12
|
+
colgroup: set('col template'),
|
|
13
|
+
};
|
|
14
|
+
const CLOSES_P = set('address article aside blockquote center details dialog dir div dl fieldset figcaption ' +
|
|
15
|
+
'figure footer header hgroup main menu nav ol p search section summary ul h1 h2 h3 h4 h5 h6 ' +
|
|
16
|
+
'pre listing form plaintext table hr xmp li dd dt');
|
|
17
|
+
const HEADINGS = set('h1 h2 h3 h4 h5 h6');
|
|
18
|
+
const SCOPE = {
|
|
19
|
+
html: set('applet caption html table td th marquee object template'),
|
|
20
|
+
math: set('mi mo mn ms mtext annotation-xml'),
|
|
21
|
+
svg: set('foreignobject desc title'),
|
|
22
|
+
};
|
|
23
|
+
const SPECIAL = set('address applet area article aside base basefont bgsound blockquote body br button caption ' +
|
|
24
|
+
'center col colgroup dd details dir div dl dt embed fieldset figcaption figure footer form ' +
|
|
25
|
+
'frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html iframe img input keygen li link ' +
|
|
26
|
+
'listing main marquee menu meta nav noembed noframes noscript object ol p param plaintext ' +
|
|
27
|
+
'pre script search section select source style summary table tbody td template textarea ' +
|
|
28
|
+
'tfoot th thead title tr track ul wbr xmp');
|
|
29
|
+
const LI = set('li');
|
|
30
|
+
const DD_DT = set('dd dt');
|
|
31
|
+
const FORM = set('form');
|
|
32
|
+
const TEMPLATE = set('template');
|
|
33
|
+
const OPTION = set('option');
|
|
34
|
+
const FORMATTING_MARKERS = set('applet marquee object template td th caption');
|
|
35
|
+
const BREAKOUT = set('b big blockquote body br center code dd div dl dt em embed h1 h2 h3 h4 h5 h6 head hr i img ' +
|
|
36
|
+
'li listing menu meta nobr ol p pre ruby s small span strong strike sub sup table tt u ul var');
|
|
37
|
+
/** The context an element's children are parsed in. */
|
|
38
|
+
export function contextOf(name) {
|
|
39
|
+
if (name === 'table')
|
|
40
|
+
return 'table';
|
|
41
|
+
if (name === 'tbody' || name === 'thead' || name === 'tfoot')
|
|
42
|
+
return 'tbody';
|
|
43
|
+
if (name === 'tr' || name === 'colgroup')
|
|
44
|
+
return name;
|
|
45
|
+
return 'body';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The context of a template root or <template> content: decided by its first start tag
|
|
49
|
+
* (the "in template" insertion mode).
|
|
50
|
+
*/
|
|
51
|
+
export function rootContext(firstTag) {
|
|
52
|
+
if (firstTag === 'tr')
|
|
53
|
+
return 'tbody';
|
|
54
|
+
if (firstTag === 'td' || firstTag === 'th')
|
|
55
|
+
return 'tr';
|
|
56
|
+
if (firstTag === 'col')
|
|
57
|
+
return 'colgroup';
|
|
58
|
+
return TABLE_PARTS.has(firstTag) ? 'table' : 'body';
|
|
59
|
+
}
|
|
60
|
+
const isHtml = (el, names) => el !== undefined && el.ns === 'html' && names.has(el.name);
|
|
61
|
+
function inScope(stack, name, extra) {
|
|
62
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
63
|
+
const el = stack[k];
|
|
64
|
+
if (el === undefined)
|
|
65
|
+
break;
|
|
66
|
+
if (el.ns === 'html' && el.name === name)
|
|
67
|
+
return true;
|
|
68
|
+
if (SCOPE[el.ns].has(el.name) || (el.ns === 'html' && el.name === extra))
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
/** An open <li> (or <dd>/<dt>) that a new one would close: the "in body" list-item walk. */
|
|
74
|
+
function listItemOpen(stack, names) {
|
|
75
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
76
|
+
const el = stack[k];
|
|
77
|
+
if (el === undefined)
|
|
78
|
+
break;
|
|
79
|
+
if (isHtml(el, names))
|
|
80
|
+
return true;
|
|
81
|
+
const special = el.ns === 'html' ? SPECIAL.has(el.name) : SCOPE[el.ns].has(el.name);
|
|
82
|
+
if (special && !(el.ns === 'html' && /^(address|div|p)$/.test(el.name)))
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function formattingOpen(stack, name) {
|
|
88
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
89
|
+
const el = stack[k];
|
|
90
|
+
if (el === undefined || isHtml(el, FORMATTING_MARKERS))
|
|
91
|
+
return false;
|
|
92
|
+
if (el.ns === 'html' && el.name === name)
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
function inTableMessage(name, ctx) {
|
|
98
|
+
const where = ctx === 'tbody' ? '<tbody>' : `<${ctx}>`;
|
|
99
|
+
if (name === 'tr' || ((name === 'td' || name === 'th') && ctx === 'table')) {
|
|
100
|
+
return msg.repaired(`<${name}> directly inside ${where}`, 'Wrap rows in <tbody>: <table><tbody><tr><td>…</td></tr></tbody></table>.');
|
|
101
|
+
}
|
|
102
|
+
if (name === 'td' || name === 'th') {
|
|
103
|
+
return msg.repaired(`<${name}> directly inside ${where}`, 'Wrap cells in <tr>.');
|
|
104
|
+
}
|
|
105
|
+
if (name === 'col')
|
|
106
|
+
return msg.repaired(`<col> directly inside ${where}`, 'Wrap it in <colgroup>.');
|
|
107
|
+
if (ctx === 'colgroup')
|
|
108
|
+
return msg.repaired(`<${name}> inside <colgroup>`, 'Only <col> goes there.');
|
|
109
|
+
return msg.repaired(`<${name}> directly inside ${where}`, 'The parser moves it out of the table. Put it inside a <td>, <th> or <caption>.');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Rule 7 for a start tag in the HTML namespace. `atRoot`: the container is the template root
|
|
113
|
+
* or a <template>'s content, where content the parser would foster-parent stays in place.
|
|
114
|
+
* Returns the error message, or undefined when the parser keeps the tag where it is written.
|
|
115
|
+
*/
|
|
116
|
+
export function startRepair(name, ctx, atRoot, stack, hiddenInput) {
|
|
117
|
+
if (ctx !== 'body') {
|
|
118
|
+
if (ALLOWED[ctx].has(name) || (ctx === 'table' && name === 'input' && hiddenInput))
|
|
119
|
+
return;
|
|
120
|
+
if (atRoot && ctx !== 'colgroup' && !TABLE_PARTS.has(name))
|
|
121
|
+
return;
|
|
122
|
+
return inTableMessage(name, ctx);
|
|
123
|
+
}
|
|
124
|
+
const top = stack.at(-1);
|
|
125
|
+
if (TABLE_PARTS.has(name)) {
|
|
126
|
+
return msg.repaired(`<${name}> outside its table structure is dropped by the parser`, `Put it where it belongs: <table><tbody><tr><td>…</td></tr></tbody></table>.`);
|
|
127
|
+
}
|
|
128
|
+
if (CLOSES_P.has(name) && inScope(stack, 'p', 'button')) {
|
|
129
|
+
return msg.repaired(`<${name}> inside <p>: a paragraph holds only phrasing content, so the parser closes it`, `Close </p> before <${name}>, or use a <div> instead of the <p>.`);
|
|
130
|
+
}
|
|
131
|
+
if (name === 'li' && listItemOpen(stack, LI)) {
|
|
132
|
+
return msg.repaired('<li> inside another <li> closes the first one', 'Close </li> first, or nest a list: <li>…<ul><li>…</li></ul></li>.');
|
|
133
|
+
}
|
|
134
|
+
if ((name === 'dd' || name === 'dt') && listItemOpen(stack, DD_DT)) {
|
|
135
|
+
return msg.repaired(`<${name}> inside <dd> or <dt> closes it`, 'Close it first.');
|
|
136
|
+
}
|
|
137
|
+
if (name === 'a' && formattingOpen(stack, 'a')) {
|
|
138
|
+
return msg.repaired("<a> inside <a>: links can't nest", 'Close the first </a> before the next <a>.');
|
|
139
|
+
}
|
|
140
|
+
if (name === 'form' &&
|
|
141
|
+
stack.some((el) => isHtml(el, FORM)) &&
|
|
142
|
+
!stack.some((el) => isHtml(el, TEMPLATE))) {
|
|
143
|
+
return msg.repaired('<form> inside <form> is dropped by the parser', 'Forms can\'t nest: close the first form, and point controls at a form with form="id".');
|
|
144
|
+
}
|
|
145
|
+
if (HEADINGS.has(name) && isHtml(top, HEADINGS)) {
|
|
146
|
+
return msg.repaired(`<${name}> inside a heading closes it`, 'Close the first heading first.');
|
|
147
|
+
}
|
|
148
|
+
if (name === 'button' && inScope(stack, 'button')) {
|
|
149
|
+
return msg.repaired('<button> inside <button> closes the first one', "Buttons can't nest.");
|
|
150
|
+
}
|
|
151
|
+
if ((name === 'option' || name === 'optgroup') && isHtml(top, OPTION)) {
|
|
152
|
+
return msg.repaired(`<${name}> inside <option> closes it`, 'Close </option> first.');
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
/** Rule 7 for static text: non-whitespace text in table structure is moved out of it. */
|
|
157
|
+
export function textRepair(raw, ctx, atRoot) {
|
|
158
|
+
if (ctx === 'body' || !/[^\t\n\f\r ]/.test(raw) || (atRoot && ctx !== 'colgroup'))
|
|
159
|
+
return;
|
|
160
|
+
return msg.repaired('Text directly inside table structure', 'The parser moves it out of the table. Put text inside a <td>, <th> or <caption>.');
|
|
161
|
+
}
|
|
162
|
+
/** Rule 7 in SVG/MathML: HTML tags that end the foreign element. */
|
|
163
|
+
export function breaksOutOfForeign(name, hasFontAttr) {
|
|
164
|
+
return BREAKOUT.has(name) || (name === 'font' && hasFontAttr);
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=repairs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repairs.js","sourceRoot":"","sources":["../../../src/view/normalize/repairs.ts"],"names":[],"mappings":"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","sourcesContent":["// Rule 7 by structure (view/09-template-rules.md \"How rule 7 is checked\"): start tags and text\n// that the WHATWG HTML tree builder would move, drop or implicitly close something for, checked\n// against the open-element stack. Only real repairs are flagged; the browser's own parse\n// (prepare.ts) and the compiler's parse5 check catch anything this misses.\nimport { msg } from './rules.js';\n\nexport type Ns = 'html' | 'svg' | 'math';\n\nexport interface OpenElement {\n readonly name: string;\n readonly ns: Ns;\n}\n\n/** How the tree builder treats content of a container (its insertion mode, roughly). */\nexport type Context = 'body' | 'table' | 'tbody' | 'tr' | 'colgroup';\n\nconst set = (names: string): ReadonlySet<string> => new Set(names.split(' '));\n\nconst TABLE_PARTS = set('caption col colgroup tbody td tfoot th thead tr');\nconst ALLOWED: Record<Exclude<Context, 'body'>, ReadonlySet<string>> = {\n table: set('caption colgroup thead tbody tfoot script style template'),\n tbody: set('tr script style template'),\n tr: set('td th script style template'),\n colgroup: set('col template'),\n};\nconst CLOSES_P = set(\n 'address article aside blockquote center details dialog dir div dl fieldset figcaption ' +\n 'figure footer header hgroup main menu nav ol p search section summary ul h1 h2 h3 h4 h5 h6 ' +\n 'pre listing form plaintext table hr xmp li dd dt',\n);\nconst HEADINGS = set('h1 h2 h3 h4 h5 h6');\nconst SCOPE: Record<Ns, ReadonlySet<string>> = {\n html: set('applet caption html table td th marquee object template'),\n math: set('mi mo mn ms mtext annotation-xml'),\n svg: set('foreignobject desc title'),\n};\nconst SPECIAL = set(\n 'address applet area article aside base basefont bgsound blockquote body br button caption ' +\n 'center col colgroup dd details dir div dl dt embed fieldset figcaption figure footer form ' +\n 'frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html iframe img input keygen li link ' +\n 'listing main marquee menu meta nav noembed noframes noscript object ol p param plaintext ' +\n 'pre script search section select source style summary table tbody td template textarea ' +\n 'tfoot th thead title tr track ul wbr xmp',\n);\nconst LI = set('li');\nconst DD_DT = set('dd dt');\nconst FORM = set('form');\nconst TEMPLATE = set('template');\nconst OPTION = set('option');\nconst FORMATTING_MARKERS = set('applet marquee object template td th caption');\nconst BREAKOUT = set(\n 'b big blockquote body br center code dd div dl dt em embed h1 h2 h3 h4 h5 h6 head hr i img ' +\n 'li listing menu meta nobr ol p pre ruby s small span strong strike sub sup table tt u ul var',\n);\n\n/** The context an element's children are parsed in. */\nexport function contextOf(name: string): Context {\n if (name === 'table') return 'table';\n if (name === 'tbody' || name === 'thead' || name === 'tfoot') return 'tbody';\n if (name === 'tr' || name === 'colgroup') return name;\n return 'body';\n}\n\n/**\n * The context of a template root or <template> content: decided by its first start tag\n * (the \"in template\" insertion mode).\n */\nexport function rootContext(firstTag: string): Context {\n if (firstTag === 'tr') return 'tbody';\n if (firstTag === 'td' || firstTag === 'th') return 'tr';\n if (firstTag === 'col') return 'colgroup';\n return TABLE_PARTS.has(firstTag) ? 'table' : 'body';\n}\n\nconst isHtml = (el: OpenElement | undefined, names: ReadonlySet<string>): boolean =>\n el !== undefined && el.ns === 'html' && names.has(el.name);\n\nfunction inScope(stack: readonly OpenElement[], name: string, extra?: string): boolean {\n for (let k = stack.length - 1; k >= 0; k--) {\n const el = stack[k];\n if (el === undefined) break;\n if (el.ns === 'html' && el.name === name) return true;\n if (SCOPE[el.ns].has(el.name) || (el.ns === 'html' && el.name === extra)) return false;\n }\n return false;\n}\n\n/** An open <li> (or <dd>/<dt>) that a new one would close: the \"in body\" list-item walk. */\nfunction listItemOpen(stack: readonly OpenElement[], names: ReadonlySet<string>): boolean {\n for (let k = stack.length - 1; k >= 0; k--) {\n const el = stack[k];\n if (el === undefined) break;\n if (isHtml(el, names)) return true;\n const special = el.ns === 'html' ? SPECIAL.has(el.name) : SCOPE[el.ns].has(el.name);\n if (special && !(el.ns === 'html' && /^(address|div|p)$/.test(el.name))) return false;\n }\n return false;\n}\n\nfunction formattingOpen(stack: readonly OpenElement[], name: string): boolean {\n for (let k = stack.length - 1; k >= 0; k--) {\n const el = stack[k];\n if (el === undefined || isHtml(el, FORMATTING_MARKERS)) return false;\n if (el.ns === 'html' && el.name === name) return true;\n }\n return false;\n}\n\nfunction inTableMessage(name: string, ctx: Exclude<Context, 'body'>): string {\n const where = ctx === 'tbody' ? '<tbody>' : `<${ctx}>`;\n if (name === 'tr' || ((name === 'td' || name === 'th') && ctx === 'table')) {\n return msg.repaired(\n `<${name}> directly inside ${where}`,\n 'Wrap rows in <tbody>: <table><tbody><tr><td>…</td></tr></tbody></table>.',\n );\n }\n if (name === 'td' || name === 'th') {\n return msg.repaired(`<${name}> directly inside ${where}`, 'Wrap cells in <tr>.');\n }\n if (name === 'col')\n return msg.repaired(`<col> directly inside ${where}`, 'Wrap it in <colgroup>.');\n if (ctx === 'colgroup')\n return msg.repaired(`<${name}> inside <colgroup>`, 'Only <col> goes there.');\n return msg.repaired(\n `<${name}> directly inside ${where}`,\n 'The parser moves it out of the table. Put it inside a <td>, <th> or <caption>.',\n );\n}\n\n/**\n * Rule 7 for a start tag in the HTML namespace. `atRoot`: the container is the template root\n * or a <template>'s content, where content the parser would foster-parent stays in place.\n * Returns the error message, or undefined when the parser keeps the tag where it is written.\n */\nexport function startRepair(\n name: string,\n ctx: Context,\n atRoot: boolean,\n stack: readonly OpenElement[],\n hiddenInput: boolean,\n): string | undefined {\n if (ctx !== 'body') {\n if (ALLOWED[ctx].has(name) || (ctx === 'table' && name === 'input' && hiddenInput)) return;\n if (atRoot && ctx !== 'colgroup' && !TABLE_PARTS.has(name)) return;\n return inTableMessage(name, ctx);\n }\n const top = stack.at(-1);\n if (TABLE_PARTS.has(name)) {\n return msg.repaired(\n `<${name}> outside its table structure is dropped by the parser`,\n `Put it where it belongs: <table><tbody><tr><td>…</td></tr></tbody></table>.`,\n );\n }\n if (CLOSES_P.has(name) && inScope(stack, 'p', 'button')) {\n return msg.repaired(\n `<${name}> inside <p>: a paragraph holds only phrasing content, so the parser closes it`,\n `Close </p> before <${name}>, or use a <div> instead of the <p>.`,\n );\n }\n if (name === 'li' && listItemOpen(stack, LI)) {\n return msg.repaired(\n '<li> inside another <li> closes the first one',\n 'Close </li> first, or nest a list: <li>…<ul><li>…</li></ul></li>.',\n );\n }\n if ((name === 'dd' || name === 'dt') && listItemOpen(stack, DD_DT)) {\n return msg.repaired(`<${name}> inside <dd> or <dt> closes it`, 'Close it first.');\n }\n if (name === 'a' && formattingOpen(stack, 'a')) {\n return msg.repaired(\n \"<a> inside <a>: links can't nest\",\n 'Close the first </a> before the next <a>.',\n );\n }\n if (\n name === 'form' &&\n stack.some((el) => isHtml(el, FORM)) &&\n !stack.some((el) => isHtml(el, TEMPLATE))\n ) {\n return msg.repaired(\n '<form> inside <form> is dropped by the parser',\n 'Forms can\\'t nest: close the first form, and point controls at a form with form=\"id\".',\n );\n }\n if (HEADINGS.has(name) && isHtml(top, HEADINGS)) {\n return msg.repaired(`<${name}> inside a heading closes it`, 'Close the first heading first.');\n }\n if (name === 'button' && inScope(stack, 'button')) {\n return msg.repaired('<button> inside <button> closes the first one', \"Buttons can't nest.\");\n }\n if ((name === 'option' || name === 'optgroup') && isHtml(top, OPTION)) {\n return msg.repaired(`<${name}> inside <option> closes it`, 'Close </option> first.');\n }\n return undefined;\n}\n\n/** Rule 7 for static text: non-whitespace text in table structure is moved out of it. */\nexport function textRepair(raw: string, ctx: Context, atRoot: boolean): string | undefined {\n if (ctx === 'body' || !/[^\\t\\n\\f\\r ]/.test(raw) || (atRoot && ctx !== 'colgroup')) return;\n return msg.repaired(\n 'Text directly inside table structure',\n 'The parser moves it out of the table. Put text inside a <td>, <th> or <caption>.',\n );\n}\n\n/** Rule 7 in SVG/MathML: HTML tags that end the foreign element. */\nexport function breaksOutOfForeign(name: string, hasFontAttr: boolean): boolean {\n return BREAKOUT.has(name) || (name === 'font' && hasFontAttr);\n}\n"]}
|