@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,121 @@
|
|
|
1
|
+
import { serverComponent, serverProvider, } from '../registry.js';
|
|
2
|
+
import { hookSpec } from '../render/hooks.js';
|
|
3
|
+
import { badHook } from '../render/warn.js';
|
|
4
|
+
import { escapeAttr, onlyWhitespace } from './escape.js';
|
|
5
|
+
import { absent, attrText, checkAttrName, lightChildrenError, multiText } from './values.js';
|
|
6
|
+
export const PLAIN = { k: 'plain' };
|
|
7
|
+
export class Markup {
|
|
8
|
+
/**
|
|
9
|
+
* `dev`: development markers and checks. `scope`: the nearest provider's scope, passed to
|
|
10
|
+
* components (undefined: the request's).
|
|
11
|
+
*/
|
|
12
|
+
constructor(dev, scope) {
|
|
13
|
+
this.dev = dev;
|
|
14
|
+
this.scope = scope;
|
|
15
|
+
this.buf = '';
|
|
16
|
+
this.items = [];
|
|
17
|
+
}
|
|
18
|
+
write(markup) {
|
|
19
|
+
this.buf += markup;
|
|
20
|
+
}
|
|
21
|
+
/** The markup and deferred components written so far. */
|
|
22
|
+
done() {
|
|
23
|
+
this.flush();
|
|
24
|
+
return this.items;
|
|
25
|
+
}
|
|
26
|
+
flush() {
|
|
27
|
+
if (this.buf !== '')
|
|
28
|
+
this.items.push(this.buf);
|
|
29
|
+
this.buf = '';
|
|
30
|
+
}
|
|
31
|
+
/** `name=${v}` or `name="a ${v} b"`; returns the index of the next value. */
|
|
32
|
+
attr(s, values, at, opening) {
|
|
33
|
+
const { name, strings } = s;
|
|
34
|
+
const value = strings === undefined
|
|
35
|
+
? attrText(values[at], s, name, this.dev)
|
|
36
|
+
: multiText(values, at, strings, s, name, this.dev);
|
|
37
|
+
if (value !== null)
|
|
38
|
+
this.valued(name, value, opening);
|
|
39
|
+
return at + (strings === undefined ? 1 : strings.length - 1);
|
|
40
|
+
}
|
|
41
|
+
valued(name, value, opening) {
|
|
42
|
+
this.buf += ` ${name}="${escapeAttr(value)}"`;
|
|
43
|
+
if (opening !== undefined)
|
|
44
|
+
opening.attributes[name] = value;
|
|
45
|
+
}
|
|
46
|
+
/** A present boolean attribute (`?name`, or `true` from a hook's server half). */
|
|
47
|
+
bare(name, opening) {
|
|
48
|
+
this.buf += ` ${name}`;
|
|
49
|
+
if (opening !== undefined)
|
|
50
|
+
opening.attributes[name] = '';
|
|
51
|
+
}
|
|
52
|
+
/** An element hook: its server half's attributes (02 "Element hooks"). */
|
|
53
|
+
hook(v, opening) {
|
|
54
|
+
if (absent(v))
|
|
55
|
+
return;
|
|
56
|
+
const spec = hookSpec(v);
|
|
57
|
+
if (spec === undefined) {
|
|
58
|
+
if (this.dev)
|
|
59
|
+
badHook(v);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const attributes = spec.server?.(v.args) ?? {};
|
|
63
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
64
|
+
checkAttrName(name);
|
|
65
|
+
if (value === true)
|
|
66
|
+
this.bare(name, opening);
|
|
67
|
+
else
|
|
68
|
+
this.valued(name, value, opening);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** A custom element's start tag: a Gyral component, a provider, or a plain element. */
|
|
72
|
+
open(tag, html, attrs) {
|
|
73
|
+
this.buf += html;
|
|
74
|
+
const component = serverComponent(tag);
|
|
75
|
+
const provider = component === undefined ? serverProvider(tag) : undefined;
|
|
76
|
+
if (component === undefined && provider === undefined)
|
|
77
|
+
return undefined;
|
|
78
|
+
return { tag, component, provider, attributes: Object.fromEntries(attrs), properties: {} };
|
|
79
|
+
}
|
|
80
|
+
openEnd(opening) {
|
|
81
|
+
if (opening === undefined) {
|
|
82
|
+
this.buf += '>';
|
|
83
|
+
return PLAIN;
|
|
84
|
+
}
|
|
85
|
+
const { attributes, properties } = opening;
|
|
86
|
+
const messages = properties['initialMessages'];
|
|
87
|
+
const input = {
|
|
88
|
+
attributes,
|
|
89
|
+
properties,
|
|
90
|
+
initialMessages: Array.isArray(messages) ? messages : undefined,
|
|
91
|
+
scope: this.scope,
|
|
92
|
+
};
|
|
93
|
+
if (opening.provider !== undefined) {
|
|
94
|
+
const provided = opening.provider.open(input);
|
|
95
|
+
for (const [name, value] of Object.entries(provided.attributes)) {
|
|
96
|
+
this.buf += ` ${name}="${escapeAttr(value)}"`;
|
|
97
|
+
}
|
|
98
|
+
this.buf += '>';
|
|
99
|
+
const frame = { k: 'provider', outer: this.scope };
|
|
100
|
+
this.scope = provided.scope;
|
|
101
|
+
return frame;
|
|
102
|
+
}
|
|
103
|
+
const component = opening.component; // Sound: open() set one of the two.
|
|
104
|
+
this.flush();
|
|
105
|
+
this.items.push({ component, input });
|
|
106
|
+
return component.light ? { k: 'light', tag: opening.tag, items: this.items.length } : PLAIN;
|
|
107
|
+
}
|
|
108
|
+
close(frame, tag) {
|
|
109
|
+
if (frame.k === 'provider')
|
|
110
|
+
this.scope = frame.outer;
|
|
111
|
+
else if (frame.k === 'light') {
|
|
112
|
+
// Whitespace-only children are dropped; anything else is an error (ADR 0014).
|
|
113
|
+
if (this.items.length !== frame.items || !onlyWhitespace(this.buf)) {
|
|
114
|
+
lightChildrenError(frame.tag);
|
|
115
|
+
}
|
|
116
|
+
this.buf = '';
|
|
117
|
+
}
|
|
118
|
+
this.buf += `</${tag}>`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.js","sourceRoot":"","sources":["../../../src/view/server/tags.ts"],"names":[],"mappings":"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","sourcesContent":["// Start tags and custom elements for the server renderer (view/06-server.md \"Writing a template\n// result\", \"Components\"): attribute, boolean and hook holes, and what a custom element's\n// `open`/`openEnd`/`close` segments do. A Gyral component's start tag is written here, but\n// its content (seed, shadow root, view) is deferred: the walk returns strings with the\n// deferred components between them, and `render` (index.ts) expands each one in its own step,\n// so every component boundary is a chunk boundary. A provider (`<gyral-stores>`) is a plain\n// element that gives its subtree a scope until its end tag.\nimport type { Segment } from '../normalize/types.js';\nimport {\n serverComponent,\n serverProvider,\n type ServerComponent,\n type ServerProvider,\n type ServerRenderInput,\n} from '../registry.js';\nimport { hookSpec, type HookResult } from '../render/hooks.js';\nimport { badHook } from '../render/warn.js';\nimport { escapeAttr, onlyWhitespace } from './escape.js';\nimport { absent, attrText, checkAttrName, lightChildrenError, multiText } from './values.js';\n\n/** A component whose content is written later, in its own render step. */\nexport interface Deferred {\n readonly component: ServerComponent;\n readonly input: ServerRenderInput;\n}\n\n/** What a walk produces: markup, with deferred components in document order. */\nexport type Item = string | Deferred;\n\n/** A custom element's start tag while its attribute holes are written. */\nexport interface Opening {\n readonly tag: string;\n readonly component: ServerComponent | undefined;\n readonly provider: ServerProvider | undefined;\n readonly attributes: Record<string, string>;\n readonly properties: Record<string, unknown>;\n}\n\n/** An open custom element until its end tag: plain, a provider, or a light component. */\nexport type Frame =\n | { readonly k: 'plain' }\n | { readonly k: 'provider'; readonly outer: unknown }\n | { readonly k: 'light'; readonly tag: string; readonly items: number };\n\nexport const PLAIN: Frame = { k: 'plain' };\n\nexport class Markup {\n protected buf = '';\n protected readonly items: Item[] = [];\n\n /**\n * `dev`: development markers and checks. `scope`: the nearest provider's scope, passed to\n * components (undefined: the request's).\n */\n constructor(\n protected readonly dev: boolean,\n protected scope: unknown,\n ) {}\n\n write(markup: string): void {\n this.buf += markup;\n }\n\n /** The markup and deferred components written so far. */\n done(): Item[] {\n this.flush();\n return this.items;\n }\n\n private flush(): void {\n if (this.buf !== '') this.items.push(this.buf);\n this.buf = '';\n }\n\n /** `name=${v}` or `name=\"a ${v} b\"`; returns the index of the next value. */\n protected attr(\n s: Extract<Segment, { k: 'attr' }>,\n values: readonly unknown[],\n at: number,\n opening: Opening | undefined,\n ): number {\n const { name, strings } = s;\n const value =\n strings === undefined\n ? attrText(values[at], s, name, this.dev)\n : multiText(values, at, strings, s, name, this.dev);\n if (value !== null) this.valued(name, value, opening);\n return at + (strings === undefined ? 1 : strings.length - 1);\n }\n\n private valued(name: string, value: string, opening: Opening | undefined): void {\n this.buf += ` ${name}=\"${escapeAttr(value)}\"`;\n if (opening !== undefined) opening.attributes[name] = value;\n }\n\n /** A present boolean attribute (`?name`, or `true` from a hook's server half). */\n protected bare(name: string, opening: Opening | undefined): void {\n this.buf += ` ${name}`;\n if (opening !== undefined) opening.attributes[name] = '';\n }\n\n /** An element hook: its server half's attributes (02 \"Element hooks\"). */\n protected hook(v: unknown, opening: Opening | undefined): void {\n if (absent(v)) return;\n const spec = hookSpec(v);\n if (spec === undefined) {\n if (this.dev) badHook(v);\n return;\n }\n const attributes = spec.server?.((v as HookResult).args) ?? {};\n for (const [name, value] of Object.entries(attributes)) {\n checkAttrName(name);\n if (value === true) this.bare(name, opening);\n else this.valued(name, value, opening);\n }\n }\n\n /** A custom element's start tag: a Gyral component, a provider, or a plain element. */\n protected open(\n tag: string,\n html: string,\n attrs: readonly (readonly [string, string])[],\n ): Opening | undefined {\n this.buf += html;\n const component = serverComponent(tag);\n const provider = component === undefined ? serverProvider(tag) : undefined;\n if (component === undefined && provider === undefined) return undefined;\n return { tag, component, provider, attributes: Object.fromEntries(attrs), properties: {} };\n }\n\n protected openEnd(opening: Opening | undefined): Frame {\n if (opening === undefined) {\n this.buf += '>';\n return PLAIN;\n }\n const { attributes, properties } = opening;\n const messages = properties['initialMessages'];\n const input: ServerRenderInput = {\n attributes,\n properties,\n initialMessages: Array.isArray(messages) ? (messages as readonly unknown[]) : undefined,\n scope: this.scope,\n };\n if (opening.provider !== undefined) {\n const provided = opening.provider.open(input);\n for (const [name, value] of Object.entries(provided.attributes)) {\n this.buf += ` ${name}=\"${escapeAttr(value)}\"`;\n }\n this.buf += '>';\n const frame: Frame = { k: 'provider', outer: this.scope };\n this.scope = provided.scope;\n return frame;\n }\n const component = opening.component as ServerComponent; // Sound: open() set one of the two.\n this.flush();\n this.items.push({ component, input });\n return component.light ? { k: 'light', tag: opening.tag, items: this.items.length } : PLAIN;\n }\n\n protected close(frame: Frame, tag: string): void {\n if (frame.k === 'provider') this.scope = frame.outer;\n else if (frame.k === 'light') {\n // Whitespace-only children are dropped; anything else is an error (ADR 0014).\n if (this.items.length !== frame.items || !onlyWhitespace(this.buf)) {\n lightChildrenError(frame.tag);\n }\n this.buf = '';\n }\n this.buf += `</${tag}>`;\n }\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const absent: (v: unknown) => boolean;
|
|
2
|
+
export declare const truthy: (v: unknown) => boolean;
|
|
3
|
+
/** A Promise in any hole: server renders are synchronous (06 "API"). Always an error. */
|
|
4
|
+
export declare function checkPromise(v: object, where: string): void;
|
|
5
|
+
/** `name=${v}`: the value to write, or null to leave the attribute out. */
|
|
6
|
+
export declare function attrText(v: unknown, at: object, name: string, dev: boolean): string | null;
|
|
7
|
+
/** `name="a ${x} b"`: pieces joined; `nothing` in any piece leaves the attribute out. */
|
|
8
|
+
export declare function multiText(values: readonly unknown[], from: number, strings: readonly string[], at: object, name: string, dev: boolean): string | null;
|
|
9
|
+
/** `<textarea>`/`<title>` content: child-hole rules flattened to a string, as on the client. */
|
|
10
|
+
export declare function textContent(v: unknown, at: object, dev: boolean): string;
|
|
11
|
+
/** Text written directly in table structure would be foster-parented (rule 7). */
|
|
12
|
+
export declare function fosterError(text: string, parent: string): never;
|
|
13
|
+
/** A light-DOM component owns its children (ADR 0014). */
|
|
14
|
+
export declare function lightChildrenError(tag: string): never;
|
|
15
|
+
/** An attribute name from a hook's server half must not break the start tag. */
|
|
16
|
+
export declare function checkAttrName(name: string): void;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Value rules for attribute and text-content holes on the server (view/02-bindings.md
|
|
2
|
+
// "Attribute values", "Live form state"; view/06-server.md "Writing a template result"),
|
|
3
|
+
// identical to the client's (render/attr-parts.ts) so both sides write the same attributes.
|
|
4
|
+
// And the server-only errors: a Promise anywhere (renders are synchronous), text the parser
|
|
5
|
+
// would move out of table structure, children given to a light-DOM component.
|
|
6
|
+
import { nothing } from '../render/values.js';
|
|
7
|
+
import { warnTrue, warnValue } from '../render/warn.js';
|
|
8
|
+
const SPEC = 'docs/design-docs/view';
|
|
9
|
+
export const absent = (v) => v === null || v === undefined || v === nothing;
|
|
10
|
+
export const truthy = (v) => v !== nothing && !!v;
|
|
11
|
+
const isObject = (v) => (typeof v === 'object' && v !== null) || typeof v === 'function';
|
|
12
|
+
/** `String(v)`: objects and functions as the client writes them (render/attr-parts.ts). */
|
|
13
|
+
const text = String;
|
|
14
|
+
const isThenable = (v) => typeof v.then === 'function';
|
|
15
|
+
/** A Promise in any hole: server renders are synchronous (06 "API"). Always an error. */
|
|
16
|
+
export function checkPromise(v, where) {
|
|
17
|
+
if (!isThenable(v))
|
|
18
|
+
return;
|
|
19
|
+
throw new TypeError(`gyral: ${where} got a Promise. Server renders are synchronous: state is known before ` +
|
|
20
|
+
'rendering, so load the data first (in the route handler or a form action) and pass ' +
|
|
21
|
+
`it as props (${SPEC}/06-server.md "API").`);
|
|
22
|
+
}
|
|
23
|
+
/** `String(v)` for an attribute value, with the development warning for objects. */
|
|
24
|
+
function written(v, at, name, dev) {
|
|
25
|
+
if (typeof v === 'string')
|
|
26
|
+
return v;
|
|
27
|
+
if (isObject(v)) {
|
|
28
|
+
checkPromise(v, `the attribute ${name}`);
|
|
29
|
+
if (dev)
|
|
30
|
+
warnValue(at, v, `the attribute ${name}`);
|
|
31
|
+
}
|
|
32
|
+
return text(v);
|
|
33
|
+
}
|
|
34
|
+
/** `name=${v}`: the value to write, or null to leave the attribute out. */
|
|
35
|
+
export function attrText(v, at, name, dev) {
|
|
36
|
+
return absent(v) ? null : written(v, at, name, dev);
|
|
37
|
+
}
|
|
38
|
+
/** `name="a ${x} b"`: pieces joined; `nothing` in any piece leaves the attribute out. */
|
|
39
|
+
export function multiText(values, from, strings, at, name, dev) {
|
|
40
|
+
let joined = strings[0] ?? '';
|
|
41
|
+
for (let i = 1; i < strings.length; i++) {
|
|
42
|
+
const v = values[from + i - 1];
|
|
43
|
+
if (v === nothing)
|
|
44
|
+
return null;
|
|
45
|
+
joined +=
|
|
46
|
+
(v === null || v === undefined ? '' : written(v, at, name, dev)) + strings[i];
|
|
47
|
+
}
|
|
48
|
+
return joined;
|
|
49
|
+
}
|
|
50
|
+
/** `<textarea>`/`<title>` content: child-hole rules flattened to a string, as on the client. */
|
|
51
|
+
export function textContent(v, at, dev) {
|
|
52
|
+
if (typeof v === 'string')
|
|
53
|
+
return v;
|
|
54
|
+
if (typeof v === 'number')
|
|
55
|
+
return String(v);
|
|
56
|
+
if (isObject(v)) {
|
|
57
|
+
checkPromise(v, 'the text content');
|
|
58
|
+
if (dev)
|
|
59
|
+
warnValue(at, v, 'the text content');
|
|
60
|
+
return text(v);
|
|
61
|
+
}
|
|
62
|
+
if (dev && v === true)
|
|
63
|
+
warnTrue(at);
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
/** Text written directly in table structure would be foster-parented (rule 7). */
|
|
67
|
+
export function fosterError(text, parent) {
|
|
68
|
+
throw new Error(`gyral: the text ${JSON.stringify(text.slice(0, 40))} is written directly inside ` +
|
|
69
|
+
`<${parent}>. The HTML parser moves text out of table structure (foster parenting), so ` +
|
|
70
|
+
'the page would differ from what the client renders. Put text inside a <td>, <th> or ' +
|
|
71
|
+
`<caption> (${SPEC}/09-template-rules.md, rule 7).`);
|
|
72
|
+
}
|
|
73
|
+
/** A light-DOM component owns its children (ADR 0014). */
|
|
74
|
+
export function lightChildrenError(tag) {
|
|
75
|
+
throw new Error(`gyral: <${tag}> renders in light DOM (shadow: false), so it owns its children, but its ` +
|
|
76
|
+
'parent wrote children inside it. Pass the data as props instead, or make it a shadow ' +
|
|
77
|
+
`component and use <slot> (${SPEC}/06-server.md "Components", ADR 0014).`);
|
|
78
|
+
}
|
|
79
|
+
/** An attribute name from a hook's server half must not break the start tag. */
|
|
80
|
+
export function checkAttrName(name) {
|
|
81
|
+
if (/^[^\s"'>/=\0]+$/.test(name))
|
|
82
|
+
return;
|
|
83
|
+
throw new TypeError(`gyral: an element hook's server half returned the attribute name ${JSON.stringify(name)}, ` +
|
|
84
|
+
`which isn't a valid attribute name (${SPEC}/02-bindings.md "Element hooks").`);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.js","sourceRoot":"","sources":["../../../src/view/server/values.ts"],"names":[],"mappings":"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","sourcesContent":["// Value rules for attribute and text-content holes on the server (view/02-bindings.md\n// \"Attribute values\", \"Live form state\"; view/06-server.md \"Writing a template result\"),\n// identical to the client's (render/attr-parts.ts) so both sides write the same attributes.\n// And the server-only errors: a Promise anywhere (renders are synchronous), text the parser\n// would move out of table structure, children given to a light-DOM component.\nimport { nothing } from '../render/values.js';\nimport { warnTrue, warnValue } from '../render/warn.js';\n\nconst SPEC = 'docs/design-docs/view';\n\nexport const absent = (v: unknown): boolean => v === null || v === undefined || v === nothing;\nexport const truthy = (v: unknown): boolean => v !== nothing && !!v;\n\nconst isObject = (v: unknown): v is object =>\n (typeof v === 'object' && v !== null) || typeof v === 'function';\n\n/** `String(v)`: objects and functions as the client writes them (render/attr-parts.ts). */\nconst text = String as (value: unknown) => string;\n\nconst isThenable = (v: object): boolean =>\n typeof (v as { readonly then?: unknown }).then === 'function';\n\n/** A Promise in any hole: server renders are synchronous (06 \"API\"). Always an error. */\nexport function checkPromise(v: object, where: string): void {\n if (!isThenable(v)) return;\n throw new TypeError(\n `gyral: ${where} got a Promise. Server renders are synchronous: state is known before ` +\n 'rendering, so load the data first (in the route handler or a form action) and pass ' +\n `it as props (${SPEC}/06-server.md \"API\").`,\n );\n}\n\n/** `String(v)` for an attribute value, with the development warning for objects. */\nfunction written(v: unknown, at: object, name: string, dev: boolean): string {\n if (typeof v === 'string') return v;\n if (isObject(v)) {\n checkPromise(v, `the attribute ${name}`);\n if (dev) warnValue(at, v, `the attribute ${name}`);\n }\n return text(v);\n}\n\n/** `name=${v}`: the value to write, or null to leave the attribute out. */\nexport function attrText(v: unknown, at: object, name: string, dev: boolean): string | null {\n return absent(v) ? null : written(v, at, name, dev);\n}\n\n/** `name=\"a ${x} b\"`: pieces joined; `nothing` in any piece leaves the attribute out. */\nexport function multiText(\n values: readonly unknown[],\n from: number,\n strings: readonly string[],\n at: object,\n name: string,\n dev: boolean,\n): string | null {\n let joined = strings[0] ?? '';\n for (let i = 1; i < strings.length; i++) {\n const v = values[from + i - 1];\n if (v === nothing) return null;\n joined +=\n (v === null || v === undefined ? '' : written(v, at, name, dev)) + (strings[i] as string);\n }\n return joined;\n}\n\n/** `<textarea>`/`<title>` content: child-hole rules flattened to a string, as on the client. */\nexport function textContent(v: unknown, at: object, dev: boolean): string {\n if (typeof v === 'string') return v;\n if (typeof v === 'number') return String(v);\n if (isObject(v)) {\n checkPromise(v, 'the text content');\n if (dev) warnValue(at, v, 'the text content');\n return text(v);\n }\n if (dev && v === true) warnTrue(at);\n return '';\n}\n\n/** Text written directly in table structure would be foster-parented (rule 7). */\nexport function fosterError(text: string, parent: string): never {\n throw new Error(\n `gyral: the text ${JSON.stringify(text.slice(0, 40))} is written directly inside ` +\n `<${parent}>. The HTML parser moves text out of table structure (foster parenting), so ` +\n 'the page would differ from what the client renders. Put text inside a <td>, <th> or ' +\n `<caption> (${SPEC}/09-template-rules.md, rule 7).`,\n );\n}\n\n/** A light-DOM component owns its children (ADR 0014). */\nexport function lightChildrenError(tag: string): never {\n throw new Error(\n `gyral: <${tag}> renders in light DOM (shadow: false), so it owns its children, but its ` +\n 'parent wrote children inside it. Pass the data as props instead, or make it a shadow ' +\n `component and use <slot> (${SPEC}/06-server.md \"Components\", ADR 0014).`,\n );\n}\n\n/** An attribute name from a hook's server half must not break the start tag. */\nexport function checkAttrName(name: string): void {\n if (/^[^\\s\"'>/=\\0]+$/.test(name)) return;\n throw new TypeError(\n `gyral: an element hook's server half returned the attribute name ${JSON.stringify(name)}, ` +\n `which isn't a valid attribute name (${SPEC}/02-bindings.md \"Element hooks\").`,\n );\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type TemplateResult } from '../template.js';
|
|
2
|
+
import { Markup } from './tags.js';
|
|
3
|
+
export type { Deferred, Item } from './tags.js';
|
|
4
|
+
/** Warning key for values outside any template (the render root, a component's view). */
|
|
5
|
+
export declare const ROOT: object;
|
|
6
|
+
export declare class Writer extends Markup {
|
|
7
|
+
/** Writing a page shell's holes (a `server` template): never hydrated, so no anchors (06). */
|
|
8
|
+
private shell;
|
|
9
|
+
/** A child value (02 "Child values"). `table`: the parent, when it is table structure. */
|
|
10
|
+
child(v: unknown, table: string | undefined, at: object): void;
|
|
11
|
+
private object;
|
|
12
|
+
/** `each(…)`: rows one after another, no markers (03). Keys are checked in development. */
|
|
13
|
+
private list;
|
|
14
|
+
/** One template instance: its segments with this render's values. */
|
|
15
|
+
template(result: TemplateResult, table: string | undefined): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// The server renderer's walk (view/06-server.md "Writing a template result"): writes a child
|
|
2
|
+
// value by walking template objects' `segments` (01), never tokenizing or building a DOM. Each
|
|
3
|
+
// string segment is written verbatim; each hole op takes its value(s). Start tags and custom
|
|
4
|
+
// elements are tags.ts.
|
|
5
|
+
import { isList, nothing, rawHtml } from '../render/values.js';
|
|
6
|
+
import { badChild, badKey, warnTrue } from '../render/warn.js';
|
|
7
|
+
import { isTemplateResult, templateOf } from '../template.js';
|
|
8
|
+
import { escapeText, onlyWhitespace } from './escape.js';
|
|
9
|
+
import { Markup, PLAIN } from './tags.js';
|
|
10
|
+
import { checkPromise, fosterError, textContent, truthy } from './values.js';
|
|
11
|
+
/** Parents in which the parser moves non-whitespace text out (foster parenting). */
|
|
12
|
+
const TABLE = new Set(['table', 'tbody', 'thead', 'tfoot', 'tr']);
|
|
13
|
+
const tableTag = (parent) => (TABLE.has(parent) ? parent : undefined);
|
|
14
|
+
/** Warning key for values outside any template (the render root, a component's view). */
|
|
15
|
+
export const ROOT = {};
|
|
16
|
+
export class Writer extends Markup {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
/** Writing a page shell's holes (a `server` template): never hydrated, so no anchors (06). */
|
|
20
|
+
this.shell = false;
|
|
21
|
+
}
|
|
22
|
+
/** A child value (02 "Child values"). `table`: the parent, when it is table structure. */
|
|
23
|
+
child(v, table, at) {
|
|
24
|
+
switch (typeof v) {
|
|
25
|
+
case 'string':
|
|
26
|
+
if (v === '')
|
|
27
|
+
return;
|
|
28
|
+
if (table !== undefined && this.dev && !onlyWhitespace(v))
|
|
29
|
+
fosterError(v, table);
|
|
30
|
+
this.buf += escapeText(v);
|
|
31
|
+
return;
|
|
32
|
+
case 'number':
|
|
33
|
+
if (table !== undefined && this.dev)
|
|
34
|
+
fosterError(String(v), table);
|
|
35
|
+
this.buf += String(v);
|
|
36
|
+
return;
|
|
37
|
+
case 'object':
|
|
38
|
+
if (v !== null)
|
|
39
|
+
this.object(v, table, at);
|
|
40
|
+
return;
|
|
41
|
+
case 'boolean':
|
|
42
|
+
if (v && this.dev)
|
|
43
|
+
warnTrue(at);
|
|
44
|
+
return;
|
|
45
|
+
case 'undefined':
|
|
46
|
+
return;
|
|
47
|
+
default:
|
|
48
|
+
if (v !== nothing && this.dev)
|
|
49
|
+
badChild(v);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
object(v, table, at) {
|
|
53
|
+
if (isTemplateResult(v)) {
|
|
54
|
+
this.template(v, table);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (isList(v)) {
|
|
58
|
+
this.list(v, table, at);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (Array.isArray(v)) {
|
|
62
|
+
for (const item of v)
|
|
63
|
+
this.child(item, table, at);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const raw = rawHtml(v);
|
|
67
|
+
if (raw !== undefined) {
|
|
68
|
+
// The start anchor hydration finds (02 "raw(html)"); a page shell is never hydrated.
|
|
69
|
+
this.buf += this.shell ? raw : `<!---->${raw}`;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
checkPromise(v, 'a child hole');
|
|
73
|
+
if (this.dev)
|
|
74
|
+
badChild(v);
|
|
75
|
+
}
|
|
76
|
+
/** `each(…)`: rows one after another, no markers (03). Keys are checked in development. */
|
|
77
|
+
list(v, table, at) {
|
|
78
|
+
const { items, key, row, pick } = v;
|
|
79
|
+
const seen = this.dev ? new Set() : undefined;
|
|
80
|
+
for (let i = 0; i < items.length; i++) {
|
|
81
|
+
const item = items[i];
|
|
82
|
+
if (seen !== undefined) {
|
|
83
|
+
const k = key(item);
|
|
84
|
+
const dup = seen.has(k);
|
|
85
|
+
if (dup || (typeof k !== 'string' && typeof k !== 'number'))
|
|
86
|
+
badKey(k, i, dup);
|
|
87
|
+
seen.add(k);
|
|
88
|
+
}
|
|
89
|
+
this.child(row(item, pick === undefined ? undefined : pick(item)), table, at);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** One template instance: its segments with this render's values. */
|
|
93
|
+
template(result, table) {
|
|
94
|
+
const template = templateOf(result);
|
|
95
|
+
const segments = template.segments;
|
|
96
|
+
if (segments === undefined) {
|
|
97
|
+
throw new Error(`gyral: template ${JSON.stringify(template.html.slice(0, 40))} has no server ` +
|
|
98
|
+
'segments: it was compiled for the client. Render on the server with an SSR build ' +
|
|
99
|
+
'(or the runtime normalizer), which keeps them ' +
|
|
100
|
+
'(docs/design-docs/view/01-templates.md "Compiled").');
|
|
101
|
+
}
|
|
102
|
+
// Development marker: hydration checks the id before each instance (07). Page shells are
|
|
103
|
+
// never hydrated.
|
|
104
|
+
if (this.dev && !template.server)
|
|
105
|
+
this.buf += `<!--gyral:${template.id ?? ''}-->`;
|
|
106
|
+
const values = result.values;
|
|
107
|
+
const outer = this.shell;
|
|
108
|
+
this.shell = template.server === true;
|
|
109
|
+
let at = 0;
|
|
110
|
+
let opening;
|
|
111
|
+
let frames;
|
|
112
|
+
for (const s of segments) {
|
|
113
|
+
if (typeof s === 'string') {
|
|
114
|
+
this.buf += s;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
switch (s.k) {
|
|
118
|
+
case 'child':
|
|
119
|
+
this.child(values[at++], s.in === undefined ? table : tableTag(s.in), s);
|
|
120
|
+
break;
|
|
121
|
+
case 'text':
|
|
122
|
+
this.buf += escapeText(textContent(values[at++], s, this.dev));
|
|
123
|
+
break;
|
|
124
|
+
case 'attr':
|
|
125
|
+
at = this.attr(s, values, at, opening);
|
|
126
|
+
break;
|
|
127
|
+
case 'bool':
|
|
128
|
+
// `?indeterminate` has no attribute: it is the property only (02 "Live form state").
|
|
129
|
+
if (truthy(values[at]) && s.name !== 'indeterminate')
|
|
130
|
+
this.bare(s.name, opening);
|
|
131
|
+
at++;
|
|
132
|
+
break;
|
|
133
|
+
case 'prop':
|
|
134
|
+
if (opening !== undefined)
|
|
135
|
+
opening.properties[s.name] = values[at];
|
|
136
|
+
at++;
|
|
137
|
+
break;
|
|
138
|
+
case 'hook':
|
|
139
|
+
this.hook(values[at++], opening);
|
|
140
|
+
break;
|
|
141
|
+
case 'open':
|
|
142
|
+
opening = this.open(s.tag, s.html, s.attrs);
|
|
143
|
+
break;
|
|
144
|
+
case 'openEnd':
|
|
145
|
+
(frames ??= []).push(this.openEnd(opening));
|
|
146
|
+
opening = undefined;
|
|
147
|
+
break;
|
|
148
|
+
case 'close':
|
|
149
|
+
this.close(frames?.pop() ?? PLAIN, s.tag);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
this.shell = outer;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/view/server/writer.ts"],"names":[],"mappings":"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","sourcesContent":["// The server renderer's walk (view/06-server.md \"Writing a template result\"): writes a child\n// value by walking template objects' `segments` (01), never tokenizing or building a DOM. Each\n// string segment is written verbatim; each hole op takes its value(s). Start tags and custom\n// elements are tags.ts.\nimport { isList, nothing, rawHtml, type ListResult } from '../render/values.js';\nimport { badChild, badKey, warnTrue } from '../render/warn.js';\nimport { isTemplateResult, templateOf, type TemplateResult } from '../template.js';\nimport { escapeText, onlyWhitespace } from './escape.js';\nimport { Markup, PLAIN, type Frame, type Opening } from './tags.js';\nimport { checkPromise, fosterError, textContent, truthy } from './values.js';\n\nexport type { Deferred, Item } from './tags.js';\n\n/** Parents in which the parser moves non-whitespace text out (foster parenting). */\nconst TABLE = new Set(['table', 'tbody', 'thead', 'tfoot', 'tr']);\n\nconst tableTag = (parent: string): string | undefined => (TABLE.has(parent) ? parent : undefined);\n\n/** Warning key for values outside any template (the render root, a component's view). */\nexport const ROOT: object = {};\n\nexport class Writer extends Markup {\n /** Writing a page shell's holes (a `server` template): never hydrated, so no anchors (06). */\n private shell = false;\n\n /** A child value (02 \"Child values\"). `table`: the parent, when it is table structure. */\n child(v: unknown, table: string | undefined, at: object): void {\n switch (typeof v) {\n case 'string':\n if (v === '') return;\n if (table !== undefined && this.dev && !onlyWhitespace(v)) fosterError(v, table);\n this.buf += escapeText(v);\n return;\n case 'number':\n if (table !== undefined && this.dev) fosterError(String(v), table);\n this.buf += String(v);\n return;\n case 'object':\n if (v !== null) this.object(v, table, at);\n return;\n case 'boolean':\n if (v && this.dev) warnTrue(at);\n return;\n case 'undefined':\n return;\n default:\n if (v !== nothing && this.dev) badChild(v);\n }\n }\n\n private object(v: object, table: string | undefined, at: object): void {\n if (isTemplateResult(v)) {\n this.template(v, table);\n return;\n }\n if (isList(v)) {\n this.list(v, table, at);\n return;\n }\n if (Array.isArray(v)) {\n for (const item of v as readonly unknown[]) this.child(item, table, at);\n return;\n }\n const raw = rawHtml(v);\n if (raw !== undefined) {\n // The start anchor hydration finds (02 \"raw(html)\"); a page shell is never hydrated.\n this.buf += this.shell ? raw : `<!---->${raw}`;\n return;\n }\n checkPromise(v, 'a child hole');\n if (this.dev) badChild(v);\n }\n\n /** `each(…)`: rows one after another, no markers (03). Keys are checked in development. */\n private list(v: ListResult, table: string | undefined, at: object): void {\n const { items, key, row, pick } = v;\n const seen = this.dev ? new Set<unknown>() : undefined;\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (seen !== undefined) {\n const k = key(item);\n const dup = seen.has(k);\n if (dup || (typeof k !== 'string' && typeof k !== 'number')) badKey(k, i, dup);\n seen.add(k);\n }\n this.child(row(item, pick === undefined ? undefined : pick(item)), table, at);\n }\n }\n\n /** One template instance: its segments with this render's values. */\n template(result: TemplateResult, table: string | undefined): void {\n const template = templateOf(result);\n const segments = template.segments;\n if (segments === undefined) {\n throw new Error(\n `gyral: template ${JSON.stringify(template.html.slice(0, 40))} has no server ` +\n 'segments: it was compiled for the client. Render on the server with an SSR build ' +\n '(or the runtime normalizer), which keeps them ' +\n '(docs/design-docs/view/01-templates.md \"Compiled\").',\n );\n }\n // Development marker: hydration checks the id before each instance (07). Page shells are\n // never hydrated.\n if (this.dev && !template.server) this.buf += `<!--gyral:${template.id ?? ''}-->`;\n const values = result.values;\n const outer = this.shell;\n this.shell = template.server === true;\n let at = 0;\n let opening: Opening | undefined;\n let frames: Frame[] | undefined;\n for (const s of segments) {\n if (typeof s === 'string') {\n this.buf += s;\n continue;\n }\n switch (s.k) {\n case 'child':\n this.child(values[at++], s.in === undefined ? table : tableTag(s.in), s);\n break;\n case 'text':\n this.buf += escapeText(textContent(values[at++], s, this.dev));\n break;\n case 'attr':\n at = this.attr(s, values, at, opening);\n break;\n case 'bool':\n // `?indeterminate` has no attribute: it is the property only (02 \"Live form state\").\n if (truthy(values[at]) && s.name !== 'indeterminate') this.bare(s.name, opening);\n at++;\n break;\n case 'prop':\n if (opening !== undefined) opening.properties[s.name] = values[at];\n at++;\n break;\n case 'hook':\n this.hook(values[at++], opening);\n break;\n case 'open':\n opening = this.open(s.tag, s.html, s.attrs);\n break;\n case 'openEnd':\n (frames ??= []).push(this.openEnd(opening));\n opening = undefined;\n break;\n case 'close':\n this.close(frames?.pop() ?? PLAIN, s.tag);\n }\n }\n this.shell = outer;\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
declare const STYLE: unique symbol;
|
|
2
|
+
/** What `css` returns: CSS text, mapped to one shared `CSSStyleSheet` in the browser. */
|
|
3
|
+
export interface StyleSource {
|
|
4
|
+
readonly [STYLE]: true;
|
|
5
|
+
readonly text: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* What `spec.styles` accepts: `css` values, plain CSS strings (e.g.
|
|
9
|
+
* `import base from './base.css?inline'`) and arrays of them, nested freely. CSS is trusted
|
|
10
|
+
* author code: never interpolate user input.
|
|
11
|
+
*/
|
|
12
|
+
export type Styles = StyleSource | string | readonly Styles[];
|
|
13
|
+
/** A `css` interpolation: inserted as written (another `css` value inserts its text). */
|
|
14
|
+
export type CssValue = string | number | StyleSource;
|
|
15
|
+
export declare const isStyleSource: (value: unknown) => value is StyleSource;
|
|
16
|
+
/**
|
|
17
|
+
* Component CSS: `` css`p { margin-block: ${GAP}px; }` ``. Strings and numbers are inserted as
|
|
18
|
+
* written, so values from your own constants need no wrapper.
|
|
19
|
+
*/
|
|
20
|
+
export declare function css(strings: TemplateStringsArray, ...values: readonly CssValue[]): StyleSource;
|
|
21
|
+
/** The CSS texts of `styles`, flattened in order (the server writes them, 06). */
|
|
22
|
+
export declare function styleTexts(styles: Styles | undefined): string[];
|
|
23
|
+
/** One shared sheet per style source (or per distinct string), created on first use. */
|
|
24
|
+
export declare function sheetsFor(styles: Styles | undefined): CSSStyleSheet[];
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Component styles (view/08-styles.md "Authoring", "Browser"): the `css` tag and the shared
|
|
2
|
+
// sheets. A style source is CSS text plus a cache slot for its one `CSSStyleSheet`, created on
|
|
3
|
+
// first use with `replaceSync`; components that share a source share the sheet. Plain strings
|
|
4
|
+
// are style sources too (cached by text). The server reads the text (06), never the sheet.
|
|
5
|
+
const STYLE = Symbol('gyral.css');
|
|
6
|
+
export const isStyleSource = (value) => typeof value === 'object' && value !== null && STYLE in value;
|
|
7
|
+
/**
|
|
8
|
+
* Component CSS: `` css`p { margin-block: ${GAP}px; }` ``. Strings and numbers are inserted as
|
|
9
|
+
* written, so values from your own constants need no wrapper.
|
|
10
|
+
*/
|
|
11
|
+
export function css(strings, ...values) {
|
|
12
|
+
let text = strings[0] ?? '';
|
|
13
|
+
for (let i = 0; i < values.length; i++) {
|
|
14
|
+
const v = values[i];
|
|
15
|
+
text += (isStyleSource(v) ? v.text : String(v)) + (strings[i + 1] ?? '');
|
|
16
|
+
}
|
|
17
|
+
return { [STYLE]: true, text };
|
|
18
|
+
}
|
|
19
|
+
/** The CSS texts of `styles`, flattened in order (the server writes them, 06). */
|
|
20
|
+
export function styleTexts(styles) {
|
|
21
|
+
if (styles === undefined)
|
|
22
|
+
return [];
|
|
23
|
+
if (typeof styles === 'string')
|
|
24
|
+
return [styles];
|
|
25
|
+
if (isStyleSource(styles))
|
|
26
|
+
return [styles.text];
|
|
27
|
+
return styles.flatMap(styleTexts);
|
|
28
|
+
}
|
|
29
|
+
const bySource = new WeakMap();
|
|
30
|
+
const byText = new Map();
|
|
31
|
+
function sheetOf(text) {
|
|
32
|
+
const sheet = new CSSStyleSheet();
|
|
33
|
+
sheet.replaceSync(text);
|
|
34
|
+
return sheet;
|
|
35
|
+
}
|
|
36
|
+
/** One shared sheet per style source (or per distinct string), created on first use. */
|
|
37
|
+
export function sheetsFor(styles) {
|
|
38
|
+
if (styles === undefined)
|
|
39
|
+
return [];
|
|
40
|
+
if (typeof styles === 'string') {
|
|
41
|
+
let sheet = byText.get(styles);
|
|
42
|
+
if (sheet === undefined) {
|
|
43
|
+
sheet = sheetOf(styles);
|
|
44
|
+
byText.set(styles, sheet);
|
|
45
|
+
}
|
|
46
|
+
return [sheet];
|
|
47
|
+
}
|
|
48
|
+
if (isStyleSource(styles)) {
|
|
49
|
+
let sheet = bySource.get(styles);
|
|
50
|
+
if (sheet === undefined) {
|
|
51
|
+
sheet = sheetOf(styles.text);
|
|
52
|
+
bySource.set(styles, sheet);
|
|
53
|
+
}
|
|
54
|
+
return [sheet];
|
|
55
|
+
}
|
|
56
|
+
return styles.flatMap(sheetsFor);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/view/styles.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,+FAA+F;AAC/F,8FAA8F;AAC9F,2FAA2F;AAE3F,MAAM,KAAK,GAAkB,MAAM,CAAC,WAAW,CAAC,CAAC;AAkBjD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAc,EAAwB,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC;AAEhE;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,OAA6B,EAAE,GAAG,MAA2B;IAC/E,IAAI,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,MAA0B;IACnD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACpC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA8B,CAAC;AAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;AAEhD,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;IAClC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,SAAS,CAAC,MAA0B;IAClD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACpC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IACD,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7B,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC","sourcesContent":["// Component styles (view/08-styles.md \"Authoring\", \"Browser\"): the `css` tag and the shared\n// sheets. A style source is CSS text plus a cache slot for its one `CSSStyleSheet`, created on\n// first use with `replaceSync`; components that share a source share the sheet. Plain strings\n// are style sources too (cached by text). The server reads the text (06), never the sheet.\n\nconst STYLE: unique symbol = Symbol('gyral.css');\n\n/** What `css` returns: CSS text, mapped to one shared `CSSStyleSheet` in the browser. */\nexport interface StyleSource {\n readonly [STYLE]: true;\n readonly text: string;\n}\n\n/**\n * What `spec.styles` accepts: `css` values, plain CSS strings (e.g.\n * `import base from './base.css?inline'`) and arrays of them, nested freely. CSS is trusted\n * author code: never interpolate user input.\n */\nexport type Styles = StyleSource | string | readonly Styles[];\n\n/** A `css` interpolation: inserted as written (another `css` value inserts its text). */\nexport type CssValue = string | number | StyleSource;\n\nexport const isStyleSource = (value: unknown): value is StyleSource =>\n typeof value === 'object' && value !== null && STYLE in value;\n\n/**\n * Component CSS: `` css`p { margin-block: ${GAP}px; }` ``. Strings and numbers are inserted as\n * written, so values from your own constants need no wrapper.\n */\nexport function css(strings: TemplateStringsArray, ...values: readonly CssValue[]): StyleSource {\n let text = strings[0] ?? '';\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n text += (isStyleSource(v) ? v.text : String(v)) + (strings[i + 1] ?? '');\n }\n return { [STYLE]: true, text };\n}\n\n/** The CSS texts of `styles`, flattened in order (the server writes them, 06). */\nexport function styleTexts(styles: Styles | undefined): string[] {\n if (styles === undefined) return [];\n if (typeof styles === 'string') return [styles];\n if (isStyleSource(styles)) return [styles.text];\n return styles.flatMap(styleTexts);\n}\n\nconst bySource = new WeakMap<StyleSource, CSSStyleSheet>();\nconst byText = new Map<string, CSSStyleSheet>();\n\nfunction sheetOf(text: string): CSSStyleSheet {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(text);\n return sheet;\n}\n\n/** One shared sheet per style source (or per distinct string), created on first use. */\nexport function sheetsFor(styles: Styles | undefined): CSSStyleSheet[] {\n if (styles === undefined) return [];\n if (typeof styles === 'string') {\n let sheet = byText.get(styles);\n if (sheet === undefined) {\n sheet = sheetOf(styles);\n byText.set(styles, sheet);\n }\n return [sheet];\n }\n if (isStyleSource(styles)) {\n let sheet = bySource.get(styles);\n if (sheet === undefined) {\n sheet = sheetOf(styles.text);\n bySource.set(styles, sheet);\n }\n return [sheet];\n }\n return styles.flatMap(sheetsFor);\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// One <template> element per template object, created on first use (view/01-templates.md
|
|
2
|
+
// "Instantiation"). The browser's own parser builds it from the template HTML; the runtime
|
|
3
|
+
// preparer then checks the parse (rule 7). Server templates never get one (rule 11).
|
|
4
|
+
import { verify } from '#prepare';
|
|
5
|
+
import { DEV } from '#view-dev';
|
|
6
|
+
import { SERVER_ONLY, TemplateError } from './normalize/errors.js';
|
|
7
|
+
const elements = new WeakMap();
|
|
8
|
+
/** The cached <template> element for `template`, parsed (and checked) on first use. */
|
|
9
|
+
export function templateElement(template) {
|
|
10
|
+
const cached = elements.get(template);
|
|
11
|
+
if (cached !== undefined)
|
|
12
|
+
return cached;
|
|
13
|
+
if (template.server) {
|
|
14
|
+
// Production keeps a short message: the class and its explanation stay out of the bundle.
|
|
15
|
+
throw DEV
|
|
16
|
+
? new TemplateError(11, SERVER_ONLY, template.loc)
|
|
17
|
+
: new Error('gyral: template rule 11: a page shell rendered in the browser.');
|
|
18
|
+
}
|
|
19
|
+
const el = document.createElement('template');
|
|
20
|
+
el.innerHTML = template.html;
|
|
21
|
+
verify(template, el);
|
|
22
|
+
elements.set(template, el);
|
|
23
|
+
return el;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=template-element.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-element.js","sourceRoot":"","sources":["../../src/view/template-element.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,2FAA2F;AAC3F,qFAAqF;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGnE,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAuC,CAAC;AAEpE,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,QAAwB;IACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,0FAA0F;QAC1F,MAAM,GAAG;YACP,CAAC,CAAC,IAAI,aAAa,CAAC,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC;YAClD,CAAC,CAAC,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAC9C,EAAE,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC7B,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC;AACZ,CAAC","sourcesContent":["// One <template> element per template object, created on first use (view/01-templates.md\n// \"Instantiation\"). The browser's own parser builds it from the template HTML; the runtime\n// preparer then checks the parse (rule 7). Server templates never get one (rule 11).\nimport { verify } from '#prepare';\nimport { DEV } from '#view-dev';\nimport { SERVER_ONLY, TemplateError } from './normalize/errors.js';\nimport type { TemplateObject } from './normalize/types.js';\n\nconst elements = new WeakMap<TemplateObject, HTMLTemplateElement>();\n\n/** The cached <template> element for `template`, parsed (and checked) on first use. */\nexport function templateElement(template: TemplateObject): HTMLTemplateElement {\n const cached = elements.get(template);\n if (cached !== undefined) return cached;\n if (template.server) {\n // Production keeps a short message: the class and its explanation stay out of the bundle.\n throw DEV\n ? new TemplateError(11, SERVER_ONLY, template.loc)\n : new Error('gyral: template rule 11: a page shell rendered in the browser.');\n }\n const el = document.createElement('template');\n el.innerHTML = template.html;\n verify(template, el);\n elements.set(template, el);\n return el;\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TemplateObject } from './normalize/types.js';
|
|
2
|
+
declare const SOURCE: unique symbol;
|
|
3
|
+
/** What `html` returns: a template plus this render's values. */
|
|
4
|
+
export interface TemplateResult {
|
|
5
|
+
readonly [SOURCE]: readonly string[] | TemplateObject;
|
|
6
|
+
readonly values: readonly unknown[];
|
|
7
|
+
}
|
|
8
|
+
/** The template tag: `` html`<p>${s.text}</p>` ``. */
|
|
9
|
+
export declare function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;
|
|
10
|
+
/** Internal: what the template compiler (Phase 6) rewrites `html` calls into. */
|
|
11
|
+
export declare function compiled(template: TemplateObject, values: readonly unknown[]): TemplateResult;
|
|
12
|
+
export declare function isTemplateResult(value: unknown): value is TemplateResult;
|
|
13
|
+
/** Internal: a result's source, compared by identity to skip `templateOf` on updates. */
|
|
14
|
+
export declare const sourceOf: (result: TemplateResult) => readonly string[] | TemplateObject;
|
|
15
|
+
/**
|
|
16
|
+
* The template object of a result: compiled results carry it; a runtime result's call site is
|
|
17
|
+
* normalized once by `#prepare` (cached there, so compiled builds carry neither the preparer
|
|
18
|
+
* nor its cache).
|
|
19
|
+
*/
|
|
20
|
+
export declare function templateOf(result: TemplateResult): TemplateObject;
|
|
21
|
+
export {};
|