@gyral/core 0.1.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 +112 -0
- package/LICENSE +1 -1
- package/NOTICE +1 -1
- package/README.md +9 -7
- 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 -334
- 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 -0
- 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 +9 -2
- package/dist/focus.js +17 -2
- 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 -16
- package/dist/index.js +9 -16
- 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 +118 -73
- 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/view/normalize/whitespace.js +182 -0
- 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 +41 -7
- 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/internal/runtime.d.ts +0 -7
- package/dist/internal/runtime.js +0 -8
- package/dist/internal/runtime.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/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/textarea.d.ts +0 -26
- package/dist/textarea.js +0 -85
- package/dist/textarea.js.map +0 -1
|
@@ -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"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Messages, by rule. Each names the fix. */
|
|
2
|
+
export declare const msg: {
|
|
3
|
+
eventBinding: (name: string) => string;
|
|
4
|
+
dynamicTag: () => string;
|
|
5
|
+
dynamicName: () => string;
|
|
6
|
+
inRawText: (tag: string) => string;
|
|
7
|
+
inComment: () => string;
|
|
8
|
+
inDoctype: () => string;
|
|
9
|
+
inTemplate: () => string;
|
|
10
|
+
formState: (tag: string, name: string, write: string) => string;
|
|
11
|
+
unquoted: (name: string) => string;
|
|
12
|
+
notSingle: (name: string) => string;
|
|
13
|
+
selfClosing: (tag: string) => string;
|
|
14
|
+
repaired: (what: string, write: string) => string;
|
|
15
|
+
badEndTag: () => string;
|
|
16
|
+
unterminated: (what: "comment" | "tag") => string;
|
|
17
|
+
svgOnly: (tag: string) => string;
|
|
18
|
+
serverOnly: () => string;
|
|
19
|
+
textContent: (tag: string) => string;
|
|
20
|
+
charRef: (ref: string) => string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Rule 4: property bindings that would be form state (view/02-bindings.md "Live form state").
|
|
24
|
+
* Returns the spelling to use instead, or undefined when the binding is fine.
|
|
25
|
+
*/
|
|
26
|
+
export declare function formStateFix(tag: string, name: string): string | undefined;
|
|
27
|
+
/** Rule 10: SVG element names that HTML doesn't share (lower case). */
|
|
28
|
+
export declare const SVG_ONLY: Set<string>;
|
|
29
|
+
/** HTML void elements: no end tag, no content. */
|
|
30
|
+
export declare const VOID: Set<string>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// The template rules of view/09-template-rules.md that one template's strings reveal: every
|
|
2
|
+
// message says what is wrong and what to write instead (core belief 7). The tokenizer
|
|
3
|
+
// (rules 1, 2, 3, 5), the tree builder (3, 4, 6, 7, 10, 12, 13) and repairs.ts (7) throw them
|
|
4
|
+
// through Source.fail; the browser's own parse (prepare.ts) backs rule 7 up, and
|
|
5
|
+
// template-element.ts throws rule 11 when a server template reaches the browser.
|
|
6
|
+
import { SERVER_ONLY } from './errors.js';
|
|
7
|
+
const H = '${…}';
|
|
8
|
+
/** Messages, by rule. Each names the fix. */
|
|
9
|
+
export const msg = {
|
|
10
|
+
// 1
|
|
11
|
+
eventBinding: (name) => `${name}=${H} is an event binding, and Gyral has none: views never attach listeners. ` +
|
|
12
|
+
`Name the intent instead, data-intent=\${i.Name}, and parse the event in the component's ` +
|
|
13
|
+
`intent (ADR 0001).`,
|
|
14
|
+
// 2
|
|
15
|
+
dynamicTag: () => `A hole can't be (part of) a tag name: <${H}> and </${H}> are not supported. Choose ` +
|
|
16
|
+
`between whole templates instead: \${cond ? html\`<a>…</a>\` : html\`<b>…</b>\`}. ` +
|
|
17
|
+
`For a literal "<" next to a value, write <.`,
|
|
18
|
+
dynamicName: () => `A hole can't be (part of) an attribute name. Write the name statically (name=${H}), ` +
|
|
19
|
+
`use ?name=${H} to add or remove it, or choose between whole templates.`,
|
|
20
|
+
// 3
|
|
21
|
+
inRawText: (tag) => `A hole can't sit inside <${tag}>: no part can live there, and values in it are an ` +
|
|
22
|
+
`injection risk. Pass data through an attribute, styles through a CSS custom property ` +
|
|
23
|
+
`(style=\${\`--x: \${v}\`}), or trusted markup through raw().`,
|
|
24
|
+
inComment: () => `A hole can't sit inside an HTML comment: no part can live there. Move the value out of ` +
|
|
25
|
+
`the comment, or drop the comment.`,
|
|
26
|
+
inDoctype: () => `A hole can't sit inside <!doctype>. Write the doctype statically: <!doctype html>.`,
|
|
27
|
+
inTemplate: () => `A hole can't sit inside a <template> element: its content is inert and no part can live ` +
|
|
28
|
+
`there. Render the content with a nested html\`…\` template in a child hole instead.`,
|
|
29
|
+
// 4
|
|
30
|
+
formState: (tag, name, write) => `.${name}=${H} on <${tag}> binds form state as a property, which never reaches the ` +
|
|
31
|
+
`server and has a second spelling. Write ${write} instead: Gyral keeps it live ` +
|
|
32
|
+
`(view/02-bindings.md, "Live form state").`,
|
|
33
|
+
// 5
|
|
34
|
+
unquoted: (name) => `${name}=… mixes static text and holes without quotes, which is ambiguous. Quote the ` +
|
|
35
|
+
`whole value: ${name}="a ${H}".`,
|
|
36
|
+
notSingle: (name) => `${name} takes exactly one value and no static text. Write ${name}=${H}, and build the ` +
|
|
37
|
+
`value in the expression.`,
|
|
38
|
+
// 6
|
|
39
|
+
selfClosing: (tag) => `<${tag} /> is not self-closing in HTML: the parser ignores the "/", so <${tag}> would ` +
|
|
40
|
+
`swallow the siblings that follow it. Write <${tag}></${tag}>.`,
|
|
41
|
+
// 7
|
|
42
|
+
repaired: (what, write) => `${what}. The HTML parser repairs this markup, so the DOM would differ from the template ` +
|
|
43
|
+
`and part paths would shift. ${write}`,
|
|
44
|
+
badEndTag: () => `"</" must start an end tag such as </p>. For a literal "</" in text, write </.`,
|
|
45
|
+
unterminated: (what) => what === 'comment'
|
|
46
|
+
? `The template ends inside a comment or <!…> declaration. Close it with -->.`
|
|
47
|
+
: `The template ends inside a tag, so the parser would drop it. Close the tag with ">".`,
|
|
48
|
+
// 10
|
|
49
|
+
svgOnly: (tag) => `<${tag}> is an SVG element outside an <svg>: there is no svg tag, and the HTML parser ` +
|
|
50
|
+
`creates SVG elements only inside <svg>. Wrap it: <svg viewBox="…"><${tag} …></${tag}></svg>.`,
|
|
51
|
+
// 11
|
|
52
|
+
serverOnly: () => SERVER_ONLY,
|
|
53
|
+
// 12 (proposed)
|
|
54
|
+
textContent: (tag) => `A hole in <${tag}> must be its whole content. Write <${tag}>${H}</${tag}> and build the ` +
|
|
55
|
+
`text in the value: <${tag}>\${\`\${page} | Site\`}</${tag}>.`,
|
|
56
|
+
// 13 (proposed)
|
|
57
|
+
charRef: (ref) => `&${ref}; in the static text of a bound or custom-element attribute is a named character ` +
|
|
58
|
+
`reference the normalizer doesn't decode (it knows & < > " ' ). ` +
|
|
59
|
+
`Write the character itself, or a numeric reference such as ©.`,
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Rule 4: property bindings that would be form state (view/02-bindings.md "Live form state").
|
|
63
|
+
* Returns the spelling to use instead, or undefined when the binding is fine.
|
|
64
|
+
*/
|
|
65
|
+
const FORM_STATE = {
|
|
66
|
+
input: { value: `value=${H}`, checked: `?checked=${H}`, indeterminate: `?indeterminate=${H}` },
|
|
67
|
+
textarea: { value: `<textarea>${H}</textarea>` },
|
|
68
|
+
select: { value: `?selected=${H} on the chosen <option>` },
|
|
69
|
+
option: { selected: `?selected=${H}`, value: `value=${H}` },
|
|
70
|
+
details: { open: `?open=${H}` },
|
|
71
|
+
dialog: { open: `?open=${H}` },
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Rule 4: property bindings that would be form state (view/02-bindings.md "Live form state").
|
|
75
|
+
* Returns the spelling to use instead, or undefined when the binding is fine.
|
|
76
|
+
*/
|
|
77
|
+
export function formStateFix(tag, name) {
|
|
78
|
+
return FORM_STATE[tag]?.[name];
|
|
79
|
+
}
|
|
80
|
+
/** Rule 10: SVG element names that HTML doesn't share (lower case). */
|
|
81
|
+
export const SVG_ONLY = new Set(('animate animatemotion animatetransform circle clippath defs desc ellipse feblend ' +
|
|
82
|
+
'fecolormatrix fecomponenttransfer fecomposite feconvolvematrix fediffuselighting ' +
|
|
83
|
+
'fedisplacementmap fedropshadow feflood fegaussianblur feimage femerge femergenode ' +
|
|
84
|
+
'femorphology feoffset fespecularlighting fetile feturbulence filter foreignobject g line ' +
|
|
85
|
+
'lineargradient marker mask metadata mpath path pattern polygon polyline radialgradient ' +
|
|
86
|
+
'rect stop symbol text textpath tspan use').split(' '));
|
|
87
|
+
/** HTML void elements: no end tag, no content. */
|
|
88
|
+
export const VOID = new Set(('area base basefont bgsound br col embed frame hr img input keygen link meta param source ' +
|
|
89
|
+
'track wbr').split(' '));
|
|
90
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/view/normalize/rules.ts"],"names":[],"mappings":"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","sourcesContent":["// The template rules of view/09-template-rules.md that one template's strings reveal: every\n// message says what is wrong and what to write instead (core belief 7). The tokenizer\n// (rules 1, 2, 3, 5), the tree builder (3, 4, 6, 7, 10, 12, 13) and repairs.ts (7) throw them\n// through Source.fail; the browser's own parse (prepare.ts) backs rule 7 up, and\n// template-element.ts throws rule 11 when a server template reaches the browser.\n\nimport { SERVER_ONLY } from './errors.js';\n\nconst H = '${…}';\n\n/** Messages, by rule. Each names the fix. */\nexport const msg = {\n // 1\n eventBinding: (name: string): string =>\n `${name}=${H} is an event binding, and Gyral has none: views never attach listeners. ` +\n `Name the intent instead, data-intent=\\${i.Name}, and parse the event in the component's ` +\n `intent (ADR 0001).`,\n // 2\n dynamicTag: (): string =>\n `A hole can't be (part of) a tag name: <${H}> and </${H}> are not supported. Choose ` +\n `between whole templates instead: \\${cond ? html\\`<a>…</a>\\` : html\\`<b>…</b>\\`}. ` +\n `For a literal \"<\" next to a value, write <.`,\n dynamicName: (): string =>\n `A hole can't be (part of) an attribute name. Write the name statically (name=${H}), ` +\n `use ?name=${H} to add or remove it, or choose between whole templates.`,\n // 3\n inRawText: (tag: string): string =>\n `A hole can't sit inside <${tag}>: no part can live there, and values in it are an ` +\n `injection risk. Pass data through an attribute, styles through a CSS custom property ` +\n `(style=\\${\\`--x: \\${v}\\`}), or trusted markup through raw().`,\n inComment: (): string =>\n `A hole can't sit inside an HTML comment: no part can live there. Move the value out of ` +\n `the comment, or drop the comment.`,\n inDoctype: (): string =>\n `A hole can't sit inside <!doctype>. Write the doctype statically: <!doctype html>.`,\n inTemplate: (): string =>\n `A hole can't sit inside a <template> element: its content is inert and no part can live ` +\n `there. Render the content with a nested html\\`…\\` template in a child hole instead.`,\n // 4\n formState: (tag: string, name: string, write: string): string =>\n `.${name}=${H} on <${tag}> binds form state as a property, which never reaches the ` +\n `server and has a second spelling. Write ${write} instead: Gyral keeps it live ` +\n `(view/02-bindings.md, \"Live form state\").`,\n // 5\n unquoted: (name: string): string =>\n `${name}=… mixes static text and holes without quotes, which is ambiguous. Quote the ` +\n `whole value: ${name}=\"a ${H}\".`,\n notSingle: (name: string): string =>\n `${name} takes exactly one value and no static text. Write ${name}=${H}, and build the ` +\n `value in the expression.`,\n // 6\n selfClosing: (tag: string): string =>\n `<${tag} /> is not self-closing in HTML: the parser ignores the \"/\", so <${tag}> would ` +\n `swallow the siblings that follow it. Write <${tag}></${tag}>.`,\n // 7\n repaired: (what: string, write: string): string =>\n `${what}. The HTML parser repairs this markup, so the DOM would differ from the template ` +\n `and part paths would shift. ${write}`,\n badEndTag: (): string =>\n `\"</\" must start an end tag such as </p>. For a literal \"</\" in text, write </.`,\n unterminated: (what: 'comment' | 'tag'): string =>\n what === 'comment'\n ? `The template ends inside a comment or <!…> declaration. Close it with -->.`\n : `The template ends inside a tag, so the parser would drop it. Close the tag with \">\".`,\n // 10\n svgOnly: (tag: string): string =>\n `<${tag}> is an SVG element outside an <svg>: there is no svg tag, and the HTML parser ` +\n `creates SVG elements only inside <svg>. Wrap it: <svg viewBox=\"…\"><${tag} …></${tag}></svg>.`,\n // 11\n serverOnly: (): string => SERVER_ONLY,\n // 12 (proposed)\n textContent: (tag: string): string =>\n `A hole in <${tag}> must be its whole content. Write <${tag}>${H}</${tag}> and build the ` +\n `text in the value: <${tag}>\\${\\`\\${page} | Site\\`}</${tag}>.`,\n // 13 (proposed)\n charRef: (ref: string): string =>\n `&${ref}; in the static text of a bound or custom-element attribute is a named character ` +\n `reference the normalizer doesn't decode (it knows & < > " ' ). ` +\n `Write the character itself, or a numeric reference such as ©.`,\n};\n\n/**\n * Rule 4: property bindings that would be form state (view/02-bindings.md \"Live form state\").\n * Returns the spelling to use instead, or undefined when the binding is fine.\n */\nconst FORM_STATE: Record<string, Record<string, string> | undefined> = {\n input: { value: `value=${H}`, checked: `?checked=${H}`, indeterminate: `?indeterminate=${H}` },\n textarea: { value: `<textarea>${H}</textarea>` },\n select: { value: `?selected=${H} on the chosen <option>` },\n option: { selected: `?selected=${H}`, value: `value=${H}` },\n details: { open: `?open=${H}` },\n dialog: { open: `?open=${H}` },\n};\n\n/**\n * Rule 4: property bindings that would be form state (view/02-bindings.md \"Live form state\").\n * Returns the spelling to use instead, or undefined when the binding is fine.\n */\nexport function formStateFix(tag: string, name: string): string | undefined {\n return FORM_STATE[tag]?.[name];\n}\n\n/** Rule 10: SVG element names that HTML doesn't share (lower case). */\nexport const SVG_ONLY = new Set(\n (\n 'animate animatemotion animatetransform circle clippath defs desc ellipse feblend ' +\n 'fecolormatrix fecomponenttransfer fecomposite feconvolvematrix fediffuselighting ' +\n 'fedisplacementmap fedropshadow feflood fegaussianblur feimage femerge femergenode ' +\n 'femorphology feoffset fespecularlighting fetile feturbulence filter foreignobject g line ' +\n 'lineargradient marker mask metadata mpath path pattern polygon polyline radialgradient ' +\n 'rect stop symbol text textpath tspan use'\n ).split(' '),\n);\n\n/** HTML void elements: no end tag, no content. */\nexport const VOID = new Set(\n (\n 'area base basefont bgsound br col embed frame hr img input keygen link meta param source ' +\n 'track wbr'\n ).split(' '),\n);\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Shape, TemplateObject } from './types.js';
|
|
2
|
+
/** The first place where `got` differs from `want`, as a message; undefined if none. */
|
|
3
|
+
export declare function shapeMismatch(want: Shape, got: Shape, path?: readonly number[]): string | undefined;
|
|
4
|
+
/** Rule 7's error for a parse that differs from the template's shape (`problem`). */
|
|
5
|
+
export declare function repairError(template: TemplateObject, parser: string, problem: string): Error;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Rule 7 by comparison (view/09-template-rules.md "How rule 7 is checked"): the DOM an HTML
|
|
2
|
+
// parser built from a template's HTML, as a Shape, against the shape the normalizer computed
|
|
3
|
+
// the part paths for. Any difference means the parser repaired the markup. The runtime
|
|
4
|
+
// preparer feeds it the browser's own parse (prepare.ts); the Vite template compiler feeds it
|
|
5
|
+
// parse5's (src/compiler/parse5-check.ts). No DOM here: it runs in Node and in the browser.
|
|
6
|
+
import { TemplateError } from './errors.js';
|
|
7
|
+
const describe = (node) => node === undefined
|
|
8
|
+
? 'nothing'
|
|
9
|
+
: typeof node === 'string'
|
|
10
|
+
? node === '#text'
|
|
11
|
+
? 'text'
|
|
12
|
+
: 'a comment'
|
|
13
|
+
: `<${node[0]}>`;
|
|
14
|
+
const same = (want, got) => typeof want === 'string' ? want === got : typeof got !== 'string' && want[0] === got[0];
|
|
15
|
+
/** The first place where `got` differs from `want`, as a message; undefined if none. */
|
|
16
|
+
export function shapeMismatch(want, got, path = []) {
|
|
17
|
+
for (let k = 0; k < Math.max(want.length, got.length); k++) {
|
|
18
|
+
const w = want[k];
|
|
19
|
+
const g = got[k];
|
|
20
|
+
if (w === undefined || g === undefined || !same(w, g)) {
|
|
21
|
+
return `at path [${[...path, k].join(', ')}]: expected ${describe(w)}, found ${describe(g)}`;
|
|
22
|
+
}
|
|
23
|
+
if (typeof w !== 'string' && typeof g !== 'string') {
|
|
24
|
+
const inner = shapeMismatch(w[1], g[1], [...path, k]);
|
|
25
|
+
if (inner !== undefined)
|
|
26
|
+
return inner;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Rule 7's error for a parse that differs from the template's shape (`problem`). */
|
|
32
|
+
export function repairError(template, parser, problem) {
|
|
33
|
+
return new TemplateError(7, `${parser} built a different DOM for this template ${problem}. It repaired the markup ` +
|
|
34
|
+
`(for example <tr> directly in <table>, a block element inside <p>, or <a> inside ` +
|
|
35
|
+
`<a>), so part paths would point at the wrong nodes. Write the valid structure ` +
|
|
36
|
+
`(<tbody>, closing </p> first, …).\n template: ${template.html.slice(0, 120)}`, template.loc);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../src/view/normalize/shape.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,6FAA6F;AAC7F,uFAAuF;AACvF,8FAA8F;AAC9F,4FAA4F;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,QAAQ,GAAG,CAAC,IAA2B,EAAU,EAAE,CACvD,IAAI,KAAK,SAAS;IAChB,CAAC,CAAC,SAAS;IACX,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ;QACxB,CAAC,CAAC,IAAI,KAAK,OAAO;YAChB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,WAAW;QACf,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AAEvB,MAAM,IAAI,GAAG,CAAC,IAAe,EAAE,GAAc,EAAW,EAAE,CACxD,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;AAE1F,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAC3B,IAAW,EACX,GAAU,EACV,OAA0B,EAAE;IAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3D,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACjB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACtD,OAAO,YAAY,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,CAAC,CAAC,WAAW,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACtD,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,WAAW,CAAC,QAAwB,EAAE,MAAc,EAAE,OAAe;IACnF,OAAO,IAAI,aAAa,CACtB,CAAC,EACD,GAAG,MAAM,4CAA4C,OAAO,2BAA2B;QACrF,mFAAmF;QACnF,gFAAgF;QAChF,kDAAkD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EACjF,QAAQ,CAAC,GAAG,CACb,CAAC;AACJ,CAAC","sourcesContent":["// Rule 7 by comparison (view/09-template-rules.md \"How rule 7 is checked\"): the DOM an HTML\n// parser built from a template's HTML, as a Shape, against the shape the normalizer computed\n// the part paths for. Any difference means the parser repaired the markup. The runtime\n// preparer feeds it the browser's own parse (prepare.ts); the Vite template compiler feeds it\n// parse5's (src/compiler/parse5-check.ts). No DOM here: it runs in Node and in the browser.\nimport { TemplateError } from './errors.js';\nimport type { Shape, ShapeNode, TemplateObject } from './types.js';\n\nconst describe = (node: ShapeNode | undefined): string =>\n node === undefined\n ? 'nothing'\n : typeof node === 'string'\n ? node === '#text'\n ? 'text'\n : 'a comment'\n : `<${node[0]}>`;\n\nconst same = (want: ShapeNode, got: ShapeNode): boolean =>\n typeof want === 'string' ? want === got : typeof got !== 'string' && want[0] === got[0];\n\n/** The first place where `got` differs from `want`, as a message; undefined if none. */\nexport function shapeMismatch(\n want: Shape,\n got: Shape,\n path: readonly number[] = [],\n): string | undefined {\n for (let k = 0; k < Math.max(want.length, got.length); k++) {\n const w = want[k];\n const g = got[k];\n if (w === undefined || g === undefined || !same(w, g)) {\n return `at path [${[...path, k].join(', ')}]: expected ${describe(w)}, found ${describe(g)}`;\n }\n if (typeof w !== 'string' && typeof g !== 'string') {\n const inner = shapeMismatch(w[1], g[1], [...path, k]);\n if (inner !== undefined) return inner;\n }\n }\n return undefined;\n}\n\n/** Rule 7's error for a parse that differs from the template's shape (`problem`). */\nexport function repairError(template: TemplateObject, parser: string, problem: string): Error {\n return new TemplateError(\n 7,\n `${parser} built a different DOM for this template ${problem}. It repaired the markup ` +\n `(for example <tr> directly in <table>, a block element inside <p>, or <a> inside ` +\n `<a>), so part paths would point at the wrong nodes. Write the valid structure ` +\n `(<tbody>, closing </p> first, …).\\n template: ${template.html.slice(0, 120)}`,\n template.loc,\n );\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class Source {
|
|
2
|
+
readonly strings: readonly string[];
|
|
3
|
+
readonly loc: string | undefined;
|
|
4
|
+
/** Index of the string being read, and the offset in it. */
|
|
5
|
+
i: number;
|
|
6
|
+
pos: number;
|
|
7
|
+
constructor(strings: readonly string[], loc: string | undefined);
|
|
8
|
+
get last(): boolean;
|
|
9
|
+
/** The markup around the current position, with holes shown as `${…}`. */
|
|
10
|
+
near(): string;
|
|
11
|
+
/** Throws rule `rule`'s error, quoting the markup at the current position. */
|
|
12
|
+
fail(rule: number, message: string): never;
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// The strings being normalized and the tokenizer's position in them, so every rule violation
|
|
2
|
+
// (view/09-template-rules.md) can quote the markup it is about.
|
|
3
|
+
import { TemplateError } from './errors.js';
|
|
4
|
+
const HOLE = '${…}';
|
|
5
|
+
export class Source {
|
|
6
|
+
constructor(strings, loc) {
|
|
7
|
+
this.strings = strings;
|
|
8
|
+
this.loc = loc;
|
|
9
|
+
/** Index of the string being read, and the offset in it. */
|
|
10
|
+
this.i = 0;
|
|
11
|
+
this.pos = 0;
|
|
12
|
+
}
|
|
13
|
+
get last() {
|
|
14
|
+
return this.i === this.strings.length - 1;
|
|
15
|
+
}
|
|
16
|
+
/** The markup around the current position, with holes shown as `${…}`. */
|
|
17
|
+
near() {
|
|
18
|
+
const s = this.strings[this.i] ?? '';
|
|
19
|
+
let before = s.slice(Math.max(0, this.pos - 40), this.pos);
|
|
20
|
+
if (this.pos < 40 && this.i > 0) {
|
|
21
|
+
before = (this.strings[this.i - 1] ?? '').slice(this.pos - 40) + HOLE + before;
|
|
22
|
+
}
|
|
23
|
+
let after = s.slice(this.pos, this.pos + 20);
|
|
24
|
+
if (this.pos + 20 > s.length && !this.last) {
|
|
25
|
+
after += HOLE + (this.strings[this.i + 1] ?? '').slice(0, 20 - after.length);
|
|
26
|
+
}
|
|
27
|
+
return `${before}${after}`.replace(/\s+/g, ' ').trim();
|
|
28
|
+
}
|
|
29
|
+
/** Throws rule `rule`'s error, quoting the markup at the current position. */
|
|
30
|
+
fail(rule, message) {
|
|
31
|
+
throw new TemplateError(rule, message, this.loc, this.near());
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../../src/view/normalize/source.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,gEAAgE;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB,MAAM,OAAO,MAAM;IAKjB,YACW,OAA0B,EAC1B,GAAuB;QADvB,YAAO,GAAP,OAAO,CAAmB;QAC1B,QAAG,GAAH,GAAG,CAAoB;QANlC,4DAA4D;QAC5D,MAAC,GAAG,CAAC,CAAC;QACN,QAAG,GAAG,CAAC,CAAC;IAKL,CAAC;IAEJ,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,0EAA0E;IAC1E,IAAI;QACF,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,GAAG,GAAG,EAAE,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC;QACjF,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC3C,KAAK,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,8EAA8E;IAC9E,IAAI,CAAC,IAAY,EAAE,OAAe;QAChC,MAAM,IAAI,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;CACF","sourcesContent":["// The strings being normalized and the tokenizer's position in them, so every rule violation\n// (view/09-template-rules.md) can quote the markup it is about.\nimport { TemplateError } from './errors.js';\n\nconst HOLE = '${…}';\n\nexport class Source {\n /** Index of the string being read, and the offset in it. */\n i = 0;\n pos = 0;\n\n constructor(\n readonly strings: readonly string[],\n readonly loc: string | undefined,\n ) {}\n\n get last(): boolean {\n return this.i === this.strings.length - 1;\n }\n\n /** The markup around the current position, with holes shown as `${…}`. */\n near(): string {\n const s = this.strings[this.i] ?? '';\n let before = s.slice(Math.max(0, this.pos - 40), this.pos);\n if (this.pos < 40 && this.i > 0) {\n before = (this.strings[this.i - 1] ?? '').slice(this.pos - 40) + HOLE + before;\n }\n let after = s.slice(this.pos, this.pos + 20);\n if (this.pos + 20 > s.length && !this.last) {\n after += HOLE + (this.strings[this.i + 1] ?? '').slice(0, 20 - after.length);\n }\n return `${before}${after}`.replace(/\\s+/g, ' ').trim();\n }\n\n /** Throws rule `rule`'s error, quoting the markup at the current position. */\n fail(rule: number, message: string): never {\n throw new TemplateError(rule, message, this.loc, this.near());\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Source } from './source.js';
|
|
2
|
+
import { type Sink, type StartTag, type State } from './tokens.js';
|
|
3
|
+
export declare abstract class TagStates {
|
|
4
|
+
protected readonly src: Source;
|
|
5
|
+
protected readonly sink: Sink;
|
|
6
|
+
protected state: State;
|
|
7
|
+
protected buf: string;
|
|
8
|
+
protected rawEnd: string;
|
|
9
|
+
protected tag: StartTag;
|
|
10
|
+
private attrName;
|
|
11
|
+
private pieces;
|
|
12
|
+
constructor(src: Source, sink: Sink);
|
|
13
|
+
protected tagChar(c: string): void;
|
|
14
|
+
private startName;
|
|
15
|
+
private endValueless;
|
|
16
|
+
private pushStatic;
|
|
17
|
+
private endQuoted;
|
|
18
|
+
/** A bound attribute named `attrName`; `strings` for a multi-attribute. */
|
|
19
|
+
private pushBound;
|
|
20
|
+
private emitTag;
|
|
21
|
+
/** A hole in a start tag (after string `src.i`). */
|
|
22
|
+
protected holeInTag(): void;
|
|
23
|
+
}
|