@microsoft/webui-framework 0.0.28 → 0.0.29
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/README.md +18 -0
- package/dist/element/style-catalog.d.ts +33 -0
- package/dist/element/style-catalog.js +154 -0
- package/dist/element/styles.d.ts +2 -29
- package/dist/element/styles.js +4 -157
- package/dist/lifecycle.js +4 -0
- package/dist/static-host.js +2 -1
- package/dist/streaming-bootstrap.js +2 -2
- package/dist/streaming-deferred.js +44 -15
- package/dist/streaming-install.js +3 -2
- package/dist/streaming-mode.d.ts +2 -1
- package/dist/streaming-mode.js +4 -1
- package/dist/streaming-template-hosts.d.ts +5 -0
- package/dist/streaming-template-hosts.js +39 -0
- package/dist/template-content.d.ts +1 -0
- package/dist/template-content.js +6 -2
- package/dist/template-element.js +43 -41
- package/dist/template-registry.d.ts +29 -0
- package/dist/template-registry.js +228 -0
- package/dist/template.d.ts +1 -25
- package/dist/template.js +4 -206
- package/dist/trusted-types-policy.d.ts +4 -0
- package/dist/trusted-types-policy.js +62 -0
- package/package.json +5 -5
package/dist/streaming-mode.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
let cached;
|
|
2
2
|
export const STREAMING_BOUNDARY_ACTIVATE = Symbol.for('microsoft.webui.boundaryActivate');
|
|
3
|
-
export
|
|
3
|
+
export let resumeStreamingRoot;
|
|
4
|
+
export function registerStreamingRootResume(resume) {
|
|
5
|
+
resumeStreamingRoot = resume;
|
|
6
|
+
}
|
|
4
7
|
export const STREAMED_HOST_ATTR = 'data-ws';
|
|
5
8
|
export const ACTIVATION_ACTIVATED = 1;
|
|
6
9
|
export const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TemplateMeta } from './template-types.js';
|
|
2
|
+
export declare function loadTemplateHostRuntime(): Promise<void> | undefined;
|
|
3
|
+
export declare function prepareTemplateDefinitions(templates: Record<string, TemplateMeta>, names: readonly string[]): Promise<void> | undefined;
|
|
4
|
+
export declare function invalidateTemplateHostRuntime(): void;
|
|
5
|
+
export declare function resetTemplateHostRuntimeForTests(): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { templateNeedsStaticHost } from './template-roots.js';
|
|
2
|
+
let generation = 0;
|
|
3
|
+
let installed = false;
|
|
4
|
+
let pending;
|
|
5
|
+
export function loadTemplateHostRuntime() {
|
|
6
|
+
if (installed)
|
|
7
|
+
return;
|
|
8
|
+
if (!pending) {
|
|
9
|
+
const current = generation;
|
|
10
|
+
pending = import('./static-host.js').then(({ installTemplateElementRuntime }) => {
|
|
11
|
+
if (current !== generation) {
|
|
12
|
+
throw new Error('[WebUI] Template-host activation was abandoned.');
|
|
13
|
+
}
|
|
14
|
+
installTemplateElementRuntime();
|
|
15
|
+
installed = true;
|
|
16
|
+
pending = undefined;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return pending;
|
|
20
|
+
}
|
|
21
|
+
export function prepareTemplateDefinitions(templates, names) {
|
|
22
|
+
const exclusions = window.__webui?.templateHostExclusions;
|
|
23
|
+
for (let i = 0; i < names.length; i++) {
|
|
24
|
+
const tag = names[i];
|
|
25
|
+
if (templateNeedsStaticHost(templates[tag]) &&
|
|
26
|
+
!exclusions?.has(tag) &&
|
|
27
|
+
!customElements.get(tag)) {
|
|
28
|
+
return loadTemplateHostRuntime();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function invalidateTemplateHostRuntime() {
|
|
33
|
+
generation++;
|
|
34
|
+
pending = undefined;
|
|
35
|
+
}
|
|
36
|
+
export function resetTemplateHostRuntimeForTests() {
|
|
37
|
+
invalidateTemplateHostRuntime();
|
|
38
|
+
installed = false;
|
|
39
|
+
}
|
|
@@ -13,5 +13,6 @@ export interface TemplateStylesheetDescriptor {
|
|
|
13
13
|
readonly type: string;
|
|
14
14
|
}
|
|
15
15
|
export declare function cloneTemplateContent(meta: TemplateBlockMeta): DocumentFragment;
|
|
16
|
+
export declare function getTemplateFragment(meta: TemplateBlockMeta): DocumentFragment;
|
|
16
17
|
export declare function getTemplateStylesheets(meta: TemplateBlockMeta): readonly TemplateStylesheetDescriptor[] | undefined;
|
|
17
18
|
export declare function templateHtmlMayContainLink(html: string): boolean;
|
package/dist/template-content.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { setTemplateContent } from './trusted-types-policy.js';
|
|
1
2
|
const EMPTY_STYLESHEETS = Object.freeze([]);
|
|
2
3
|
const NO_LINK_STYLESHEETS = Object.freeze([]);
|
|
3
4
|
const templateContentCache = new WeakMap();
|
|
4
5
|
export function cloneTemplateContent(meta) {
|
|
5
|
-
return
|
|
6
|
+
return getTemplateFragment(meta).cloneNode(true);
|
|
7
|
+
}
|
|
8
|
+
export function getTemplateFragment(meta) {
|
|
9
|
+
return getTemplateContent(meta).fragment;
|
|
6
10
|
}
|
|
7
11
|
export function getTemplateStylesheets(meta) {
|
|
8
12
|
const stylesheets = getTemplateContent(meta, true).stylesheets;
|
|
@@ -37,7 +41,7 @@ function getTemplateContent(meta, mayContainLink) {
|
|
|
37
41
|
if (cached)
|
|
38
42
|
return cached;
|
|
39
43
|
const template = document.createElement('template');
|
|
40
|
-
template
|
|
44
|
+
setTemplateContent(template, meta);
|
|
41
45
|
const fragment = template.content;
|
|
42
46
|
const stylesheets = (mayContainLink ?? templateHtmlMayContainLink(meta.h))
|
|
43
47
|
? collectStylesheetDescriptors(fragment)
|
package/dist/template-element.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deferTemplateDefinition, getTemplate } from './template.js';
|
|
2
|
-
import { cloneTemplateContent, } from './template-content.js';
|
|
2
|
+
import { cloneTemplateContent, getTemplateFragment, } from './template-content.js';
|
|
3
3
|
import { hydrationStart, hydrationEnd } from './lifecycle.js';
|
|
4
|
-
import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode,
|
|
4
|
+
import { ACTIVATION_ACTIVATED, ACTIVATION_ANCESTOR_BARRIER, ACTIVATION_MISSING_TEMPLATE, ACTIVATION_STATIC_HOST_OPT_OUT, isStreamingHydrationMode, resumeStreamingRoot, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
|
|
5
5
|
import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
|
|
6
6
|
import { collectItemMarkers, nextElement, findByOrdinal, buildSSRIndex, collectTemplateElements, MARKER_COND_START, MARKER_COND_END, rawMarker, } from './element/markers.js';
|
|
7
7
|
import { claimSsrComponentStyles, installComponentStyles, isComponentStyleMarker, } from './element/styles.js';
|
|
@@ -10,7 +10,6 @@ import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLive
|
|
|
10
10
|
import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
|
|
11
11
|
const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
|
|
12
12
|
const rootTagCache = new WeakMap();
|
|
13
|
-
const templateDOMCache = new WeakMap();
|
|
14
13
|
const tplOrdinalCache = new WeakMap();
|
|
15
14
|
const textMarkerBoundaryCache = new WeakMap();
|
|
16
15
|
const NO_TEXT_MARKER_BOUNDARIES = new Int32Array(0);
|
|
@@ -149,15 +148,6 @@ function childNodesArray(parent) {
|
|
|
149
148
|
result[i] = children[i];
|
|
150
149
|
return result;
|
|
151
150
|
}
|
|
152
|
-
function getTemplateDom(meta) {
|
|
153
|
-
let cached = templateDOMCache.get(meta);
|
|
154
|
-
if (cached)
|
|
155
|
-
return cached;
|
|
156
|
-
const div = document.createElement('div');
|
|
157
|
-
div.innerHTML = meta.h;
|
|
158
|
-
templateDOMCache.set(meta, div);
|
|
159
|
-
return div;
|
|
160
|
-
}
|
|
161
151
|
function installTemplateObservedAttributes(ctor, tagName, meta = getTemplate(tagName)) {
|
|
162
152
|
if (!meta)
|
|
163
153
|
return;
|
|
@@ -295,11 +285,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
295
285
|
this.$hasAncestorBoundaryState = undefined;
|
|
296
286
|
}
|
|
297
287
|
$resumeRetainedRoot() {
|
|
298
|
-
|
|
299
|
-
if (typeof resume !== 'function')
|
|
300
|
-
return false;
|
|
301
|
-
resume.call(this);
|
|
302
|
-
return true;
|
|
288
|
+
return resumeStreamingRoot?.(this) ?? false;
|
|
303
289
|
}
|
|
304
290
|
static define(tagName) {
|
|
305
291
|
const ctor = this;
|
|
@@ -448,7 +434,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
448
434
|
this.$applyPendingParentState(isSSR);
|
|
449
435
|
}
|
|
450
436
|
if (isSSR) {
|
|
451
|
-
this.$root = this.$hydrate(root, meta,
|
|
437
|
+
this.$root = this.$hydrate(root, meta, getTemplateFragment(meta));
|
|
452
438
|
}
|
|
453
439
|
else {
|
|
454
440
|
clientRoot = this.$createStagingRoot(meta);
|
|
@@ -1054,31 +1040,47 @@ export class TemplateElement extends HTMLElement {
|
|
|
1054
1040
|
this.$pendingFlush = false;
|
|
1055
1041
|
return;
|
|
1056
1042
|
}
|
|
1057
|
-
|
|
1058
|
-
this.$
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1043
|
+
try {
|
|
1044
|
+
if (!this.$pathIndex)
|
|
1045
|
+
this.$buildPathIndex();
|
|
1046
|
+
if (!this.$pathIndex)
|
|
1047
|
+
return;
|
|
1048
|
+
while (this.$dirtyPaths && this.$dirtyPaths.size > 0) {
|
|
1049
|
+
const dirty = this.$dirtyPaths;
|
|
1050
|
+
this.$dirtyPaths = null;
|
|
1051
|
+
const paths = dirty.values();
|
|
1052
|
+
try {
|
|
1053
|
+
for (const path of paths) {
|
|
1054
|
+
if (!this.$pathIndex)
|
|
1055
|
+
this.$buildPathIndex();
|
|
1056
|
+
const entry = this.$pathIndex?.get(path);
|
|
1057
|
+
if (entry) {
|
|
1058
|
+
this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats, requireKnownState);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
for (const path of paths)
|
|
1064
|
+
(this.$dirtyPaths ??= new Set()).add(path);
|
|
1065
|
+
throw error;
|
|
1066
|
+
}
|
|
1065
1067
|
if (!this.$pathIndex)
|
|
1066
1068
|
this.$buildPathIndex();
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
this.$updateBindings(
|
|
1069
|
+
if (this.$wildcardBindings) {
|
|
1070
|
+
const wc = this.$wildcardBindings;
|
|
1071
|
+
this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats, requireKnownState);
|
|
1070
1072
|
}
|
|
1073
|
+
if (!this.$pathIndex)
|
|
1074
|
+
this.$buildPathIndex();
|
|
1071
1075
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
this.$
|
|
1076
|
+
}
|
|
1077
|
+
finally {
|
|
1078
|
+
this.$pendingFlush = false;
|
|
1079
|
+
if (this.$dirtyPaths?.size) {
|
|
1080
|
+
this.$pendingFlush = true;
|
|
1081
|
+
queueMicrotask(() => this.$flush());
|
|
1077
1082
|
}
|
|
1078
|
-
if (!this.$pathIndex)
|
|
1079
|
-
this.$buildPathIndex();
|
|
1080
1083
|
}
|
|
1081
|
-
this.$pendingFlush = false;
|
|
1082
1084
|
}
|
|
1083
1085
|
$recordPreReadyWrite(path) {
|
|
1084
1086
|
if (!this.$preReadyWrites)
|
|
@@ -1391,7 +1393,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1391
1393
|
const marker = repMarkers ? repMarkers[i] : null;
|
|
1392
1394
|
const ssrParent = (marker ? marker.parentNode : ssrElements[parentIndex]) ?? ssrRoot;
|
|
1393
1395
|
const blockMeta = this.$block(blockIndex);
|
|
1394
|
-
const blockTplDom = blockMeta ?
|
|
1396
|
+
const blockTplDom = blockMeta ? getTemplateFragment(blockMeta) : null;
|
|
1395
1397
|
const rootTag = blockMeta
|
|
1396
1398
|
&& blockTplDom?.childNodes.length === 1
|
|
1397
1399
|
&& blockTplDom.children.length === 1
|
|
@@ -1540,7 +1542,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1540
1542
|
}
|
|
1541
1543
|
$hydrateCondContent(condAnchor, blockMeta, scope) {
|
|
1542
1544
|
const rootTag = this.$rootTag(blockMeta);
|
|
1543
|
-
const tplDom =
|
|
1545
|
+
const tplDom = getTemplateFragment(blockMeta);
|
|
1544
1546
|
if (rootTag && tplDom.children.length === 1 && !this.$hasRootStructuralSlot(blockMeta)) {
|
|
1545
1547
|
const el = nextElement(condAnchor);
|
|
1546
1548
|
if (el) {
|
|
@@ -1871,11 +1873,11 @@ export class TemplateElement extends HTMLElement {
|
|
|
1871
1873
|
const range = document.createRange();
|
|
1872
1874
|
range.setStartAfter(b.node);
|
|
1873
1875
|
range.setEndBefore(b.rawEnd);
|
|
1876
|
+
const fragment = val !== '' ? range.createContextualFragment(val) : undefined;
|
|
1874
1877
|
range.deleteContents();
|
|
1875
1878
|
let rawNodes;
|
|
1876
1879
|
const rawOwner = b.rawOwner;
|
|
1877
|
-
if (
|
|
1878
|
-
const fragment = range.createContextualFragment(val);
|
|
1880
|
+
if (fragment) {
|
|
1879
1881
|
if (rawOwner && b.node.parentNode === rawOwner.container) {
|
|
1880
1882
|
rawNodes = childNodesArray(fragment);
|
|
1881
1883
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type { CompiledAttrGroupMeta, CompiledAttrMeta, CompiledAttrPart, CompiledCondition, CompiledConditionFn, CompiledConditionalMeta, CompiledEventArg, CompiledEventArgs, CompiledEventBindingMeta, CompiledEventGroupMeta, SerializedCompiledCondition, TemplateCondition, CompiledTextRunMeta, TemplateBlockMeta, TemplateMeta, TemplateNodeIndex, TemplateSlot, } from './template-types.js';
|
|
2
|
+
import { type ComponentStyles } from './element/style-catalog.js';
|
|
3
|
+
import type { CompiledConditionFn, TemplateMeta } from './template-types.js';
|
|
4
|
+
declare let prepareResources: ((templates: Record<string, TemplateMeta>) => Promise<void> | undefined) | undefined;
|
|
5
|
+
type DefinitionPreparation = (templates: Record<string, TemplateMeta>, names: readonly string[]) => Promise<void> | undefined;
|
|
6
|
+
export declare function installTemplateResourcePreparation(prepare: NonNullable<typeof prepareResources>): void;
|
|
7
|
+
export declare function installTemplateDefinitionPreparation(prepare: DefinitionPreparation | null): void;
|
|
8
|
+
declare global {
|
|
9
|
+
interface WebUIRuntimeGlobal {
|
|
10
|
+
state?: Record<string, unknown>;
|
|
11
|
+
templates?: Record<string, TemplateMeta>;
|
|
12
|
+
templateFns?: Record<string, CompiledConditionFn[]>;
|
|
13
|
+
componentAssetStyles?: Record<string, readonly string[]>;
|
|
14
|
+
templateHostExclusions?: Set<string>;
|
|
15
|
+
componentStyles?: ComponentStyles;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
interface Window {
|
|
19
|
+
__webui?: WebUIRuntimeGlobal;
|
|
20
|
+
__webuiRegisterComponentStyles?: (value: unknown) => Promise<void> | undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export declare function getTemplate(name: string): TemplateMeta | undefined;
|
|
24
|
+
export declare function getTemplateRegistry(): Record<string, TemplateMeta> | undefined;
|
|
25
|
+
export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>, componentStylesValue?: ComponentStyles | unknown): void;
|
|
26
|
+
export declare function prepareAssetTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>): void;
|
|
27
|
+
export declare function deferTemplateDefinition(tagName: string, ctor: Function, define: () => void): void;
|
|
28
|
+
export declare function releaseSSRBootstrapState(): void;
|
|
29
|
+
export declare function releaseNonRoutedSSRBootstrapState(): void;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { dispatchTemplatesRegistered, templateRegistrationDetail, TEMPLATES_REGISTERED_EVENT, } from './template-events.js';
|
|
2
|
+
import { prepareComponentStyles, registerComponentStyles, registerPreparedComponentStyles, validateComponentStylesRegistration, } from './element/style-catalog.js';
|
|
3
|
+
import { registerTrustedTemplateBlock } from './trusted-types-policy.js';
|
|
4
|
+
const WEBUI_DATA_ID = 'webui-data';
|
|
5
|
+
const HYDRATION_COMPLETE_EVENT = 'webui:hydration-complete';
|
|
6
|
+
const TEMPLATE_FN_COUNT = Symbol.for('microsoft.webui.templateFnCount');
|
|
7
|
+
const normalizedTemplates = new WeakSet();
|
|
8
|
+
const assetNormalizedTemplates = new WeakSet();
|
|
9
|
+
let webuiDataLoaded = false;
|
|
10
|
+
function runtimeTemplateFnCount(registry) {
|
|
11
|
+
const count = registry[TEMPLATE_FN_COUNT];
|
|
12
|
+
if (typeof count === 'number')
|
|
13
|
+
return count;
|
|
14
|
+
const initialized = Object.keys(registry).length;
|
|
15
|
+
registry[TEMPLATE_FN_COUNT] = initialized;
|
|
16
|
+
return initialized;
|
|
17
|
+
}
|
|
18
|
+
const pendingTemplateDefinitions = new Map();
|
|
19
|
+
let prepareResources;
|
|
20
|
+
let prepareDefinitions;
|
|
21
|
+
export function installTemplateResourcePreparation(prepare) {
|
|
22
|
+
prepareResources = prepare;
|
|
23
|
+
}
|
|
24
|
+
export function installTemplateDefinitionPreparation(prepare) {
|
|
25
|
+
if (prepareDefinitions !== null)
|
|
26
|
+
prepareDefinitions = prepare;
|
|
27
|
+
}
|
|
28
|
+
function acceptTemplateData(templates, templateFns, componentStyles) {
|
|
29
|
+
validateComponentStylesRegistration(componentStyles);
|
|
30
|
+
prepareTemplateData(templates, templateFns);
|
|
31
|
+
if (componentStyles) {
|
|
32
|
+
registerPreparedComponentStyles(componentStyles);
|
|
33
|
+
}
|
|
34
|
+
const w = window;
|
|
35
|
+
if (!w.__webui)
|
|
36
|
+
w.__webui = {};
|
|
37
|
+
if (!w.__webui.templates)
|
|
38
|
+
w.__webui.templates = {};
|
|
39
|
+
const names = Object.keys(templates);
|
|
40
|
+
for (let i = 0; i < names.length; i++) {
|
|
41
|
+
const tag = names[i];
|
|
42
|
+
w.__webui.templates[tag] = templates[tag];
|
|
43
|
+
}
|
|
44
|
+
for (let i = 0; i < names.length; i++) {
|
|
45
|
+
const pending = pendingTemplateDefinitions.get(names[i]);
|
|
46
|
+
if (!pending)
|
|
47
|
+
continue;
|
|
48
|
+
pendingTemplateDefinitions.delete(names[i]);
|
|
49
|
+
pending.define();
|
|
50
|
+
}
|
|
51
|
+
return names;
|
|
52
|
+
}
|
|
53
|
+
if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
|
|
54
|
+
if (window.__webui?.componentStyles) {
|
|
55
|
+
registerComponentStyles(window.__webui.componentStyles);
|
|
56
|
+
}
|
|
57
|
+
window.addEventListener(TEMPLATES_REGISTERED_EVENT, (event) => {
|
|
58
|
+
const detail = templateRegistrationDetail(event);
|
|
59
|
+
if (!detail?.templates)
|
|
60
|
+
return;
|
|
61
|
+
const styles = detail.componentStyles === undefined
|
|
62
|
+
? undefined
|
|
63
|
+
: prepareComponentStyles(detail.componentStyles);
|
|
64
|
+
const templates = detail.templates;
|
|
65
|
+
const names = acceptTemplateData(templates, undefined, styles);
|
|
66
|
+
const definitions = detail.waitUntil
|
|
67
|
+
? prepareDefinitions?.(templates, names)
|
|
68
|
+
: undefined;
|
|
69
|
+
if (definitions) {
|
|
70
|
+
detail.waitUntil?.(definitions.then(() => prepareResources?.(templates)));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const ready = prepareResources?.(templates);
|
|
74
|
+
if (ready)
|
|
75
|
+
detail.waitUntil?.(ready);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
window.addEventListener(HYDRATION_COMPLETE_EVENT, releaseNonRoutedSSRBootstrapState, { once: true });
|
|
79
|
+
}
|
|
80
|
+
export function getTemplate(name) {
|
|
81
|
+
let meta = window.__webui?.templates?.[name];
|
|
82
|
+
if (!meta) {
|
|
83
|
+
loadWebUIDataBlock();
|
|
84
|
+
meta = window.__webui?.templates?.[name];
|
|
85
|
+
}
|
|
86
|
+
if (meta)
|
|
87
|
+
normalizeTemplate(name, meta);
|
|
88
|
+
return meta;
|
|
89
|
+
}
|
|
90
|
+
export function getTemplateRegistry() {
|
|
91
|
+
loadWebUIDataBlock();
|
|
92
|
+
return window.__webui?.templates;
|
|
93
|
+
}
|
|
94
|
+
export function registerTemplateData(templates, templateFns, componentStylesValue) {
|
|
95
|
+
const componentStyles = prepareComponentStyles(componentStylesValue);
|
|
96
|
+
if (acceptTemplateData(templates, templateFns, componentStyles).length > 0) {
|
|
97
|
+
dispatchTemplatesRegistered(templates);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function prepareTemplateData(templates, templateFns) {
|
|
101
|
+
const names = Object.keys(templates);
|
|
102
|
+
for (let i = 0; i < names.length; i++) {
|
|
103
|
+
const name = names[i];
|
|
104
|
+
normalizeTemplate(name, templates[name], templateFns?.[name]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function prepareAssetTemplateData(templates, templateFns) {
|
|
108
|
+
const names = Object.keys(templates);
|
|
109
|
+
for (let i = 0; i < names.length; i++) {
|
|
110
|
+
const name = names[i];
|
|
111
|
+
const meta = templates[name];
|
|
112
|
+
if (assetNormalizedTemplates.has(meta))
|
|
113
|
+
continue;
|
|
114
|
+
normalizeTemplateConditions(name, meta, templateFns?.[name] ?? []);
|
|
115
|
+
normalizedTemplates.add(meta);
|
|
116
|
+
assetNormalizedTemplates.add(meta);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function deferTemplateDefinition(tagName, ctor, define) {
|
|
120
|
+
const pending = pendingTemplateDefinitions.get(tagName);
|
|
121
|
+
if (pending) {
|
|
122
|
+
if (pending.ctor === ctor) {
|
|
123
|
+
throw new Error(`[WebUI] <${tagName}> is already pending definition.`);
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`[WebUI] <${tagName}> already has a pending authored definition.`);
|
|
126
|
+
}
|
|
127
|
+
pendingTemplateDefinitions.set(tagName, { ctor, define });
|
|
128
|
+
}
|
|
129
|
+
function loadWebUIDataBlock() {
|
|
130
|
+
if (webuiDataLoaded || window.__webui?.state !== undefined || typeof document === 'undefined')
|
|
131
|
+
return;
|
|
132
|
+
const el = document.getElementById(WEBUI_DATA_ID);
|
|
133
|
+
if (!el) {
|
|
134
|
+
webuiDataLoaded = true;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const text = el.textContent;
|
|
138
|
+
if (text) {
|
|
139
|
+
const templateFns = window.__webui?.templateFns;
|
|
140
|
+
const componentAssetStyles = window.__webui?.componentAssetStyles;
|
|
141
|
+
const parsed = JSON.parse(text);
|
|
142
|
+
if (templateFns)
|
|
143
|
+
parsed.templateFns = templateFns;
|
|
144
|
+
if (componentAssetStyles)
|
|
145
|
+
parsed.componentAssetStyles = componentAssetStyles;
|
|
146
|
+
window.__webui = parsed;
|
|
147
|
+
el.remove();
|
|
148
|
+
webuiDataLoaded = true;
|
|
149
|
+
if (parsed.componentStyles !== undefined) {
|
|
150
|
+
registerComponentStyles(parsed.componentStyles);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
el.remove();
|
|
155
|
+
webuiDataLoaded = true;
|
|
156
|
+
}
|
|
157
|
+
export function releaseSSRBootstrapState() {
|
|
158
|
+
const runtime = window.__webui;
|
|
159
|
+
if (runtime?.state !== undefined) {
|
|
160
|
+
delete runtime.state;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export function releaseNonRoutedSSRBootstrapState() {
|
|
164
|
+
const runtime = window.__webui;
|
|
165
|
+
if (runtime?.chain === undefined &&
|
|
166
|
+
runtime?.templateHostExclusions === undefined) {
|
|
167
|
+
releaseSSRBootstrapState();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function normalizeTemplate(name, meta, suppliedFns) {
|
|
171
|
+
if (normalizedTemplates.has(meta))
|
|
172
|
+
return;
|
|
173
|
+
const runtimeFns = window.__webui?.templateFns;
|
|
174
|
+
const fns = suppliedFns ?? runtimeFns?.[name] ?? [];
|
|
175
|
+
normalizeTemplateConditions(name, meta, fns);
|
|
176
|
+
normalizedTemplates.add(meta);
|
|
177
|
+
if (suppliedFns === undefined && runtimeFns?.[name] === fns) {
|
|
178
|
+
let remaining = runtimeTemplateFnCount(runtimeFns);
|
|
179
|
+
delete runtimeFns[name];
|
|
180
|
+
remaining--;
|
|
181
|
+
runtimeFns[TEMPLATE_FN_COUNT] = remaining;
|
|
182
|
+
if (remaining === 0 && window.__webui) {
|
|
183
|
+
const liveCount = Object.keys(runtimeFns).length;
|
|
184
|
+
if (liveCount === 0) {
|
|
185
|
+
delete window.__webui.templateFns;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
runtimeFns[TEMPLATE_FN_COUNT] = liveCount;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function normalizeTemplateConditions(name, meta, fns) {
|
|
194
|
+
const stack = [meta];
|
|
195
|
+
while (stack.length > 0) {
|
|
196
|
+
const block = stack.pop();
|
|
197
|
+
if (!block)
|
|
198
|
+
continue;
|
|
199
|
+
registerTrustedTemplateBlock(block);
|
|
200
|
+
if (block.a) {
|
|
201
|
+
for (let i = 0; i < block.a.length; i++) {
|
|
202
|
+
const attr = block.a[i];
|
|
203
|
+
if (attr[1] === 2)
|
|
204
|
+
normalizeCondition(name, attr[2], fns);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (block.c) {
|
|
208
|
+
for (let i = 0; i < block.c.length; i++) {
|
|
209
|
+
normalizeCondition(name, block.c[i][0], fns);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const children = block.b;
|
|
213
|
+
if (children) {
|
|
214
|
+
for (let i = 0; i < children.length; i++)
|
|
215
|
+
stack.push(children[i]);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function normalizeCondition(tagName, condition, fns) {
|
|
220
|
+
const first = condition[0];
|
|
221
|
+
if (typeof first === 'function')
|
|
222
|
+
return;
|
|
223
|
+
const fn = fns[first];
|
|
224
|
+
if (typeof fn !== 'function') {
|
|
225
|
+
throw new Error(`[WebUI] Missing condition closure ${first} for <${tagName}>.`);
|
|
226
|
+
}
|
|
227
|
+
condition[0] = fn;
|
|
228
|
+
}
|
package/dist/template.d.ts
CHANGED
|
@@ -1,25 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
import { type ComponentStyles } from './element/styles.js';
|
|
3
|
-
import type { CompiledConditionFn, TemplateMeta } from './template-types.js';
|
|
4
|
-
declare global {
|
|
5
|
-
interface WebUIRuntimeGlobal {
|
|
6
|
-
state?: Record<string, unknown>;
|
|
7
|
-
templates?: Record<string, TemplateMeta>;
|
|
8
|
-
templateFns?: Record<string, CompiledConditionFn[]>;
|
|
9
|
-
componentAssetStyles?: Record<string, readonly string[]>;
|
|
10
|
-
templateHostExclusions?: Set<string>;
|
|
11
|
-
componentStyles?: ComponentStyles;
|
|
12
|
-
[key: string]: unknown;
|
|
13
|
-
}
|
|
14
|
-
interface Window {
|
|
15
|
-
__webui?: WebUIRuntimeGlobal;
|
|
16
|
-
__webuiRegisterComponentStyles?: (value: unknown) => Promise<void> | undefined;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
export declare function getTemplate(name: string): TemplateMeta | undefined;
|
|
20
|
-
export declare function getTemplateRegistry(): Record<string, TemplateMeta> | undefined;
|
|
21
|
-
export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>, componentStylesValue?: ComponentStyles | unknown): void;
|
|
22
|
-
export declare function prepareAssetTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>): void;
|
|
23
|
-
export declare function deferTemplateDefinition(tagName: string, ctor: Function, define: () => void): void;
|
|
24
|
-
export declare function releaseSSRBootstrapState(): void;
|
|
25
|
-
export declare function releaseNonRoutedSSRBootstrapState(): void;
|
|
1
|
+
export * from './template-registry.js';
|