@microsoft/webui-framework 0.0.25 → 0.0.26
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 +209 -53
- package/dist/component-asset/asset.d.ts +4 -2
- package/dist/component-asset/asset.js +19 -3
- package/dist/component-asset/generated-manifest.d.ts +1 -0
- package/dist/component-asset/generated-manifest.js +32 -0
- package/dist/component-asset/loader.d.ts +2 -1
- package/dist/component-asset/loader.js +98 -22
- package/dist/component-asset/manifest.d.ts +2 -1
- package/dist/component-asset.d.ts +1 -1
- package/dist/component-asset.js +12 -2
- package/dist/element/link-styles.d.ts +20 -0
- package/dist/element/link-styles.js +1030 -0
- package/dist/element/markers.d.ts +4 -0
- package/dist/element/markers.js +60 -5
- package/dist/element/styles.d.ts +39 -1
- package/dist/element/styles.js +623 -13
- package/dist/element/types.d.ts +5 -3
- package/dist/element.js +9 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/interaction-hydration.d.ts +8 -0
- package/dist/interaction-hydration.js +167 -0
- package/dist/static-host.js +43 -1
- package/dist/streaming-activation.d.ts +2 -2
- package/dist/streaming-activation.js +4 -2
- package/dist/streaming-bootstrap.d.ts +2 -2
- package/dist/streaming-bootstrap.js +7 -1
- package/dist/streaming-cleanup.d.ts +1 -0
- package/dist/streaming-cleanup.js +66 -14
- package/dist/streaming-coordinator.d.ts +3 -2
- package/dist/streaming-coordinator.js +107 -47
- package/dist/streaming-deferred.d.ts +14 -4
- package/dist/streaming-deferred.js +200 -87
- package/dist/streaming-dom.d.ts +12 -3
- package/dist/streaming-dom.js +41 -27
- package/dist/streaming-mode.d.ts +5 -0
- package/dist/streaming-mode.js +4 -0
- package/dist/streaming-protocol.d.ts +19 -3
- package/dist/streaming-protocol.js +4 -3
- package/dist/streaming-spans.d.ts +9 -0
- package/dist/streaming-spans.js +200 -0
- package/dist/template-content.d.ts +17 -0
- package/dist/template-content.js +106 -0
- package/dist/template-element.d.ts +17 -2
- package/dist/template-element.js +493 -177
- package/dist/template-events.d.ts +7 -0
- package/dist/template-events.js +7 -1
- package/dist/template-types.d.ts +1 -2
- package/dist/template.d.ts +7 -1
- package/dist/template.js +79 -16
- package/package.json +9 -3
- package/dist/component-asset/resources.d.ts +0 -4
- package/dist/component-asset/resources.js +0 -83
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { TemplateMeta } from './template.js';
|
|
2
2
|
export declare const TEMPLATES_REGISTERED_EVENT = "webui:templates-registered";
|
|
3
|
+
export interface TemplateRegistrationDetail {
|
|
4
|
+
templates?: Record<string, TemplateMeta>;
|
|
5
|
+
componentStyles?: unknown;
|
|
6
|
+
waitUntil?: (promise: PromiseLike<unknown>) => void;
|
|
7
|
+
}
|
|
3
8
|
export declare function dispatchTemplatesRegistered(templates: Record<string, TemplateMeta>): void;
|
|
4
9
|
export declare function templateRegistrationDetail(event: Event): {
|
|
5
10
|
templates?: Record<string, TemplateMeta>;
|
|
11
|
+
componentStyles?: unknown;
|
|
12
|
+
waitUntil?: (promise: PromiseLike<unknown>) => void;
|
|
6
13
|
} | undefined;
|
package/dist/template-events.js
CHANGED
|
@@ -18,6 +18,12 @@ export function templateRegistrationDetail(event) {
|
|
|
18
18
|
templates: typeof templates === 'object' && templates !== null
|
|
19
19
|
? templates
|
|
20
20
|
: undefined,
|
|
21
|
+
componentStyles: detail.componentStyles,
|
|
22
|
+
waitUntil: typeof detail.waitUntil === 'function'
|
|
23
|
+
? detail.waitUntil
|
|
24
|
+
: undefined,
|
|
21
25
|
};
|
|
22
|
-
return payload.templates
|
|
26
|
+
return payload.templates || payload.componentStyles !== undefined || payload.waitUntil
|
|
27
|
+
? payload
|
|
28
|
+
: undefined;
|
|
23
29
|
}
|
package/dist/template-types.d.ts
CHANGED
|
@@ -44,11 +44,10 @@ export interface TemplateBlockMeta {
|
|
|
44
44
|
}
|
|
45
45
|
export interface TemplateMeta extends TemplateBlockMeta {
|
|
46
46
|
b?: TemplateBlockMeta[];
|
|
47
|
-
sa?: string;
|
|
48
47
|
re?: [string, string, CompiledEventArgs][];
|
|
49
48
|
tr?: string[];
|
|
50
49
|
ta?: string[];
|
|
51
50
|
sd?: 1;
|
|
52
51
|
th?: boolean | 1;
|
|
53
|
-
wp?: 1 | 2;
|
|
52
|
+
wp?: 1 | 2 | 3 | 4;
|
|
54
53
|
}
|
package/dist/template.d.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
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/styles.js';
|
|
2
3
|
import type { CompiledConditionFn, TemplateMeta } from './template-types.js';
|
|
3
4
|
declare global {
|
|
4
5
|
interface WebUIRuntimeGlobal {
|
|
5
6
|
state?: Record<string, unknown>;
|
|
6
7
|
templates?: Record<string, TemplateMeta>;
|
|
7
8
|
templateFns?: Record<string, CompiledConditionFn[]>;
|
|
9
|
+
componentAssetStyles?: Record<string, readonly string[]>;
|
|
8
10
|
templateHostExclusions?: Set<string>;
|
|
11
|
+
componentStyles?: ComponentStyles;
|
|
9
12
|
[key: string]: unknown;
|
|
10
13
|
}
|
|
11
14
|
interface Window {
|
|
12
15
|
__webui?: WebUIRuntimeGlobal;
|
|
16
|
+
__webuiRegisterComponentStyles?: (value: unknown) => Promise<void> | undefined;
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
export declare function getTemplate(name: string): TemplateMeta | undefined;
|
|
16
20
|
export declare function getTemplateRegistry(): Record<string, TemplateMeta> | undefined;
|
|
17
|
-
export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]
|
|
21
|
+
export declare function registerTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>, componentStylesValue?: ComponentStyles | unknown): void;
|
|
18
22
|
export declare function prepareAssetTemplateData(templates: Record<string, TemplateMeta>, templateFns?: Record<string, CompiledConditionFn[]>): void;
|
|
19
23
|
export declare function deferTemplateDefinition(tagName: string, ctor: Function, define: () => void): void;
|
|
24
|
+
export declare function releaseSSRBootstrapState(): void;
|
|
25
|
+
export declare function releaseNonRoutedSSRBootstrapState(): void;
|
package/dist/template.js
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
import { dispatchTemplatesRegistered, templateRegistrationDetail, TEMPLATES_REGISTERED_EVENT, } from './template-events.js';
|
|
2
|
+
import { prepareComponentStyles, registerComponentStyles, registerPreparedComponentStyles, requireComponentStyles, validateComponentStylesRegistration, } from './element/styles.js';
|
|
3
|
+
import { prepareComponentStyleLinks, prepareRegisteredLinkStyles, } from './element/link-styles.js';
|
|
2
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');
|
|
3
7
|
const normalizedTemplates = new WeakSet();
|
|
4
8
|
const assetNormalizedTemplates = new WeakSet();
|
|
5
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
|
+
}
|
|
6
18
|
const pendingTemplateDefinitions = new Map();
|
|
7
|
-
function acceptTemplateData(templates, templateFns) {
|
|
19
|
+
function acceptTemplateData(templates, templateFns, componentStyles) {
|
|
20
|
+
validateComponentStylesRegistration(componentStyles);
|
|
8
21
|
prepareTemplateData(templates, templateFns);
|
|
22
|
+
if (componentStyles) {
|
|
23
|
+
registerPreparedComponentStyles(componentStyles);
|
|
24
|
+
}
|
|
9
25
|
const w = window;
|
|
10
26
|
if (!w.__webui)
|
|
11
27
|
w.__webui = {};
|
|
12
28
|
if (!w.__webui.templates)
|
|
13
29
|
w.__webui.templates = {};
|
|
14
|
-
if (templateFns) {
|
|
15
|
-
if (!w.__webui.templateFns)
|
|
16
|
-
w.__webui.templateFns = {};
|
|
17
|
-
const fnNames = Object.keys(templateFns);
|
|
18
|
-
for (let i = 0; i < fnNames.length; i++) {
|
|
19
|
-
const tag = fnNames[i];
|
|
20
|
-
w.__webui.templateFns[tag] = templateFns[tag];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
30
|
const names = Object.keys(templates);
|
|
24
31
|
for (let i = 0; i < names.length; i++) {
|
|
25
32
|
const tag = names[i];
|
|
@@ -35,11 +42,28 @@ function acceptTemplateData(templates, templateFns) {
|
|
|
35
42
|
return names.length > 0;
|
|
36
43
|
}
|
|
37
44
|
if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
|
|
45
|
+
window.__webuiRegisterComponentStyles = (value) => {
|
|
46
|
+
const styles = requireComponentStyles(value);
|
|
47
|
+
validateComponentStylesRegistration(styles);
|
|
48
|
+
registerPreparedComponentStyles(styles);
|
|
49
|
+
return prepareComponentStyleLinks(styles);
|
|
50
|
+
};
|
|
51
|
+
if (window.__webui?.componentStyles) {
|
|
52
|
+
registerComponentStyles(window.__webui.componentStyles);
|
|
53
|
+
}
|
|
38
54
|
window.addEventListener(TEMPLATES_REGISTERED_EVENT, (event) => {
|
|
39
|
-
const
|
|
40
|
-
if (templates)
|
|
41
|
-
|
|
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);
|
|
42
65
|
});
|
|
66
|
+
window.addEventListener(HYDRATION_COMPLETE_EVENT, releaseNonRoutedSSRBootstrapState, { once: true });
|
|
43
67
|
}
|
|
44
68
|
export function getTemplate(name) {
|
|
45
69
|
let meta = window.__webui?.templates?.[name];
|
|
@@ -55,8 +79,9 @@ export function getTemplateRegistry() {
|
|
|
55
79
|
loadWebUIDataBlock();
|
|
56
80
|
return window.__webui?.templates;
|
|
57
81
|
}
|
|
58
|
-
export function registerTemplateData(templates, templateFns) {
|
|
59
|
-
|
|
82
|
+
export function registerTemplateData(templates, templateFns, componentStylesValue) {
|
|
83
|
+
const componentStyles = prepareComponentStyles(componentStylesValue);
|
|
84
|
+
if (acceptTemplateData(templates, templateFns, componentStyles)) {
|
|
60
85
|
dispatchTemplatesRegistered(templates);
|
|
61
86
|
}
|
|
62
87
|
}
|
|
@@ -100,20 +125,58 @@ function loadWebUIDataBlock() {
|
|
|
100
125
|
const text = el.textContent;
|
|
101
126
|
if (text) {
|
|
102
127
|
const templateFns = window.__webui?.templateFns;
|
|
128
|
+
const componentAssetStyles = window.__webui?.componentAssetStyles;
|
|
103
129
|
const parsed = JSON.parse(text);
|
|
104
130
|
if (templateFns)
|
|
105
131
|
parsed.templateFns = templateFns;
|
|
132
|
+
if (componentAssetStyles)
|
|
133
|
+
parsed.componentAssetStyles = componentAssetStyles;
|
|
106
134
|
window.__webui = parsed;
|
|
135
|
+
el.remove();
|
|
136
|
+
webuiDataLoaded = true;
|
|
137
|
+
if (parsed.componentStyles !== undefined) {
|
|
138
|
+
registerComponentStyles(parsed.componentStyles);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
107
141
|
}
|
|
108
142
|
el.remove();
|
|
109
143
|
webuiDataLoaded = true;
|
|
110
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
|
+
}
|
|
111
158
|
function normalizeTemplate(name, meta, suppliedFns) {
|
|
112
159
|
if (normalizedTemplates.has(meta))
|
|
113
160
|
return;
|
|
114
|
-
const
|
|
161
|
+
const runtimeFns = window.__webui?.templateFns;
|
|
162
|
+
const fns = suppliedFns ?? runtimeFns?.[name] ?? [];
|
|
115
163
|
normalizeTemplateConditions(name, meta, fns);
|
|
116
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
|
+
}
|
|
117
180
|
}
|
|
118
181
|
function normalizeTemplateConditions(name, meta, fns) {
|
|
119
182
|
const stack = [meta];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/webui-framework",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "WebUI Framework Next — Preact-inspired lightweight Web Component runtime with SSR hydration and compiled-template path mapping.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,6 +28,12 @@
|
|
|
28
28
|
"types": "./dist/lazy-hydration-entry.d.ts",
|
|
29
29
|
"default": "./dist/lazy-hydration-entry.js"
|
|
30
30
|
}
|
|
31
|
+
},
|
|
32
|
+
"./interaction-hydration.js": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/interaction-hydration.d.ts",
|
|
35
|
+
"default": "./dist/interaction-hydration.js"
|
|
36
|
+
}
|
|
31
37
|
}
|
|
32
38
|
},
|
|
33
39
|
"files": [
|
|
@@ -36,8 +42,8 @@
|
|
|
36
42
|
"devDependencies": {
|
|
37
43
|
"@playwright/test": "^1.61.1",
|
|
38
44
|
"@types/node": "^26.1.0",
|
|
39
|
-
"typescript": "
|
|
40
|
-
"@microsoft/webui-test-support": "0.0.
|
|
45
|
+
"typescript": "7.0.2",
|
|
46
|
+
"@microsoft/webui-test-support": "0.0.26"
|
|
41
47
|
},
|
|
42
48
|
"scripts": {
|
|
43
49
|
"build": "tsc",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export type PreparedAssetStyles = Record<string, string>[];
|
|
2
|
-
export declare function prepareAssetStyles(templateStyles: readonly string[]): PreparedAssetStyles;
|
|
3
|
-
export declare function readNonce(): string;
|
|
4
|
-
export declare function registerAssetStyles(preparedStyles: readonly Record<string, string>[], nonce: string): void;
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
const injectedAssetStyles = new Set();
|
|
2
|
-
let assetStylesSeeded = false;
|
|
3
|
-
export function prepareAssetStyles(templateStyles) {
|
|
4
|
-
const prepared = new Array(templateStyles.length);
|
|
5
|
-
for (let i = 0; i < templateStyles.length; i++) {
|
|
6
|
-
prepared[i] = parseImportMap(templateStyles[i]);
|
|
7
|
-
}
|
|
8
|
-
return prepared;
|
|
9
|
-
}
|
|
10
|
-
export function readNonce() {
|
|
11
|
-
const nonce = assetGlobal()?.nonce;
|
|
12
|
-
if (nonce)
|
|
13
|
-
return nonce;
|
|
14
|
-
const meta = document.querySelector('meta[name="webui-nonce"]');
|
|
15
|
-
return meta?.content ?? '';
|
|
16
|
-
}
|
|
17
|
-
export function registerAssetStyles(preparedStyles, nonce) {
|
|
18
|
-
if (preparedStyles.length === 0)
|
|
19
|
-
return;
|
|
20
|
-
seedAssetStyleSet();
|
|
21
|
-
for (let i = 0; i < preparedStyles.length; i++) {
|
|
22
|
-
const imports = preparedStyles[i];
|
|
23
|
-
const nextImports = {};
|
|
24
|
-
let hasNewImport = false;
|
|
25
|
-
const specifiers = Object.keys(imports);
|
|
26
|
-
for (let j = 0; j < specifiers.length; j++) {
|
|
27
|
-
const specifier = specifiers[j];
|
|
28
|
-
if (injectedAssetStyles.has(specifier))
|
|
29
|
-
continue;
|
|
30
|
-
injectedAssetStyles.add(specifier);
|
|
31
|
-
nextImports[specifier] = imports[specifier];
|
|
32
|
-
hasNewImport = true;
|
|
33
|
-
}
|
|
34
|
-
if (!hasNewImport)
|
|
35
|
-
continue;
|
|
36
|
-
const script = document.createElement('script');
|
|
37
|
-
script.type = 'importmap';
|
|
38
|
-
if (nonce)
|
|
39
|
-
script.nonce = nonce;
|
|
40
|
-
script.textContent = JSON.stringify({ imports: nextImports });
|
|
41
|
-
document.head.appendChild(script);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function assetGlobal() {
|
|
45
|
-
return window.__webui;
|
|
46
|
-
}
|
|
47
|
-
function seedAssetStyleSet() {
|
|
48
|
-
if (assetStylesSeeded)
|
|
49
|
-
return;
|
|
50
|
-
assetStylesSeeded = true;
|
|
51
|
-
const styles = assetGlobal()?.styles;
|
|
52
|
-
if (!styles)
|
|
53
|
-
return;
|
|
54
|
-
for (let i = 0; i < styles.length; i++) {
|
|
55
|
-
injectedAssetStyles.add(styles[i]);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function parseImportMap(scriptMarkup) {
|
|
59
|
-
const trimmed = scriptMarkup.trim();
|
|
60
|
-
if (!trimmed.startsWith('<script type="importmap"')) {
|
|
61
|
-
throw new Error('[WebUI] Component asset templateStyles entry must be a <script type="importmap"> tag.');
|
|
62
|
-
}
|
|
63
|
-
const openTagEnd = trimmed.indexOf('>');
|
|
64
|
-
const closeTagStart = trimmed.lastIndexOf('</script>');
|
|
65
|
-
if (openTagEnd < 0 || closeTagStart <= openTagEnd) {
|
|
66
|
-
throw new Error('[WebUI] Component asset importmap tag is malformed.');
|
|
67
|
-
}
|
|
68
|
-
const parsed = JSON.parse(trimmed.substring(openTagEnd + 1, closeTagStart));
|
|
69
|
-
if (!parsed.imports || typeof parsed.imports !== 'object') {
|
|
70
|
-
throw new Error('[WebUI] Component asset importmap is missing an imports object.');
|
|
71
|
-
}
|
|
72
|
-
const imports = {};
|
|
73
|
-
const specifiers = Object.keys(parsed.imports);
|
|
74
|
-
for (let i = 0; i < specifiers.length; i++) {
|
|
75
|
-
const specifier = specifiers[i];
|
|
76
|
-
const uri = parsed.imports[specifier];
|
|
77
|
-
if (typeof uri !== 'string' || !uri.startsWith('data:text/css,')) {
|
|
78
|
-
throw new Error(`[WebUI] Component asset importmap entry "${specifier}" must be a data:text/css URI.`);
|
|
79
|
-
}
|
|
80
|
-
imports[specifier] = uri;
|
|
81
|
-
}
|
|
82
|
-
return imports;
|
|
83
|
-
}
|