@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/dist/template.js CHANGED
@@ -1,215 +1,13 @@
1
- import { dispatchTemplatesRegistered, templateRegistrationDetail, TEMPLATES_REGISTERED_EVENT, } from './template-events.js';
2
- import { prepareComponentStyles, registerComponentStyles, registerPreparedComponentStyles, requireComponentStyles, validateComponentStylesRegistration, } from './element/styles.js';
1
+ export * from './template-registry.js';
2
+ import { installTemplateResourcePreparation } from './template-registry.js';
3
+ import { registerPreparedComponentStyles, requireComponentStyles, validateComponentStylesRegistration, } from './element/style-catalog.js';
3
4
  import { prepareComponentStyleLinks, prepareRegisteredLinkStyles, } from './element/link-styles.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
- function acceptTemplateData(templates, templateFns, componentStyles) {
20
- validateComponentStylesRegistration(componentStyles);
21
- prepareTemplateData(templates, templateFns);
22
- if (componentStyles) {
23
- registerPreparedComponentStyles(componentStyles);
24
- }
25
- const w = window;
26
- if (!w.__webui)
27
- w.__webui = {};
28
- if (!w.__webui.templates)
29
- w.__webui.templates = {};
30
- const names = Object.keys(templates);
31
- for (let i = 0; i < names.length; i++) {
32
- const tag = names[i];
33
- w.__webui.templates[tag] = templates[tag];
34
- }
35
- for (let i = 0; i < names.length; i++) {
36
- const pending = pendingTemplateDefinitions.get(names[i]);
37
- if (!pending)
38
- continue;
39
- pendingTemplateDefinitions.delete(names[i]);
40
- pending.define();
41
- }
42
- return names.length > 0;
43
- }
44
5
  if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
6
+ installTemplateResourcePreparation(prepareRegisteredLinkStyles);
45
7
  window.__webuiRegisterComponentStyles = (value) => {
46
8
  const styles = requireComponentStyles(value);
47
9
  validateComponentStylesRegistration(styles);
48
10
  registerPreparedComponentStyles(styles);
49
11
  return prepareComponentStyleLinks(styles);
50
12
  };
51
- if (window.__webui?.componentStyles) {
52
- registerComponentStyles(window.__webui.componentStyles);
53
- }
54
- window.addEventListener(TEMPLATES_REGISTERED_EVENT, (event) => {
55
- const detail = templateRegistrationDetail(event);
56
- if (!detail?.templates)
57
- return;
58
- const styles = detail.componentStyles === undefined
59
- ? undefined
60
- : prepareComponentStyles(detail.componentStyles);
61
- acceptTemplateData(detail.templates, undefined, styles);
62
- const ready = prepareRegisteredLinkStyles(detail.templates);
63
- if (ready)
64
- detail.waitUntil?.(ready);
65
- });
66
- window.addEventListener(HYDRATION_COMPLETE_EVENT, releaseNonRoutedSSRBootstrapState, { once: true });
67
- }
68
- export function getTemplate(name) {
69
- let meta = window.__webui?.templates?.[name];
70
- if (!meta) {
71
- loadWebUIDataBlock();
72
- meta = window.__webui?.templates?.[name];
73
- }
74
- if (meta)
75
- normalizeTemplate(name, meta);
76
- return meta;
77
- }
78
- export function getTemplateRegistry() {
79
- loadWebUIDataBlock();
80
- return window.__webui?.templates;
81
- }
82
- export function registerTemplateData(templates, templateFns, componentStylesValue) {
83
- const componentStyles = prepareComponentStyles(componentStylesValue);
84
- if (acceptTemplateData(templates, templateFns, componentStyles)) {
85
- dispatchTemplatesRegistered(templates);
86
- }
87
- }
88
- function prepareTemplateData(templates, templateFns) {
89
- const names = Object.keys(templates);
90
- for (let i = 0; i < names.length; i++) {
91
- const name = names[i];
92
- normalizeTemplate(name, templates[name], templateFns?.[name]);
93
- }
94
- }
95
- export function prepareAssetTemplateData(templates, templateFns) {
96
- const names = Object.keys(templates);
97
- for (let i = 0; i < names.length; i++) {
98
- const name = names[i];
99
- const meta = templates[name];
100
- if (assetNormalizedTemplates.has(meta))
101
- continue;
102
- normalizeTemplateConditions(name, meta, templateFns?.[name] ?? []);
103
- normalizedTemplates.add(meta);
104
- assetNormalizedTemplates.add(meta);
105
- }
106
- }
107
- export function deferTemplateDefinition(tagName, ctor, define) {
108
- const pending = pendingTemplateDefinitions.get(tagName);
109
- if (pending) {
110
- if (pending.ctor === ctor) {
111
- throw new Error(`[WebUI] <${tagName}> is already pending definition.`);
112
- }
113
- throw new Error(`[WebUI] <${tagName}> already has a pending authored definition.`);
114
- }
115
- pendingTemplateDefinitions.set(tagName, { ctor, define });
116
- }
117
- function loadWebUIDataBlock() {
118
- if (webuiDataLoaded || window.__webui?.state !== undefined || typeof document === 'undefined')
119
- return;
120
- const el = document.getElementById(WEBUI_DATA_ID);
121
- if (!el) {
122
- webuiDataLoaded = true;
123
- return;
124
- }
125
- const text = el.textContent;
126
- if (text) {
127
- const templateFns = window.__webui?.templateFns;
128
- const componentAssetStyles = window.__webui?.componentAssetStyles;
129
- const parsed = JSON.parse(text);
130
- if (templateFns)
131
- parsed.templateFns = templateFns;
132
- if (componentAssetStyles)
133
- parsed.componentAssetStyles = componentAssetStyles;
134
- window.__webui = parsed;
135
- el.remove();
136
- webuiDataLoaded = true;
137
- if (parsed.componentStyles !== undefined) {
138
- registerComponentStyles(parsed.componentStyles);
139
- }
140
- return;
141
- }
142
- el.remove();
143
- webuiDataLoaded = true;
144
- }
145
- export function releaseSSRBootstrapState() {
146
- const runtime = window.__webui;
147
- if (runtime?.state !== undefined) {
148
- delete runtime.state;
149
- }
150
- }
151
- export function releaseNonRoutedSSRBootstrapState() {
152
- const runtime = window.__webui;
153
- if (runtime?.chain === undefined &&
154
- runtime?.templateHostExclusions === undefined) {
155
- releaseSSRBootstrapState();
156
- }
157
- }
158
- function normalizeTemplate(name, meta, suppliedFns) {
159
- if (normalizedTemplates.has(meta))
160
- return;
161
- const runtimeFns = window.__webui?.templateFns;
162
- const fns = suppliedFns ?? runtimeFns?.[name] ?? [];
163
- normalizeTemplateConditions(name, meta, fns);
164
- normalizedTemplates.add(meta);
165
- if (suppliedFns === undefined && runtimeFns?.[name] === fns) {
166
- let remaining = runtimeTemplateFnCount(runtimeFns);
167
- delete runtimeFns[name];
168
- remaining--;
169
- runtimeFns[TEMPLATE_FN_COUNT] = remaining;
170
- if (remaining === 0 && window.__webui) {
171
- const liveCount = Object.keys(runtimeFns).length;
172
- if (liveCount === 0) {
173
- delete window.__webui.templateFns;
174
- }
175
- else {
176
- runtimeFns[TEMPLATE_FN_COUNT] = liveCount;
177
- }
178
- }
179
- }
180
- }
181
- function normalizeTemplateConditions(name, meta, fns) {
182
- const stack = [meta];
183
- while (stack.length > 0) {
184
- const block = stack.pop();
185
- if (!block)
186
- continue;
187
- if (block.a) {
188
- for (let i = 0; i < block.a.length; i++) {
189
- const attr = block.a[i];
190
- if (attr[1] === 2)
191
- normalizeCondition(name, attr[2], fns);
192
- }
193
- }
194
- if (block.c) {
195
- for (let i = 0; i < block.c.length; i++) {
196
- normalizeCondition(name, block.c[i][0], fns);
197
- }
198
- }
199
- const children = block.b;
200
- if (children) {
201
- for (let i = 0; i < children.length; i++)
202
- stack.push(children[i]);
203
- }
204
- }
205
- }
206
- function normalizeCondition(tagName, condition, fns) {
207
- const first = condition[0];
208
- if (typeof first === 'function')
209
- return;
210
- const fn = fns[first];
211
- if (typeof fn !== 'function') {
212
- throw new Error(`[WebUI] Missing condition closure ${first} for <${tagName}>.`);
213
- }
214
- condition[0] = fn;
215
13
  }
@@ -0,0 +1,4 @@
1
+ import type { TemplateBlockMeta } from './template-types.js';
2
+ export declare function registerTrustedTemplateBlock(meta: TemplateBlockMeta): void;
3
+ export declare function setTemplateContent(target: HTMLTemplateElement, meta: TemplateBlockMeta): void;
4
+ export declare function setImportMapContent(script: HTMLScriptElement, specifier: string, css: string, view: Window | null): void;
@@ -0,0 +1,62 @@
1
+ let documentPolicies;
2
+ let templateHTML;
3
+ function getDocumentPolicy(view) {
4
+ if (!view?.trustedTypes)
5
+ return undefined;
6
+ const existing = documentPolicies?.get(view);
7
+ if (existing)
8
+ return existing;
9
+ const capability = {};
10
+ const authorize = (input, token) => {
11
+ if (token !== capability) {
12
+ throw new TypeError('[WebUI] Only the compiled-template runtime may use this Trusted Types policy.');
13
+ }
14
+ return input;
15
+ };
16
+ let policy;
17
+ try {
18
+ policy = view.trustedTypes.createPolicy('webui', {
19
+ createHTML: authorize,
20
+ createScript: authorize,
21
+ });
22
+ }
23
+ catch (cause) {
24
+ throw new Error('[WebUI] Cannot create Trusted Types policy "webui". Allow "webui" in CSP, share one framework module, and do not pre-create the policy.', { cause });
25
+ }
26
+ const trust = { policy, capability };
27
+ (documentPolicies ??= new WeakMap()).set(view, trust);
28
+ return trust;
29
+ }
30
+ export function registerTrustedTemplateBlock(meta) {
31
+ const view = window;
32
+ if (!view.trustedTypes)
33
+ return;
34
+ templateHTML ??= new WeakMap();
35
+ if (!templateHTML.has(meta))
36
+ templateHTML.set(meta, meta.h);
37
+ }
38
+ export function setTemplateContent(target, meta) {
39
+ const trust = getDocumentPolicy(window);
40
+ if (!trust) {
41
+ target.innerHTML = meta.h;
42
+ return;
43
+ }
44
+ const html = templateHTML?.get(meta);
45
+ if (html === undefined || html !== meta.h) {
46
+ throw new Error('[WebUI] Unregistered or modified compiled template. Register only immutable compiler output.');
47
+ }
48
+ target.innerHTML = trust.policy.createHTML(html, trust.capability);
49
+ }
50
+ export function setImportMapContent(script, specifier, css, view) {
51
+ if (script.type !== 'importmap') {
52
+ throw new TypeError('[WebUI] CSS data requires a script with type="importmap".');
53
+ }
54
+ const json = JSON.stringify({
55
+ imports: { [specifier]: `data:text/css,${encodeURIComponent(css)}` },
56
+ });
57
+ const trust = getDocumentPolicy(view);
58
+ if (trust)
59
+ script.textContent = trust.policy.createScript(json, trust.capability);
60
+ else
61
+ script.textContent = json;
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/webui-framework",
3
- "version": "0.0.28",
3
+ "version": "0.0.29",
4
4
  "publishConfig": {
5
5
  "tag": "latest"
6
6
  },
@@ -43,10 +43,10 @@
43
43
  "dist/"
44
44
  ],
45
45
  "devDependencies": {
46
- "@playwright/test": "^1.61.1",
47
- "@types/node": "^26.1.0",
48
- "typescript": "7.0.2",
49
- "@microsoft/webui-test-support": "0.0.28"
46
+ "@microsoft/webui-test-support": "0.0.29",
47
+ "@playwright/test": "^1.63.0",
48
+ "@types/node": "^26.5.1",
49
+ "typescript": "7.0.2"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsc",