@microsoft/webui-framework 0.0.30 → 3.0.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/README.md +46 -44
- package/dist/component-asset/asset.d.ts +8 -14
- package/dist/component-asset/asset.js +46 -143
- package/dist/component-asset/loader.d.ts +2 -0
- package/dist/component-asset/loader.js +7 -149
- package/dist/component-asset/registration.d.ts +11 -0
- package/dist/component-asset/registration.js +63 -0
- package/dist/component-asset/validate-graph.d.ts +2 -0
- package/dist/component-asset/validate-graph.js +42 -0
- package/dist/component-asset-runtime.d.ts +7 -0
- package/dist/component-asset-runtime.js +38 -0
- package/dist/component-asset.d.ts +1 -1
- package/dist/template-content.d.ts +1 -1
- package/dist/template-content.js +2 -2
- package/dist/template-element.js +16 -14
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -396,36 +396,40 @@ In most components you do not call `$update()` directly. Property changes throug
|
|
|
396
396
|
|
|
397
397
|
### Static component assets
|
|
398
398
|
|
|
399
|
-
|
|
400
|
-
`settings-dialog.webui.js` next to `protocol.bin`. Load the ESM asset before
|
|
401
|
-
creating the component when you are not using `@microsoft/webui-router`:
|
|
399
|
+
Generate stable ESM inputs separately from deployable output:
|
|
402
400
|
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
panelSlot.replaceChildren(await settingsAssets.create('settings-dialog'));
|
|
401
|
+
```bash
|
|
402
|
+
webui build ./src --out ./dist --plugin=webui \
|
|
403
|
+
--emit-component-assets settings-dialog \
|
|
404
|
+
--component-assets-out ./.webui
|
|
408
405
|
```
|
|
409
406
|
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
407
|
+
```typescript
|
|
408
|
+
import {
|
|
409
|
+
defineComponentAsset,
|
|
410
|
+
preloadComponentAssetStyles,
|
|
411
|
+
} from '@microsoft/webui-framework/component-asset-runtime.js';
|
|
412
|
+
|
|
413
|
+
const loadSettings = () => {
|
|
414
|
+
preloadComponentAssetStyles('settings-dialog');
|
|
415
|
+
return import('../../.webui/settings-dialog.webui.js')
|
|
416
|
+
.then(module => defineComponentAsset(module.default));
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
void loadSettings().then(asset => asset.preload()).catch(() => {});
|
|
420
|
+
panelSlot.replaceChildren(await (await loadSettings()).create());
|
|
421
421
|
```
|
|
422
422
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
423
|
+
WebUI emits one module per asset-owned component and a thin root entry with
|
|
424
|
+
static imports for its closure. The application bundler owns final splitting,
|
|
425
|
+
sharing, hashes, public paths, and caching. Entry-owned templates remain
|
|
426
|
+
external prerequisites, so load the normal application entry first. Component
|
|
427
|
+
assets cannot be combined with `<route>`. Current generated assets use version
|
|
428
|
+
4 on the root. Imported component payloads contain template and style data,
|
|
429
|
+
not repeated root manifests. Compiler-proven coverage is checked at build time;
|
|
430
|
+
registration checks external entry templates and live style conflicts before
|
|
431
|
+
mutating the registries. The manifest-driven loader additionally validates
|
|
432
|
+
untrusted graphs.
|
|
429
433
|
|
|
430
434
|
The compiler records final Link stylesheet filenames in the protocol. For
|
|
431
435
|
Shadow builds, the handler emits that finite manifest as inert JSON in the
|
|
@@ -437,25 +441,23 @@ WebUI handler or `Protocol`, which emits `#webui-component-assets`. A shell that
|
|
|
437
441
|
uses build artifacts without rendering the protocol still mounts safely through
|
|
438
442
|
the native stylesheet guard, but it does not receive the earlier
|
|
439
443
|
compiler-owned style preload.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
module is evicted from the registry so a later `preload(tag)` or `create(tag)`
|
|
458
|
-
retries it.
|
|
444
|
+
Only stable generated root input paths belong in authored imports. Final
|
|
445
|
+
JavaScript filenames are application-bundler outputs, while content-hashed
|
|
446
|
+
stylesheet filenames remain compiler-owned. `--metafile` exposes the static
|
|
447
|
+
input graph for analysis and build tooling.
|
|
448
|
+
|
|
449
|
+
Generated roots export semantic payloads only. Application-owned code loads
|
|
450
|
+
component behavior and data directly, so it also owns caching and recovery policy.
|
|
451
|
+
The runtime returns one facade per imported payload. Native ESM failures may stay
|
|
452
|
+
cached for the document lifetime: resetting an application promise does not
|
|
453
|
+
guarantee a new network request. Handle real open failures with visible errors
|
|
454
|
+
and bundler-owned recovery or an explicit page reload. Consume speculative
|
|
455
|
+
preload rejections without automatically reloading.
|
|
456
|
+
In Shadow builds, applications can import the small runtime eagerly, call
|
|
457
|
+
`preloadComponentAssetStyles(root)` before the dynamic import, and wrap the
|
|
458
|
+
default generated payload with `defineComponentAsset()`. This keeps templates
|
|
459
|
+
lazy while starting CSS and JavaScript in parallel. Concurrent roots share
|
|
460
|
+
in-flight registration and stylesheet work.
|
|
459
461
|
|
|
460
462
|
### `@observable`
|
|
461
463
|
|
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import type { CompiledConditionFn, TemplateMeta } from '../template.js';
|
|
2
2
|
import { type ComponentStyles } from '../element/styles.js';
|
|
3
|
-
export interface
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export interface ComponentAssetPayload {
|
|
4
|
+
componentStyles: ComponentStyles;
|
|
5
|
+
templates: Record<string, TemplateMeta>;
|
|
6
|
+
templateFunctions?: Record<string, CompiledConditionFn[]>;
|
|
7
7
|
}
|
|
8
8
|
export interface ComponentAsset {
|
|
9
9
|
type: 'webui-component-asset';
|
|
10
|
-
version:
|
|
11
|
-
|
|
12
|
-
root?: string;
|
|
13
|
-
components: string[];
|
|
14
|
-
requiredComponents: string[];
|
|
10
|
+
version: 4;
|
|
11
|
+
root: string;
|
|
15
12
|
externalComponents: string[];
|
|
16
|
-
imports:
|
|
13
|
+
imports: ComponentAssetPayload[];
|
|
17
14
|
componentStyles: ComponentStyles;
|
|
18
|
-
templates: Record<string, TemplateMeta>;
|
|
19
|
-
templateFunctions?: Record<string, CompiledConditionFn[]>;
|
|
20
15
|
}
|
|
21
16
|
export declare function prepareAssetComponentStyles(value: unknown): ComponentStyles;
|
|
22
17
|
export declare function readComponentAssetModule(module: unknown): unknown;
|
|
23
|
-
export declare function validateAsset(value: unknown
|
|
24
|
-
export declare function sameComponents(left: readonly string[], right: readonly string[]): boolean;
|
|
18
|
+
export declare function validateAsset(value: unknown): asserts value is ComponentAsset;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { requireComponentStyles, } from '../element/styles.js';
|
|
2
|
-
const ASSET_TYPE = 'webui-component-asset';
|
|
3
|
-
const COMPONENT_STYLES_ASSET_VERSION = 3;
|
|
4
2
|
const preparedAssetStyles = new WeakMap();
|
|
5
3
|
export function prepareAssetComponentStyles(value) {
|
|
6
|
-
if (
|
|
4
|
+
if (isObject(value)) {
|
|
7
5
|
const cached = preparedAssetStyles.get(value);
|
|
8
6
|
if (cached)
|
|
9
7
|
return cached;
|
|
@@ -19,169 +17,74 @@ export function readComponentAssetModule(module) {
|
|
|
19
17
|
}
|
|
20
18
|
return module.default;
|
|
21
19
|
}
|
|
22
|
-
export function validateAsset(value
|
|
20
|
+
export function validateAsset(value) {
|
|
23
21
|
if (!isObject(value)) {
|
|
24
22
|
throw new Error('[WebUI] Component asset default export must be an object.');
|
|
25
23
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
throw new Error(`[WebUI] Invalid component asset type: ${String(asset.type)}`);
|
|
24
|
+
if (value.type !== 'webui-component-asset') {
|
|
25
|
+
throw new Error(`[WebUI] Invalid component asset type: ${String(value.type)}`);
|
|
29
26
|
}
|
|
30
|
-
if (
|
|
31
|
-
throw new Error(`[WebUI] Unsupported component asset version: ${String(
|
|
27
|
+
if (value.version !== 4) {
|
|
28
|
+
throw new Error(`[WebUI] Unsupported component asset version: ${String(value.version)}`);
|
|
32
29
|
}
|
|
33
|
-
if (
|
|
34
|
-
throw new Error('[WebUI]
|
|
30
|
+
if (typeof value.root !== 'string' || value.root.length === 0) {
|
|
31
|
+
throw new Error('[WebUI] Component asset root must name its root component.');
|
|
35
32
|
}
|
|
36
|
-
if (
|
|
37
|
-
throw new Error(
|
|
33
|
+
if (value.componentStyles === undefined) {
|
|
34
|
+
throw new Error('[WebUI] Version 4 component assets require componentStyles.');
|
|
38
35
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!Array.isArray(asset.imports)) {
|
|
36
|
+
prepareAssetComponentStyles(value.componentStyles);
|
|
37
|
+
validateExternalComponents(value.externalComponents);
|
|
38
|
+
if (!Array.isArray(value.imports)) {
|
|
43
39
|
throw new Error('[WebUI] Component asset imports must be an array.');
|
|
44
40
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (expectedKind === 'root') {
|
|
51
|
-
if (typeof asset.root !== 'string' || asset.root.length === 0) {
|
|
52
|
-
throw new Error('[WebUI] Component asset root must name its root component.');
|
|
41
|
+
const providers = new Set(value.externalComponents);
|
|
42
|
+
for (let i = 0; i < value.imports.length; i++) {
|
|
43
|
+
const tag = validatePayload(value.imports[i]);
|
|
44
|
+
if (providers.has(tag)) {
|
|
45
|
+
throw new Error(`[WebUI] Component asset assigns template <${tag}> to more than one import or external prerequisite.`);
|
|
53
46
|
}
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
validateChunkShape(asset.root, asset.imports, asset.externalComponents);
|
|
47
|
+
providers.add(tag);
|
|
58
48
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
validateAssetPayload(asset.components, asset.templates, asset.templateFunctions);
|
|
63
|
-
validateAssetCoverage(root, asset.components, asset.requiredComponents, asset.externalComponents, asset.imports);
|
|
64
|
-
}
|
|
65
|
-
export function sameComponents(left, right) {
|
|
66
|
-
if (left.length !== right.length)
|
|
67
|
-
return false;
|
|
68
|
-
for (let i = 0; i < left.length; i++) {
|
|
69
|
-
if (left[i] !== right[i])
|
|
70
|
-
return false;
|
|
49
|
+
if (!providers.has(value.root)) {
|
|
50
|
+
throw new Error(`[WebUI] Component asset root <${value.root}> has no imported payload or external prerequisite.`);
|
|
71
51
|
}
|
|
72
|
-
return true;
|
|
73
52
|
}
|
|
74
53
|
function isObject(value) {
|
|
75
54
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
76
55
|
}
|
|
77
|
-
function
|
|
78
|
-
if (root !== undefined) {
|
|
79
|
-
throw new Error('[WebUI] Shared component asset chunks cannot declare a root.');
|
|
80
|
-
}
|
|
81
|
-
if (imports.length !== 0) {
|
|
82
|
-
throw new Error('[WebUI] Shared component asset chunks cannot import other chunks.');
|
|
83
|
-
}
|
|
84
|
-
if (externalComponents.length !== 0) {
|
|
85
|
-
throw new Error('[WebUI] Shared component asset chunks cannot declare external components.');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function validateAssetImport(value) {
|
|
89
|
-
if (!isObject(value)) {
|
|
90
|
-
throw new Error('[WebUI] Component asset import must be an object.');
|
|
91
|
-
}
|
|
92
|
-
const assetImport = value;
|
|
93
|
-
validateUniqueStringArray(assetImport.components, 'import components');
|
|
94
|
-
if (assetImport.components.length === 0) {
|
|
95
|
-
throw new Error('[WebUI] Component asset imports must provide at least one component.');
|
|
96
|
-
}
|
|
97
|
-
if (typeof assetImport.href !== 'string' || assetImport.href.length === 0) {
|
|
98
|
-
throw new Error('[WebUI] Component asset import href must be a non-empty string.');
|
|
99
|
-
}
|
|
100
|
-
if (typeof assetImport.load !== 'function') {
|
|
101
|
-
throw new Error('[WebUI] Component asset import load must be a function.');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function validateUniqueStringArray(value, field) {
|
|
105
|
-
validateStringArray(value, field);
|
|
106
|
-
const seen = new Set();
|
|
107
|
-
for (let i = 0; i < value.length; i++) {
|
|
108
|
-
if (seen.has(value[i])) {
|
|
109
|
-
throw new Error(`[WebUI] Component asset ${field} cannot contain duplicate <${value[i]}> entries.`);
|
|
110
|
-
}
|
|
111
|
-
seen.add(value[i]);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function validateStringArray(value, field) {
|
|
56
|
+
function validateExternalComponents(value) {
|
|
115
57
|
if (!Array.isArray(value)) {
|
|
116
|
-
throw new Error(
|
|
58
|
+
throw new Error('[WebUI] Component asset externalComponents must be an array.');
|
|
117
59
|
}
|
|
60
|
+
const seen = new Set();
|
|
118
61
|
for (let i = 0; i < value.length; i++) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
function validateAssetCoverage(root, components, requiredComponents, externalComponents, imports) {
|
|
125
|
-
const required = new Set(requiredComponents);
|
|
126
|
-
if (root && !required.has(root)) {
|
|
127
|
-
throw new Error(`[WebUI] Component asset root <${root}> must include itself in requiredComponents.`);
|
|
128
|
-
}
|
|
129
|
-
const providers = new Set();
|
|
130
|
-
const addProvider = (component) => {
|
|
131
|
-
if (!required.has(component)) {
|
|
132
|
-
throw new Error(`[WebUI] Component asset provides undeclared template <${component}>. Add it to requiredComponents.`);
|
|
133
|
-
}
|
|
134
|
-
if (providers.has(component)) {
|
|
135
|
-
throw new Error(`[WebUI] Component asset assigns required template <${component}> to more than one payload, import, or external prerequisite.`);
|
|
136
|
-
}
|
|
137
|
-
providers.add(component);
|
|
138
|
-
};
|
|
139
|
-
for (let i = 0; i < components.length; i++)
|
|
140
|
-
addProvider(components[i]);
|
|
141
|
-
for (let i = 0; i < externalComponents.length; i++)
|
|
142
|
-
addProvider(externalComponents[i]);
|
|
143
|
-
for (let i = 0; i < imports.length; i++) {
|
|
144
|
-
const imported = imports[i].components;
|
|
145
|
-
for (let j = 0; j < imported.length; j++)
|
|
146
|
-
addProvider(imported[j]);
|
|
147
|
-
}
|
|
148
|
-
for (let i = 0; i < requiredComponents.length; i++) {
|
|
149
|
-
const component = requiredComponents[i];
|
|
150
|
-
if (!providers.has(component)) {
|
|
151
|
-
throw new Error(`[WebUI] Component asset required template <${component}> has no payload, import, or external prerequisite.`);
|
|
62
|
+
const tag = value[i];
|
|
63
|
+
if (typeof tag !== 'string' || tag.length === 0 || seen.has(tag)) {
|
|
64
|
+
throw new Error('[WebUI] Component asset externalComponents must contain unique non-empty strings.');
|
|
152
65
|
}
|
|
66
|
+
seen.add(tag);
|
|
153
67
|
}
|
|
154
68
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
if (templateFunctions === undefined)
|
|
171
|
-
return;
|
|
172
|
-
if (!isObject(templateFunctions)) {
|
|
173
|
-
throw new Error('[WebUI] Component asset templateFunctions must be an object.');
|
|
174
|
-
}
|
|
175
|
-
const functionNames = Object.keys(templateFunctions);
|
|
176
|
-
for (let i = 0; i < functionNames.length; i++) {
|
|
177
|
-
const component = functionNames[i];
|
|
178
|
-
if (!declared.has(component)) {
|
|
179
|
-
throw new Error(`[WebUI] Component asset templateFunctions contains undeclared payload <${component}>.`);
|
|
69
|
+
function validatePayload(value) {
|
|
70
|
+
if (!isObject(value) || !isObject(value.templates)) {
|
|
71
|
+
throw new Error('[WebUI] Component asset payload must contain its template metadata.');
|
|
72
|
+
}
|
|
73
|
+
const tags = Object.keys(value.templates);
|
|
74
|
+
if (tags.length !== 1 || tags[0].length === 0) {
|
|
75
|
+
throw new Error('[WebUI] Component asset payload must contain exactly one template.');
|
|
76
|
+
}
|
|
77
|
+
const tag = tags[0];
|
|
78
|
+
prepareAssetComponentStyles(value.componentStyles);
|
|
79
|
+
const functions = value.templateFunctions;
|
|
80
|
+
if (functions !== undefined) {
|
|
81
|
+
if (!isObject(functions) || Object.keys(functions).some(name => name !== tag)) {
|
|
82
|
+
throw new Error(`[WebUI] Component asset templateFunctions contains undeclared payload for <${tag}>.`);
|
|
180
83
|
}
|
|
181
|
-
const
|
|
182
|
-
if (!Array.isArray(
|
|
183
|
-
|
|
184
|
-
throw new Error(`[WebUI] Component asset templateFunctions for <${component}> must contain only functions.`);
|
|
84
|
+
const closures = functions[tag];
|
|
85
|
+
if (!Array.isArray(closures) || closures.some(candidate => typeof candidate !== 'function')) {
|
|
86
|
+
throw new Error(`[WebUI] Component asset templateFunctions for <${tag}> must contain only functions.`);
|
|
185
87
|
}
|
|
186
88
|
}
|
|
89
|
+
return tag;
|
|
187
90
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
import { type ComponentAsset } from './asset.js';
|
|
1
2
|
import type { ComponentAssetSource } from './manifest.js';
|
|
2
3
|
export declare function loadComponentAsset(tag: string, source: ComponentAssetSource): Promise<void>;
|
|
4
|
+
export declare function registerComponentAsset(asset: ComponentAsset): Promise<void>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { prepareComponentStyleLinks, prepareRegisteredLinkStyles, } from '../element/link-styles.js';
|
|
1
|
+
import { prepareAssetComponentStyles, readComponentAssetModule, validateAsset, } from './asset.js';
|
|
2
|
+
import { registerComponentAssetGraph } from './registration.js';
|
|
3
|
+
import { validateComponentAssetGraph } from './validate-graph.js';
|
|
5
4
|
const assetModulePromises = new Map();
|
|
6
5
|
export function loadComponentAsset(tag, source) {
|
|
7
6
|
if (typeof source === 'function') {
|
|
@@ -16,34 +15,11 @@ export function loadComponentAsset(tag, source) {
|
|
|
16
15
|
}
|
|
17
16
|
async function registerRootAsset(expectedRoot, href, imported) {
|
|
18
17
|
const asset = readComponentAssetModule(imported);
|
|
19
|
-
validateAsset(asset
|
|
18
|
+
validateAsset(asset);
|
|
20
19
|
if (asset.root !== expectedRoot) {
|
|
21
20
|
throw new Error(`[WebUI] Component asset manifest expected <${expectedRoot}> but ${href} exports <${String(asset.root)}>.`);
|
|
22
21
|
}
|
|
23
|
-
|
|
24
|
-
const root = prepareComponentPayload(asset);
|
|
25
|
-
const chunks = await prepareAssetImports(asset.imports);
|
|
26
|
-
const graph = [...chunks, root];
|
|
27
|
-
validatePreparedGraph(graph);
|
|
28
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
29
|
-
registerComponentResources(chunks[i]);
|
|
30
|
-
}
|
|
31
|
-
registerComponentResources(root);
|
|
32
|
-
let pendingStyles;
|
|
33
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
34
|
-
const ready = chunks[i].linkStyles;
|
|
35
|
-
if (!ready)
|
|
36
|
-
continue;
|
|
37
|
-
(pendingStyles ??= []).push(ready);
|
|
38
|
-
}
|
|
39
|
-
if (root.linkStyles)
|
|
40
|
-
(pendingStyles ??= []).push(root.linkStyles);
|
|
41
|
-
if (pendingStyles)
|
|
42
|
-
await Promise.all(pendingStyles);
|
|
43
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
44
|
-
registerComponentTemplates(chunks[i]);
|
|
45
|
-
}
|
|
46
|
-
registerComponentTemplates(root);
|
|
22
|
+
await registerComponentAssetGraph(asset, prepareAssetComponentStyles, validateComponentAssetGraph);
|
|
47
23
|
}
|
|
48
24
|
function loadAssetModule(href, load) {
|
|
49
25
|
let promise = assetModulePromises.get(href);
|
|
@@ -57,124 +33,6 @@ function loadAssetModule(href, load) {
|
|
|
57
33
|
assetModulePromises.set(href, promise);
|
|
58
34
|
return promise;
|
|
59
35
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
for (let i = 0; i < imports.length; i++) {
|
|
63
|
-
const assetImport = imports[i];
|
|
64
|
-
if (componentsAlreadyRegistered(assetImport.components))
|
|
65
|
-
continue;
|
|
66
|
-
pending.push(importAndPrepareChunk(assetImport));
|
|
67
|
-
}
|
|
68
|
-
return Promise.all(pending);
|
|
69
|
-
}
|
|
70
|
-
function importAndPrepareChunk(assetImport) {
|
|
71
|
-
const href = new URL(assetImport.href, document.baseURI).href;
|
|
72
|
-
return loadAssetModule(href, assetImport.load)
|
|
73
|
-
.then(imported => {
|
|
74
|
-
const chunk = readComponentAssetModule(imported);
|
|
75
|
-
validateAsset(chunk, 'chunk');
|
|
76
|
-
if (!sameComponents(chunk.components, assetImport.components)) {
|
|
77
|
-
throw new Error(`[WebUI] Shared component asset ${href} does not provide the components declared by its root import.`);
|
|
78
|
-
}
|
|
79
|
-
return prepareComponentPayload(chunk);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
function prepareComponentPayload(asset) {
|
|
83
|
-
const componentStyles = prepareAssetComponentStyles(asset.componentStyles);
|
|
84
|
-
prepareAssetTemplateData(asset.templates, asset.templateFunctions);
|
|
85
|
-
const templateLinkStyles = prepareRegisteredLinkStyles(asset.templates);
|
|
86
|
-
const componentStyleLinks = prepareComponentStyleLinks(componentStyles);
|
|
87
|
-
let linkStyles = templateLinkStyles;
|
|
88
|
-
if (templateLinkStyles && componentStyleLinks) {
|
|
89
|
-
linkStyles = Promise.all([templateLinkStyles, componentStyleLinks]).then(() => { });
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
linkStyles = templateLinkStyles ?? componentStyleLinks;
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
asset,
|
|
96
|
-
componentStyles,
|
|
97
|
-
linkStyles,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function registerComponentResources(prepared) {
|
|
101
|
-
registerPreparedComponentStyles(prepared.componentStyles);
|
|
102
|
-
}
|
|
103
|
-
function registerComponentTemplates(prepared) {
|
|
104
|
-
const { asset } = prepared;
|
|
105
|
-
if (componentsAlreadyRegistered(asset.components))
|
|
106
|
-
return;
|
|
107
|
-
registerTemplateData(asset.templates, asset.templateFunctions);
|
|
108
|
-
}
|
|
109
|
-
function validatePreparedGraph(graph) {
|
|
110
|
-
const resources = new Map();
|
|
111
|
-
const closures = new Map();
|
|
112
|
-
const provided = new Set();
|
|
113
|
-
for (let i = 0; i < graph.length; i++) {
|
|
114
|
-
const prepared = graph[i];
|
|
115
|
-
validateComponentStylesRegistration(prepared.componentStyles);
|
|
116
|
-
for (const component of prepared.asset.components)
|
|
117
|
-
provided.add(component);
|
|
118
|
-
const styles = prepared.componentStyles;
|
|
119
|
-
for (const id of Object.keys(styles.resources)) {
|
|
120
|
-
const resource = styles.resources[id];
|
|
121
|
-
const current = resources.get(id);
|
|
122
|
-
if (current && !sameComponentStyleResource(current, resource)) {
|
|
123
|
-
throw new Error(`[WebUI] Conflicting component style resource "${id}".`);
|
|
124
|
-
}
|
|
125
|
-
resources.set(id, resource);
|
|
126
|
-
}
|
|
127
|
-
for (const root of Object.keys(styles.closures)) {
|
|
128
|
-
const closure = styles.closures[root];
|
|
129
|
-
const current = closures.get(root);
|
|
130
|
-
if (current && !sameComponentStyleClosure(current, closure)) {
|
|
131
|
-
throw new Error(`[WebUI] Conflicting component style closure "${root}".`);
|
|
132
|
-
}
|
|
133
|
-
closures.set(root, closure);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
for (let i = 0; i < graph.length; i++) {
|
|
137
|
-
const styles = graph[i].componentStyles;
|
|
138
|
-
for (const root of Object.keys(styles.closures)) {
|
|
139
|
-
for (const id of styles.closures[root]) {
|
|
140
|
-
if (!resources.has(id) &&
|
|
141
|
-
!hasRegisteredComponentStyleResource(id)) {
|
|
142
|
-
throw new Error(`[WebUI] Component style closure "${root}" references missing resource "${id}".`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const missing = [];
|
|
148
|
-
for (let i = 0; i < graph.length; i++) {
|
|
149
|
-
const asset = graph[i].asset;
|
|
150
|
-
for (const required of asset.requiredComponents) {
|
|
151
|
-
if (provided.has(required) || getTemplate(required))
|
|
152
|
-
continue;
|
|
153
|
-
if (missing.indexOf(required) < 0)
|
|
154
|
-
missing.push(required);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (missing.length === 0)
|
|
158
|
-
return;
|
|
159
|
-
throw new Error(`[WebUI] Component asset is missing required templ${missing.length === 1 ? 'ate' : 'ates'} ${missing.map(tag => `<${tag}>`).join(', ')}. Load the application entry bundle and protocol before deferred component assets.`);
|
|
160
|
-
}
|
|
161
|
-
function componentsAlreadyRegistered(components) {
|
|
162
|
-
if (components.length === 0)
|
|
163
|
-
return false;
|
|
164
|
-
for (let i = 0; i < components.length; i++) {
|
|
165
|
-
if (!getTemplate(components[i]))
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
function validateExternalComponents(asset) {
|
|
171
|
-
const missing = [];
|
|
172
|
-
for (let i = 0; i < asset.externalComponents.length; i++) {
|
|
173
|
-
const component = asset.externalComponents[i];
|
|
174
|
-
if (!getTemplate(component))
|
|
175
|
-
missing.push(component);
|
|
176
|
-
}
|
|
177
|
-
if (missing.length === 0)
|
|
178
|
-
return;
|
|
179
|
-
throw new Error(`[WebUI] Component asset requires entr${missing.length === 1 ? 'y template' : 'y templates'} ${missing.map(tag => `<${tag}>`).join(', ')}. Load the application entry bundle and protocol before deferred component assets.`);
|
|
36
|
+
export function registerComponentAsset(asset) {
|
|
37
|
+
return registerRootAsset(typeof asset.root === 'string' ? asset.root : '', 'generated component asset', { default: asset });
|
|
180
38
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ComponentStyles } from '../element/styles.js';
|
|
2
|
+
import type { ComponentAsset, ComponentAssetPayload } from './asset.js';
|
|
3
|
+
export interface PreparedComponentAsset {
|
|
4
|
+
tag: string;
|
|
5
|
+
payload?: ComponentAssetPayload;
|
|
6
|
+
componentStyles: ComponentStyles;
|
|
7
|
+
}
|
|
8
|
+
type PrepareStyles = (value: unknown) => ComponentStyles;
|
|
9
|
+
export type ValidateComponentAssetGraph = (graph: readonly PreparedComponentAsset[]) => void;
|
|
10
|
+
export declare function registerComponentAssetGraph(asset: ComponentAsset, prepareStyles?: PrepareStyles, validateGraph?: ValidateComponentAssetGraph): Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getTemplate, prepareAssetTemplateData, registerTemplateData, } from '../template.js';
|
|
2
|
+
import { registerPreparedComponentStyles, validateComponentStylesRegistration, } from '../element/styles.js';
|
|
3
|
+
import { prepareComponentStyleLinks, prepareRegisteredLinkStyles, } from '../element/link-styles.js';
|
|
4
|
+
export async function registerComponentAssetGraph(asset, prepareStyles = trustedComponentStyles, validateGraph) {
|
|
5
|
+
const graph = [];
|
|
6
|
+
for (let i = 0; i < asset.imports.length; i++) {
|
|
7
|
+
const payload = asset.imports[i];
|
|
8
|
+
const styles = prepareStyles(payload.componentStyles);
|
|
9
|
+
validateComponentStylesRegistration(styles);
|
|
10
|
+
graph.push({
|
|
11
|
+
tag: Object.keys(payload.templates)[0],
|
|
12
|
+
payload,
|
|
13
|
+
componentStyles: styles,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const rootStyles = prepareStyles(asset.componentStyles);
|
|
17
|
+
validateComponentStylesRegistration(rootStyles);
|
|
18
|
+
graph.push({ tag: asset.root, componentStyles: rootStyles });
|
|
19
|
+
validateGraph?.(graph);
|
|
20
|
+
validateExternalTemplates(asset.externalComponents);
|
|
21
|
+
for (let i = 0; i < graph.length; i++) {
|
|
22
|
+
const prepared = graph[i];
|
|
23
|
+
const payload = prepared.payload;
|
|
24
|
+
if (payload && !getTemplate(prepared.tag)) {
|
|
25
|
+
prepareAssetTemplateData(payload.templates, payload.templateFunctions);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const pendingStyles = [];
|
|
29
|
+
for (let i = 0; i < graph.length; i++) {
|
|
30
|
+
const prepared = graph[i];
|
|
31
|
+
const payload = prepared.payload;
|
|
32
|
+
const templateLinks = payload && prepareRegisteredLinkStyles(payload.templates);
|
|
33
|
+
const componentLinks = prepareComponentStyleLinks(prepared.componentStyles);
|
|
34
|
+
if (templateLinks)
|
|
35
|
+
pendingStyles.push(templateLinks);
|
|
36
|
+
if (componentLinks)
|
|
37
|
+
pendingStyles.push(componentLinks);
|
|
38
|
+
}
|
|
39
|
+
for (let i = 0; i < graph.length; i++) {
|
|
40
|
+
registerPreparedComponentStyles(graph[i].componentStyles);
|
|
41
|
+
}
|
|
42
|
+
if (pendingStyles.length > 0)
|
|
43
|
+
await Promise.all(pendingStyles);
|
|
44
|
+
for (let i = 0; i < graph.length; i++) {
|
|
45
|
+
const payload = graph[i].payload;
|
|
46
|
+
if (payload && !getTemplate(graph[i].tag)) {
|
|
47
|
+
registerTemplateData(payload.templates, payload.templateFunctions);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function validateExternalTemplates(external) {
|
|
52
|
+
const missing = [];
|
|
53
|
+
for (let i = 0; i < external.length; i++) {
|
|
54
|
+
if (!getTemplate(external[i]))
|
|
55
|
+
missing.push(external[i]);
|
|
56
|
+
}
|
|
57
|
+
if (missing.length > 0) {
|
|
58
|
+
throw new Error(`[WebUI] Component asset requires entr${missing.length === 1 ? 'y template' : 'y templates'} ${missing.map(tag => `<${tag}>`).join(', ')}. Load the application entry bundle and protocol before deferred component assets.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function trustedComponentStyles(value) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { hasRegisteredComponentStyleResource, sameComponentStyleClosure, sameComponentStyleResource, } from '../element/styles.js';
|
|
2
|
+
export function validateComponentAssetGraph(graph) {
|
|
3
|
+
const resources = new Map();
|
|
4
|
+
const closures = new Map();
|
|
5
|
+
for (let i = 0; i < graph.length; i++) {
|
|
6
|
+
const styles = graph[i].componentStyles;
|
|
7
|
+
const resourceIds = Object.keys(styles.resources);
|
|
8
|
+
for (let j = 0; j < resourceIds.length; j++) {
|
|
9
|
+
const id = resourceIds[j];
|
|
10
|
+
const resource = styles.resources[id];
|
|
11
|
+
const current = resources.get(id);
|
|
12
|
+
if (current && !sameComponentStyleResource(current, resource)) {
|
|
13
|
+
throw new Error(`[WebUI] Conflicting component style resource "${id}".`);
|
|
14
|
+
}
|
|
15
|
+
resources.set(id, resource);
|
|
16
|
+
}
|
|
17
|
+
const roots = Object.keys(styles.closures);
|
|
18
|
+
for (let j = 0; j < roots.length; j++) {
|
|
19
|
+
const root = roots[j];
|
|
20
|
+
const closure = styles.closures[root];
|
|
21
|
+
const current = closures.get(root);
|
|
22
|
+
if (current && !sameComponentStyleClosure(current, closure)) {
|
|
23
|
+
throw new Error(`[WebUI] Conflicting component style closure "${root}".`);
|
|
24
|
+
}
|
|
25
|
+
closures.set(root, closure);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (let i = 0; i < graph.length; i++) {
|
|
29
|
+
const styles = graph[i].componentStyles;
|
|
30
|
+
const roots = Object.keys(styles.closures);
|
|
31
|
+
for (let j = 0; j < roots.length; j++) {
|
|
32
|
+
const root = roots[j];
|
|
33
|
+
const closure = styles.closures[root];
|
|
34
|
+
for (let k = 0; k < closure.length; k++) {
|
|
35
|
+
const id = closure[k];
|
|
36
|
+
if (!resources.has(id) && !hasRegisteredComponentStyleResource(id)) {
|
|
37
|
+
throw new Error(`[WebUI] Component style closure "${root}" references missing resource "${id}".`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ComponentAsset } from './component-asset/asset.js';
|
|
2
|
+
export interface GeneratedComponentAsset {
|
|
3
|
+
preload(): Promise<void>;
|
|
4
|
+
create(): Promise<HTMLElement>;
|
|
5
|
+
}
|
|
6
|
+
export declare function preloadComponentAssetStyles(root: string): void;
|
|
7
|
+
export declare function defineComponentAsset(asset: ComponentAsset): GeneratedComponentAsset;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { registerComponentAssetGraph } from './component-asset/registration.js';
|
|
2
|
+
import { takeGeneratedComponentAssetStyles } from './component-asset/generated-manifest.js';
|
|
3
|
+
import { preloadComponentAssetStyles as preloadComponentAssetStylesInternal } from './element/link-styles.js';
|
|
4
|
+
const facades = new WeakMap();
|
|
5
|
+
export function preloadComponentAssetStyles(root) {
|
|
6
|
+
const styles = takeGeneratedComponentAssetStyles(root);
|
|
7
|
+
if (styles)
|
|
8
|
+
preloadComponentAssetStylesInternal(styles);
|
|
9
|
+
}
|
|
10
|
+
export function defineComponentAsset(asset) {
|
|
11
|
+
if (asset.type !== 'webui-component-asset' || asset.version !== 4) {
|
|
12
|
+
throw new Error('[WebUI] Expected a version 4 component asset root. Rebuild generated inputs with a compatible compiler.');
|
|
13
|
+
}
|
|
14
|
+
const existing = facades.get(asset);
|
|
15
|
+
if (existing)
|
|
16
|
+
return existing;
|
|
17
|
+
const root = asset.root;
|
|
18
|
+
let pending;
|
|
19
|
+
const preload = () => {
|
|
20
|
+
if (pending)
|
|
21
|
+
return pending;
|
|
22
|
+
preloadComponentAssetStyles(root);
|
|
23
|
+
const next = registerComponentAssetGraph(asset).catch((error) => {
|
|
24
|
+
if (pending === next)
|
|
25
|
+
pending = undefined;
|
|
26
|
+
throw error;
|
|
27
|
+
});
|
|
28
|
+
pending = next;
|
|
29
|
+
return next;
|
|
30
|
+
};
|
|
31
|
+
const create = async () => {
|
|
32
|
+
await preload();
|
|
33
|
+
return document.createElement(root);
|
|
34
|
+
};
|
|
35
|
+
const facade = { preload, create };
|
|
36
|
+
facades.set(asset, facade);
|
|
37
|
+
return facade;
|
|
38
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ComponentAssetManifest, ComponentAssetRegistry } from './component-asset/manifest.js';
|
|
2
|
-
export type { ComponentAsset,
|
|
2
|
+
export type { ComponentAsset, ComponentAssetPayload, } from './component-asset/asset.js';
|
|
3
3
|
export type { ComponentAssetCreateOptions, ComponentAssetManifest, ComponentAssetManifestEntry, ComponentAssetPreload, ComponentAssetRegistry, ComponentAssetSource, ComponentAssetState, } from './component-asset/manifest.js';
|
|
4
4
|
export declare function defineComponentAssets(manifest: ComponentAssetManifest): ComponentAssetRegistry;
|
|
@@ -12,7 +12,7 @@ export interface TemplateStylesheetDescriptor {
|
|
|
12
12
|
readonly title: string;
|
|
13
13
|
readonly type: string;
|
|
14
14
|
}
|
|
15
|
-
export declare function cloneTemplateContent(meta: TemplateBlockMeta): DocumentFragment;
|
|
15
|
+
export declare function cloneTemplateContent(meta: TemplateBlockMeta, ownerDocument: Document): DocumentFragment;
|
|
16
16
|
export declare function getTemplateFragment(meta: TemplateBlockMeta): DocumentFragment;
|
|
17
17
|
export declare function getTemplateStylesheets(meta: TemplateBlockMeta): readonly TemplateStylesheetDescriptor[] | undefined;
|
|
18
18
|
export declare function templateHtmlMayContainLink(html: string): boolean;
|
package/dist/template-content.js
CHANGED
|
@@ -2,8 +2,8 @@ import { setTemplateContent } from './trusted-types-policy.js';
|
|
|
2
2
|
const EMPTY_STYLESHEETS = Object.freeze([]);
|
|
3
3
|
const NO_LINK_STYLESHEETS = Object.freeze([]);
|
|
4
4
|
const templateContentCache = new WeakMap();
|
|
5
|
-
export function cloneTemplateContent(meta) {
|
|
6
|
-
return getTemplateFragment(meta)
|
|
5
|
+
export function cloneTemplateContent(meta, ownerDocument) {
|
|
6
|
+
return ownerDocument.importNode(getTemplateFragment(meta), true);
|
|
7
7
|
}
|
|
8
8
|
export function getTemplateFragment(meta) {
|
|
9
9
|
return getTemplateContent(meta).fragment;
|
package/dist/template-element.js
CHANGED
|
@@ -1125,10 +1125,10 @@ export class TemplateElement extends HTMLElement {
|
|
|
1125
1125
|
void import('./hydration-mismatch.js').then((m) => m.reportHydrationMismatch(tag, writes, index, ctx));
|
|
1126
1126
|
}
|
|
1127
1127
|
$parseTemplate(meta) {
|
|
1128
|
-
return cloneTemplateContent(meta);
|
|
1128
|
+
return cloneTemplateContent(meta, this.ownerDocument);
|
|
1129
1129
|
}
|
|
1130
1130
|
$createStagingRoot(meta) {
|
|
1131
|
-
const wrapper =
|
|
1131
|
+
const wrapper = this.ownerDocument.createElement('div');
|
|
1132
1132
|
const fragment = this.$parseTemplate(meta);
|
|
1133
1133
|
wrapper.appendChild(fragment);
|
|
1134
1134
|
customElements.upgrade(wrapper);
|
|
@@ -1142,7 +1142,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1142
1142
|
root.appendChild(first);
|
|
1143
1143
|
return;
|
|
1144
1144
|
}
|
|
1145
|
-
const fragment =
|
|
1145
|
+
const fragment = this.ownerDocument.createDocumentFragment();
|
|
1146
1146
|
while (stagingRoot.firstChild) {
|
|
1147
1147
|
fragment.appendChild(stagingRoot.firstChild);
|
|
1148
1148
|
}
|
|
@@ -1174,6 +1174,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1174
1174
|
}
|
|
1175
1175
|
}
|
|
1176
1176
|
$wire(root, meta, scope) {
|
|
1177
|
+
const ownerDocument = this.ownerDocument;
|
|
1177
1178
|
const instance = {
|
|
1178
1179
|
scope, container: root, nodes: childNodesArray(root),
|
|
1179
1180
|
texts: bindingArray(meta.tx?.length ?? 0),
|
|
@@ -1198,8 +1199,8 @@ export class TemplateElement extends HTMLElement {
|
|
|
1198
1199
|
let node;
|
|
1199
1200
|
let end;
|
|
1200
1201
|
if (raw) {
|
|
1201
|
-
const start =
|
|
1202
|
-
end =
|
|
1202
|
+
const start = ownerDocument.createComment(rawMarker(rawIndex));
|
|
1203
|
+
end = ownerDocument.createComment(rawMarker(rawIndex, true));
|
|
1203
1204
|
rawIndex++;
|
|
1204
1205
|
instance.texts.push({
|
|
1205
1206
|
node: start,
|
|
@@ -1212,7 +1213,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1212
1213
|
node = start;
|
|
1213
1214
|
}
|
|
1214
1215
|
else {
|
|
1215
|
-
const textNode =
|
|
1216
|
+
const textNode = ownerDocument.createTextNode('');
|
|
1216
1217
|
instance.texts.push({ node: textNode, parts, scope });
|
|
1217
1218
|
node = textNode;
|
|
1218
1219
|
}
|
|
@@ -1234,7 +1235,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1234
1235
|
const parent = elements[parentIndex];
|
|
1235
1236
|
if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
|
|
1236
1237
|
continue;
|
|
1237
|
-
const anchor =
|
|
1238
|
+
const anchor = ownerDocument.createComment('');
|
|
1238
1239
|
instance.conds.push({
|
|
1239
1240
|
condition: condition,
|
|
1240
1241
|
blockIndex,
|
|
@@ -1260,7 +1261,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1260
1261
|
const parent = elements[parentIndex];
|
|
1261
1262
|
if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
|
|
1262
1263
|
continue;
|
|
1263
|
-
const anchor =
|
|
1264
|
+
const anchor = ownerDocument.createComment('');
|
|
1264
1265
|
const binding = {
|
|
1265
1266
|
markerId: i, collection, itemVar, blockIndex,
|
|
1266
1267
|
container: parent, start: anchor, end: null,
|
|
@@ -1288,6 +1289,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1288
1289
|
return instance;
|
|
1289
1290
|
}
|
|
1290
1291
|
$hydrate(ssrRoot, meta, tplDom, scope, pathStart = 0) {
|
|
1292
|
+
const ownerDocument = this.ownerDocument;
|
|
1291
1293
|
const instance = {
|
|
1292
1294
|
scope,
|
|
1293
1295
|
container: (pathStart > 0 ? ssrRoot.parentNode : ssrRoot),
|
|
@@ -1351,7 +1353,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1351
1353
|
}
|
|
1352
1354
|
let textNode = previous?.nodeType === 3 ? previous : null;
|
|
1353
1355
|
if (textNode === null) {
|
|
1354
|
-
textNode =
|
|
1356
|
+
textNode = ownerDocument.createTextNode('');
|
|
1355
1357
|
ssrParent.insertBefore(textNode, insertRef);
|
|
1356
1358
|
}
|
|
1357
1359
|
instance.texts.push({ node: textNode, parts, scope });
|
|
@@ -1373,7 +1375,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1373
1375
|
condAnchor = marker;
|
|
1374
1376
|
}
|
|
1375
1377
|
else {
|
|
1376
|
-
condAnchor =
|
|
1378
|
+
condAnchor = ownerDocument.createComment('');
|
|
1377
1379
|
const [, beforeIndex] = slotMeta;
|
|
1378
1380
|
const insertRef = ssrParent.childNodes[beforeIndex ?? ssrParent.childNodes.length] ?? null;
|
|
1379
1381
|
ssrParent.insertBefore(condAnchor, insertRef);
|
|
@@ -1422,7 +1424,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1422
1424
|
anchor = marker;
|
|
1423
1425
|
}
|
|
1424
1426
|
else {
|
|
1425
|
-
anchor =
|
|
1427
|
+
anchor = ownerDocument.createComment('');
|
|
1426
1428
|
const [, beforeIndex] = slotMeta;
|
|
1427
1429
|
const tplParent = tplElements[parentIndex];
|
|
1428
1430
|
const staticCount = tplParent ? tplParent.childNodes.length : 0;
|
|
@@ -1464,7 +1466,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1464
1466
|
else {
|
|
1465
1467
|
const itemParent = itemMarkers[j].parentNode;
|
|
1466
1468
|
const nextBound = j + 1 < itemMarkers.length ? itemMarkers[j + 1] : endMarker;
|
|
1467
|
-
const wrapper =
|
|
1469
|
+
const wrapper = ownerDocument.createElement('div');
|
|
1468
1470
|
let cursor = itemMarkers[j].nextSibling;
|
|
1469
1471
|
while (cursor && cursor !== nextBound) {
|
|
1470
1472
|
const next = cursor.nextSibling;
|
|
@@ -1570,7 +1572,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1570
1572
|
const condNodes = this.$collectConditionalRange(condAnchor);
|
|
1571
1573
|
if (condNodes.length === 0)
|
|
1572
1574
|
return null;
|
|
1573
|
-
const wrapper =
|
|
1575
|
+
const wrapper = this.ownerDocument.createElement('div');
|
|
1574
1576
|
for (let cn = 0; cn < condNodes.length; cn++)
|
|
1575
1577
|
wrapper.appendChild(condNodes[cn]);
|
|
1576
1578
|
const inst = this.$hydrate(wrapper, blockMeta, tplDom, scope);
|
|
@@ -2023,7 +2025,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
2023
2025
|
const first = instance.nodes[0] ?? null;
|
|
2024
2026
|
const container = first?.parentNode;
|
|
2025
2027
|
if (first && container) {
|
|
2026
|
-
const anchor =
|
|
2028
|
+
const anchor = this.ownerDocument.createComment('');
|
|
2027
2029
|
container.insertBefore(anchor, first);
|
|
2028
2030
|
this.$swapOwnedRange(c.owner, instance.nodes, anchor);
|
|
2029
2031
|
c.anchor = anchor;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/webui-framework",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"tag": "latest"
|
|
6
6
|
},
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
"default": "./dist/component-asset.js"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
+
"./component-asset-runtime.js": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/component-asset-runtime.d.ts",
|
|
26
|
+
"default": "./dist/component-asset-runtime.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
23
29
|
"./streaming.js": {
|
|
24
30
|
"import": {
|
|
25
31
|
"types": "./dist/streaming-entry.d.ts",
|
|
@@ -43,7 +49,7 @@
|
|
|
43
49
|
"dist/"
|
|
44
50
|
],
|
|
45
51
|
"devDependencies": {
|
|
46
|
-
"@microsoft/webui-test-support": "0.0
|
|
52
|
+
"@microsoft/webui-test-support": "3.0.0",
|
|
47
53
|
"@playwright/test": "^1.63.0",
|
|
48
54
|
"@types/node": "^26.5.1",
|
|
49
55
|
"typescript": "7.0.2"
|