@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.
Files changed (53) hide show
  1. package/README.md +209 -53
  2. package/dist/component-asset/asset.d.ts +4 -2
  3. package/dist/component-asset/asset.js +19 -3
  4. package/dist/component-asset/generated-manifest.d.ts +1 -0
  5. package/dist/component-asset/generated-manifest.js +32 -0
  6. package/dist/component-asset/loader.d.ts +2 -1
  7. package/dist/component-asset/loader.js +98 -22
  8. package/dist/component-asset/manifest.d.ts +2 -1
  9. package/dist/component-asset.d.ts +1 -1
  10. package/dist/component-asset.js +12 -2
  11. package/dist/element/link-styles.d.ts +20 -0
  12. package/dist/element/link-styles.js +1030 -0
  13. package/dist/element/markers.d.ts +4 -0
  14. package/dist/element/markers.js +60 -5
  15. package/dist/element/styles.d.ts +39 -1
  16. package/dist/element/styles.js +623 -13
  17. package/dist/element/types.d.ts +5 -3
  18. package/dist/element.js +9 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +1 -0
  21. package/dist/interaction-hydration.d.ts +8 -0
  22. package/dist/interaction-hydration.js +167 -0
  23. package/dist/static-host.js +43 -1
  24. package/dist/streaming-activation.d.ts +2 -2
  25. package/dist/streaming-activation.js +4 -2
  26. package/dist/streaming-bootstrap.d.ts +2 -2
  27. package/dist/streaming-bootstrap.js +7 -1
  28. package/dist/streaming-cleanup.d.ts +1 -0
  29. package/dist/streaming-cleanup.js +66 -14
  30. package/dist/streaming-coordinator.d.ts +3 -2
  31. package/dist/streaming-coordinator.js +107 -47
  32. package/dist/streaming-deferred.d.ts +14 -4
  33. package/dist/streaming-deferred.js +200 -87
  34. package/dist/streaming-dom.d.ts +12 -3
  35. package/dist/streaming-dom.js +41 -27
  36. package/dist/streaming-mode.d.ts +5 -0
  37. package/dist/streaming-mode.js +4 -0
  38. package/dist/streaming-protocol.d.ts +19 -3
  39. package/dist/streaming-protocol.js +4 -3
  40. package/dist/streaming-spans.d.ts +9 -0
  41. package/dist/streaming-spans.js +200 -0
  42. package/dist/template-content.d.ts +17 -0
  43. package/dist/template-content.js +106 -0
  44. package/dist/template-element.d.ts +17 -2
  45. package/dist/template-element.js +493 -177
  46. package/dist/template-events.d.ts +7 -0
  47. package/dist/template-events.js +7 -1
  48. package/dist/template-types.d.ts +1 -2
  49. package/dist/template.d.ts +7 -1
  50. package/dist/template.js +79 -16
  51. package/package.json +9 -3
  52. package/dist/component-asset/resources.d.ts +0 -4
  53. package/dist/component-asset/resources.js +0 -83
@@ -1,17 +1,627 @@
1
- const headInjected = new Set();
2
- export function injectModuleStyle(specifier, shadowRoot) {
3
- if (shadowRoot) {
4
- if (shadowRoot.adoptedStyleSheets.length > 0)
5
- return;
6
- import(specifier, { with: { type: 'css' } }).then((mod) => {
7
- shadowRoot.adoptedStyleSheets.push(mod.default);
8
- }, () => {
1
+ import { installComponentLinkStyles } from './link-styles.js';
2
+ const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
3
+ const catalogs = new WeakMap();
4
+ const installed = new WeakMap();
5
+ const completedClosures = new WeakMap();
6
+ const pending = new WeakMap();
7
+ const ssrMarkers = new WeakMap();
8
+ const resourceMarkers = new WeakMap();
9
+ const moduleAdoption = new WeakMap();
10
+ const moduleImportsInstalled = new WeakMap();
11
+ const moduleImportsSeeded = new WeakSet();
12
+ const defaultCssModuleLoader = (specifier) => import(specifier, { with: { type: 'css' } });
13
+ let loadCssModule = defaultCssModuleLoader;
14
+ export function setCssModuleLoaderForTests(loader) {
15
+ loadCssModule = loader ?? defaultCssModuleLoader;
16
+ }
17
+ function catalogFor(document) {
18
+ let catalog = catalogs.get(document);
19
+ if (!catalog) {
20
+ catalog = {
21
+ resources: new Map(),
22
+ closures: new Map(),
23
+ };
24
+ catalogs.set(document, catalog);
25
+ }
26
+ return catalog;
27
+ }
28
+ function isDocument(target) {
29
+ return target.nodeType === 9;
30
+ }
31
+ function owningDocument(target) {
32
+ return isDocument(target) ? target : target.ownerDocument;
33
+ }
34
+ function isObject(value) {
35
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
36
+ }
37
+ function styleGlobal(owner) {
38
+ const configured = owner.defaultView?.__webui;
39
+ if (configured || owner !== globalThis.document || typeof window === 'undefined') {
40
+ return configured;
41
+ }
42
+ return window.__webui;
43
+ }
44
+ function readNonce(target = document) {
45
+ const owner = owningDocument(target);
46
+ const nonce = styleGlobal(owner)?.nonce;
47
+ if (nonce)
48
+ return nonce;
49
+ const meta = owner.querySelector('meta[name="webui-nonce"]');
50
+ return meta?.content ?? '';
51
+ }
52
+ export function sameComponentStyleResource(current, next) {
53
+ if (current === next)
54
+ return true;
55
+ const currentMembers = current.members;
56
+ const nextMembers = next.members;
57
+ if (currentMembers?.length !== nextMembers?.length ||
58
+ currentMembers?.some((member, index) => member !== nextMembers?.[index])) {
59
+ return false;
60
+ }
61
+ switch (current.kind) {
62
+ case 'link':
63
+ return next.kind === 'link' && current.href === next.href;
64
+ case 'style':
65
+ return next.kind === 'style' && current.css === next.css;
66
+ case 'module':
67
+ return next.kind === 'module' &&
68
+ current.specifier === next.specifier &&
69
+ current.css === next.css;
70
+ }
71
+ }
72
+ export function sameComponentStyleClosure(current, next) {
73
+ return current === next ||
74
+ (current.length === next.length && current.every((id, index) => id === next[index]));
75
+ }
76
+ export function prepareComponentStyles(value) {
77
+ if (value === undefined)
78
+ return undefined;
79
+ if (!isObject(value) || value.version !== 1) {
80
+ throw new Error('[WebUI] componentStyles must use version 1.');
81
+ }
82
+ if (value.strategy !== 'link' && value.strategy !== 'style' && value.strategy !== 'module') {
83
+ throw new Error('[WebUI] componentStyles strategy must be "link", "style", or "module".');
84
+ }
85
+ if (!isObject(value.resources) || !isObject(value.closures)) {
86
+ throw new Error('[WebUI] componentStyles resources and closures must be objects.');
87
+ }
88
+ const strategy = value.strategy;
89
+ const resources = {};
90
+ for (const id of Object.keys(value.resources)) {
91
+ const resource = value.resources[id];
92
+ if (!id || !isObject(resource) || resource.kind !== strategy) {
93
+ throw new Error(`[WebUI] Invalid component style resource "${id}".`);
94
+ }
95
+ let members;
96
+ if (resource.members !== undefined) {
97
+ if (!Array.isArray(resource.members) ||
98
+ resource.members.length < 2 ||
99
+ resource.members.some(member => typeof member !== 'string' || !member) ||
100
+ new Set(resource.members).size !== resource.members.length) {
101
+ throw new Error(`[WebUI] Invalid component style resource members "${id}".`);
102
+ }
103
+ members = [...resource.members];
104
+ }
105
+ if (resource.kind === 'link' && typeof resource.href === 'string' && resource.href) {
106
+ resources[id] = members
107
+ ? { kind: 'link', href: resource.href, members }
108
+ : { kind: 'link', href: resource.href };
109
+ }
110
+ else if (resource.kind === 'style' && typeof resource.css === 'string') {
111
+ resources[id] = members
112
+ ? { kind: 'style', css: resource.css, members }
113
+ : { kind: 'style', css: resource.css };
114
+ }
115
+ else if (resource.kind === 'module' &&
116
+ typeof resource.specifier === 'string' && resource.specifier &&
117
+ typeof resource.css === 'string') {
118
+ resources[id] = members
119
+ ? { kind: 'module', specifier: resource.specifier, css: resource.css, members }
120
+ : { kind: 'module', specifier: resource.specifier, css: resource.css };
121
+ }
122
+ else {
123
+ throw new Error(`[WebUI] Invalid component style resource "${id}".`);
124
+ }
125
+ }
126
+ const closures = {};
127
+ for (const root of Object.keys(value.closures)) {
128
+ const closure = value.closures[root];
129
+ if (!root || !Array.isArray(closure)) {
130
+ throw new Error(`[WebUI] Invalid component style closure "${root}".`);
131
+ }
132
+ closures[root] = closure.map((id) => {
133
+ if (typeof id !== 'string' || !id) {
134
+ throw new Error(`[WebUI] Invalid resource ID in component style closure "${root}".`);
135
+ }
136
+ return id;
9
137
  });
10
138
  }
11
- else if (!headInjected.has(specifier)) {
12
- headInjected.add(specifier);
13
- import(specifier, { with: { type: 'css' } }).then((mod) => {
14
- document.adoptedStyleSheets.push(mod.default);
15
- }, () => { });
139
+ return { version: 1, strategy, resources, closures };
140
+ }
141
+ export function requireComponentStyles(value) {
142
+ const styles = prepareComponentStyles(value);
143
+ if (!styles) {
144
+ throw new Error('[WebUI] componentStyles is required.');
145
+ }
146
+ return styles;
147
+ }
148
+ export function validateComponentStylesRegistration(styles, document = globalThis.document) {
149
+ if (!styles)
150
+ return;
151
+ const catalog = catalogFor(document);
152
+ for (const id of Object.keys(styles.resources)) {
153
+ const current = catalog.resources.get(id);
154
+ if (current && !sameComponentStyleResource(current, styles.resources[id])) {
155
+ throw new Error(`[WebUI] Conflicting component style resource "${id}".`);
156
+ }
157
+ }
158
+ for (const root of Object.keys(styles.closures)) {
159
+ const current = catalog.closures.get(root);
160
+ const next = styles.closures[root];
161
+ if (current && !sameComponentStyleClosure(current, next)) {
162
+ throw new Error(`[WebUI] Conflicting component style closure "${root}".`);
163
+ }
164
+ }
165
+ }
166
+ export function registerPreparedComponentStyles(styles, document = globalThis.document) {
167
+ const catalog = catalogFor(document);
168
+ for (const id of Object.keys(styles.resources)) {
169
+ if (catalog.resources.has(id))
170
+ continue;
171
+ const resource = styles.resources[id];
172
+ catalog.resources.set(id, resource);
173
+ }
174
+ for (const root of Object.keys(styles.closures)) {
175
+ if (!catalog.closures.has(root)) {
176
+ catalog.closures.set(root, styles.closures[root]);
177
+ }
178
+ }
179
+ }
180
+ export function registerComponentStyles(value, document = globalThis.document) {
181
+ const styles = requireComponentStyles(value);
182
+ validateComponentStylesRegistration(styles, document);
183
+ registerPreparedComponentStyles(styles, document);
184
+ }
185
+ export function hasRegisteredComponentStyleResource(id, document = globalThis.document) {
186
+ return catalogFor(document).resources.has(id);
187
+ }
188
+ export function hasComponentStyleWork(rootId, document = globalThis.document) {
189
+ return (catalogFor(document).closures.get(rootId)?.length ?? 0) > 0;
190
+ }
191
+ function targetInstalledResources(target) {
192
+ let targetInstalled = installed.get(target);
193
+ if (!targetInstalled) {
194
+ targetInstalled = new Set();
195
+ installed.set(target, targetInstalled);
196
+ }
197
+ return targetInstalled;
198
+ }
199
+ function markResourceInstalled(targetInstalled, id, resource) {
200
+ targetInstalled.add(id);
201
+ const members = resource.members;
202
+ if (!members)
203
+ return;
204
+ for (let i = 0; i < members.length; i++)
205
+ targetInstalled.add(members[i]);
206
+ }
207
+ function isMatchingResourceMarker(candidate, resource) {
208
+ const strategy = candidate.getAttribute('data-webui-strategy');
209
+ if (strategy !== null && strategy !== resource.kind)
210
+ return false;
211
+ return resource.kind === 'link'
212
+ ? candidate.localName === 'link'
213
+ : candidate.localName === 'style';
214
+ }
215
+ export function isComponentStyleMarker(element) {
216
+ if (typeof element.getAttribute !== 'function')
217
+ return false;
218
+ if (element.getAttribute('data-webui-resource') === null)
219
+ return false;
220
+ const strategy = element.getAttribute('data-webui-strategy');
221
+ return (element.localName === 'link' && strategy === 'link') ||
222
+ (element.localName === 'style' && (strategy === 'style' || strategy === 'module'));
223
+ }
224
+ export function isComponentStyleResourceMarker(element) {
225
+ if (isComponentStyleMarker(element))
226
+ return true;
227
+ return element.localName === 'script' &&
228
+ element.getAttribute('type') === 'importmap' &&
229
+ element.getAttribute('data-webui-resource') !== null;
230
+ }
231
+ function scanSsrMarkerElements(candidates, catalog, targetInstalled, state) {
232
+ for (let i = 0; i < candidates.length; i++) {
233
+ const candidate = candidates[i];
234
+ const id = candidate.getAttribute('data-webui-resource');
235
+ if (id === null)
236
+ continue;
237
+ const resource = catalog.resources.get(id);
238
+ if (!resource || !isMatchingResourceMarker(candidate, resource))
239
+ continue;
240
+ if (resource.kind !== 'module') {
241
+ markResourceInstalled(targetInstalled, id, resource);
242
+ continue;
243
+ }
244
+ let moduleElements = state.moduleElements;
245
+ let moduleIds = state.moduleIds;
246
+ if (!moduleElements || !moduleIds) {
247
+ moduleElements = new Map();
248
+ moduleIds = [];
249
+ state.moduleElements = moduleElements;
250
+ state.moduleIds = moduleIds;
251
+ }
252
+ if (!moduleElements.has(id)) {
253
+ moduleElements.set(id, candidate);
254
+ moduleIds.push(id);
255
+ }
256
+ }
257
+ }
258
+ function ensureSsrMarkers(target, catalog, targetInstalled) {
259
+ let state = ssrMarkers.get(target);
260
+ if (state)
261
+ return state;
262
+ state = {};
263
+ ssrMarkers.set(target, state);
264
+ const candidates = target.querySelectorAll?.('link[data-webui-resource],style[data-webui-resource]');
265
+ if (candidates) {
266
+ scanSsrMarkerElements(candidates, catalog, targetInstalled, state);
267
+ }
268
+ return state;
269
+ }
270
+ export function claimSsrComponentStyles(source, target) {
271
+ const catalog = catalogFor(owningDocument(target));
272
+ const targetInstalled = targetInstalledResources(target);
273
+ const state = ensureSsrMarkers(target, catalog, targetInstalled);
274
+ const route = source.parentElement;
275
+ if (route?.localName === 'webui-route') {
276
+ scanSsrMarkerElements(route.children, catalog, targetInstalled, state);
277
+ }
278
+ }
279
+ function markerScope(target) {
280
+ return isDocument(target) ? target.head : target;
281
+ }
282
+ function directResourceMarkers(target, catalog) {
283
+ const scope = markerScope(target);
284
+ const cached = resourceMarkers.get(target);
285
+ if (cached && cached.count === scope.childElementCount) {
286
+ return cached.markers.size > 0 ? cached.markers : undefined;
287
+ }
288
+ const candidates = scope.children;
289
+ const markers = new Map();
290
+ for (let i = 0; i < candidates.length; i++) {
291
+ const id = candidates[i].getAttribute('data-webui-resource');
292
+ if (id === null)
293
+ continue;
294
+ const resource = catalog.resources.get(id);
295
+ if (!resource || !isMatchingResourceMarker(candidates[i], resource))
296
+ continue;
297
+ if (!markers.has(id))
298
+ markers.set(id, candidates[i]);
299
+ }
300
+ resourceMarkers.set(target, { count: candidates.length, markers });
301
+ return markers.size > 0 ? markers : undefined;
302
+ }
303
+ function noteMarkerInserted(target, id, element) {
304
+ const cached = resourceMarkers.get(target);
305
+ if (!cached)
306
+ return;
307
+ cached.count++;
308
+ if (!cached.markers.has(id))
309
+ cached.markers.set(id, element);
310
+ }
311
+ function appendResource(target, id, resource, before) {
312
+ const document = owningDocument(target);
313
+ const parent = isDocument(target) ? target.head : target;
314
+ if (resource.kind === 'link') {
315
+ const link = document.createElement('link');
316
+ link.rel = 'stylesheet';
317
+ link.href = resource.href;
318
+ link.setAttribute('data-webui-resource', id);
319
+ link.setAttribute('data-webui-strategy', 'link');
320
+ parent.insertBefore(link, before ?? null);
321
+ noteMarkerInserted(target, id, link);
322
+ return link;
323
+ }
324
+ if (resource.kind === 'style') {
325
+ const style = document.createElement('style');
326
+ const nonce = readNonce(target);
327
+ if (nonce)
328
+ style.nonce = nonce;
329
+ style.textContent = resource.css;
330
+ style.setAttribute('data-webui-resource', id);
331
+ style.setAttribute('data-webui-strategy', 'style');
332
+ parent.insertBefore(style, before ?? null);
333
+ noteMarkerInserted(target, id, style);
334
+ return style;
335
+ }
336
+ throw new Error(`[WebUI] Module resource "${id}" cannot be installed as an element.`);
337
+ }
338
+ function seededModuleSpecifiers(owner) {
339
+ let installedSpecifiers = moduleImportsInstalled.get(owner);
340
+ if (!installedSpecifiers) {
341
+ installedSpecifiers = new Set();
342
+ moduleImportsInstalled.set(owner, installedSpecifiers);
343
+ }
344
+ if (moduleImportsSeeded.has(owner))
345
+ return installedSpecifiers;
346
+ moduleImportsSeeded.add(owner);
347
+ const styles = styleGlobal(owner)?.styles;
348
+ if (!styles)
349
+ return installedSpecifiers;
350
+ for (let i = 0; i < styles.length; i++) {
351
+ installedSpecifiers.add(styles[i]);
352
+ }
353
+ return installedSpecifiers;
354
+ }
355
+ function installModuleImportMap(target, specifier, css) {
356
+ const owner = owningDocument(target);
357
+ const installedSpecifiers = seededModuleSpecifiers(owner);
358
+ if (installedSpecifiers.has(specifier))
359
+ return;
360
+ const script = owner.createElement('script');
361
+ script.type = 'importmap';
362
+ const nonce = readNonce(target);
363
+ if (nonce)
364
+ script.nonce = nonce;
365
+ script.textContent = JSON.stringify({
366
+ imports: { [specifier]: `data:text/css,${encodeURIComponent(css)}` },
367
+ });
368
+ owner.head.appendChild(script);
369
+ installedSpecifiers.add(specifier);
370
+ }
371
+ function loadModule(target, id, resource) {
372
+ let targetPending = pending.get(target);
373
+ if (!targetPending) {
374
+ targetPending = new Map();
375
+ pending.set(target, targetPending);
376
+ }
377
+ const existing = targetPending.get(id);
378
+ if (existing)
379
+ return existing;
380
+ installModuleImportMap(target, resource.specifier, resource.css);
381
+ const loads = targetPending;
382
+ const promise = loadCssModule(resource.specifier)
383
+ .then((module) => module.default);
384
+ loads.set(id, promise);
385
+ void promise.catch(() => {
386
+ if (loads.get(id) === promise)
387
+ loads.delete(id);
388
+ });
389
+ return promise;
390
+ }
391
+ function markClosureComplete(target, rootId) {
392
+ let roots = completedClosures.get(target);
393
+ if (!roots) {
394
+ roots = new Set();
395
+ completedClosures.set(target, roots);
396
+ }
397
+ roots.add(rootId);
398
+ }
399
+ function installElementRange(target, catalog, closure, targetInstalled, start, end, before, linkElements) {
400
+ for (let i = start; i < end; i++) {
401
+ const id = closure[i];
402
+ if (targetInstalled.has(id))
403
+ continue;
404
+ const resource = catalog.resources.get(id);
405
+ if (!resource || resource.kind === 'module')
406
+ continue;
407
+ const element = appendResource(target, id, resource, before);
408
+ if (linkElements && resource.kind === 'link') {
409
+ linkElements.push(element);
410
+ }
411
+ markResourceInstalled(targetInstalled, id, resource);
412
+ }
413
+ }
414
+ function warnOnContradictoryOrder(rootId, closure, markers) {
415
+ let previousId;
416
+ let previous;
417
+ for (let i = 0; i < closure.length; i++) {
418
+ const marker = markers.get(closure[i]);
419
+ if (!marker)
420
+ continue;
421
+ if (previous &&
422
+ typeof previous.compareDocumentPosition === 'function' &&
423
+ (previous.compareDocumentPosition(marker) & 4) === 0) {
424
+ console.warn(`[WebUI] Closure "${rootId}" wants "${previousId}" before "${closure[i]}", but they are already installed in the opposite order. The first closure to install decides, so these rules cascade against this closure's intent.`);
425
+ return;
426
+ }
427
+ previousId = closure[i];
428
+ previous = marker;
429
+ }
430
+ }
431
+ function installElements(target, catalog, closure, targetInstalled, markers, rootId, linkElements) {
432
+ let segmentEnd = closure.length;
433
+ let before;
434
+ if (markers) {
435
+ if (DEV)
436
+ warnOnContradictoryOrder(rootId, closure, markers);
437
+ for (let i = closure.length - 1; i >= 0; i--) {
438
+ const marker = markers.get(closure[i]);
439
+ if (!marker)
440
+ continue;
441
+ installElementRange(target, catalog, closure, targetInstalled, i + 1, segmentEnd, before, linkElements);
442
+ const resource = catalog.resources.get(closure[i]);
443
+ if (resource && resource.kind !== 'module') {
444
+ markResourceInstalled(targetInstalled, closure[i], resource);
445
+ }
446
+ before = marker;
447
+ segmentEnd = i;
448
+ }
449
+ }
450
+ installElementRange(target, catalog, closure, targetInstalled, 0, segmentEnd, before, linkElements);
451
+ }
452
+ function moduleAdoptionState(target) {
453
+ let state = moduleAdoption.get(target);
454
+ if (!state) {
455
+ state = { order: [], reserved: new Set(), sheets: new Map() };
456
+ moduleAdoption.set(target, state);
457
+ }
458
+ return state;
459
+ }
460
+ function reserveModuleSlot(target, id) {
461
+ const state = moduleAdoptionState(target);
462
+ if (state.reserved.has(id))
463
+ return;
464
+ state.reserved.add(id);
465
+ state.order.push(id);
466
+ }
467
+ function applyModuleOrder(target, state) {
468
+ const adopted = target.adoptedStyleSheets;
469
+ const owned = new Set(state.sheets.values());
470
+ const next = [];
471
+ for (let i = 0; i < adopted.length; i++) {
472
+ if (!owned.has(adopted[i]))
473
+ next.push(adopted[i]);
474
+ }
475
+ const seen = new Set();
476
+ for (let i = 0; i < state.order.length; i++) {
477
+ const sheet = state.sheets.get(state.order[i]);
478
+ if (!sheet || seen.has(sheet))
479
+ continue;
480
+ seen.add(sheet);
481
+ next.push(sheet);
482
+ }
483
+ if (next.length === adopted.length) {
484
+ let same = true;
485
+ for (let i = 0; i < next.length; i++) {
486
+ if (next[i] !== adopted[i]) {
487
+ same = false;
488
+ break;
489
+ }
490
+ }
491
+ if (same)
492
+ return;
493
+ }
494
+ target.adoptedStyleSheets = next;
495
+ }
496
+ function commitModules(target, ids, sheets, markers, targetInstalled) {
497
+ const state = moduleAdoptionState(target);
498
+ const catalog = catalogFor(owningDocument(target));
499
+ for (let i = 0; i < sheets.length; i++) {
500
+ const id = ids[i];
501
+ if (!state.reserved.has(id)) {
502
+ state.reserved.add(id);
503
+ state.order.push(id);
504
+ }
505
+ state.sheets.set(id, sheets[i]);
506
+ }
507
+ applyModuleOrder(target, state);
508
+ const targetPending = pending.get(target);
509
+ for (let i = 0; i < sheets.length; i++) {
510
+ const id = ids[i];
511
+ const resource = catalog.resources.get(id);
512
+ if (resource)
513
+ markResourceInstalled(targetInstalled, id, resource);
514
+ else
515
+ targetInstalled.add(id);
516
+ targetPending?.delete(id);
517
+ const marker = markers?.get(id);
518
+ if (marker) {
519
+ marker.remove();
520
+ markers?.delete(id);
521
+ }
522
+ }
523
+ if (targetPending?.size === 0)
524
+ pending.delete(target);
525
+ }
526
+ async function finishModuleLoads(target, rootId, ids, loads, markers, targetInstalled) {
527
+ const sheets = [];
528
+ try {
529
+ for (let i = 0; i < loads.length; i++) {
530
+ sheets.push(await loads[i]);
531
+ }
532
+ }
533
+ catch (error) {
534
+ if (sheets.length > 0) {
535
+ commitModules(target, ids, sheets, markers, targetInstalled);
536
+ }
537
+ throw error;
538
+ }
539
+ commitModules(target, ids, sheets, markers, targetInstalled);
540
+ markClosureComplete(target, rootId);
541
+ }
542
+ export function installComponentStyles(rootId, target, host) {
543
+ const catalog = catalogFor(owningDocument(target));
544
+ const closure = catalog.closures.get(rootId);
545
+ if (!closure || completedClosures.get(target)?.has(rootId))
546
+ return undefined;
547
+ const targetInstalled = targetInstalledResources(target);
548
+ const markerState = ensureSsrMarkers(target, catalog, targetInstalled);
549
+ let hasElementWork = false;
550
+ let hasClosureModuleWork = false;
551
+ for (let i = 0; i < closure.length; i++) {
552
+ const id = closure[i];
553
+ if (targetInstalled.has(id))
554
+ continue;
555
+ const resource = catalog.resources.get(id);
556
+ if (!resource) {
557
+ throw new Error(`[WebUI] Missing component style resource "${id}" for closure "${rootId}".`);
558
+ }
559
+ if (resource.kind === 'module')
560
+ hasClosureModuleWork = true;
561
+ else
562
+ hasElementWork = true;
563
+ }
564
+ let hasSsrModuleWork = false;
565
+ const ssrModuleIds = markerState.moduleIds;
566
+ if (ssrModuleIds) {
567
+ for (let i = 0; i < ssrModuleIds.length; i++) {
568
+ if (!targetInstalled.has(ssrModuleIds[i])) {
569
+ hasSsrModuleWork = true;
570
+ break;
571
+ }
572
+ }
573
+ }
574
+ if (!hasElementWork && !hasClosureModuleWork && !hasSsrModuleWork) {
575
+ markClosureComplete(target, rootId);
576
+ return undefined;
577
+ }
578
+ if (hasElementWork) {
579
+ const linkElements = [];
580
+ installElements(target, catalog, closure, targetInstalled, directResourceMarkers(target, catalog), rootId, linkElements);
581
+ if (host && !isDocument(target)) {
582
+ installComponentLinkStyles(host, target, linkElements);
583
+ }
584
+ }
585
+ let moduleIds;
586
+ let moduleLoads;
587
+ let queuedModules;
588
+ const queueModule = (id, resource) => {
589
+ if (!moduleIds || !moduleLoads || !queuedModules) {
590
+ moduleIds = [];
591
+ moduleLoads = [];
592
+ queuedModules = new Set();
593
+ }
594
+ reserveModuleSlot(target, id);
595
+ moduleIds.push(id);
596
+ moduleLoads.push(loadModule(target, id, resource));
597
+ queuedModules.add(id);
598
+ };
599
+ if (ssrModuleIds) {
600
+ for (let i = 0; i < ssrModuleIds.length; i++) {
601
+ const id = ssrModuleIds[i];
602
+ if (targetInstalled.has(id))
603
+ continue;
604
+ const resource = catalog.resources.get(id);
605
+ if (!resource || resource.kind !== 'module')
606
+ continue;
607
+ queueModule(id, resource);
608
+ }
609
+ }
610
+ for (let i = 0; i < closure.length; i++) {
611
+ const id = closure[i];
612
+ if (targetInstalled.has(id) || queuedModules?.has(id))
613
+ continue;
614
+ const resource = catalog.resources.get(id);
615
+ if (!resource) {
616
+ throw new Error(`[WebUI] Missing component style resource "${id}" for closure "${rootId}".`);
617
+ }
618
+ if (resource.kind === 'module') {
619
+ queueModule(id, resource);
620
+ }
621
+ }
622
+ if (!moduleLoads || !moduleIds) {
623
+ markClosureComplete(target, rootId);
624
+ return undefined;
16
625
  }
626
+ return finishModuleLoads(target, rootId, moduleIds, moduleLoads, markerState.moduleElements, targetInstalled);
17
627
  }
@@ -1,11 +1,13 @@
1
1
  import type { CompiledAttrPart, CompiledCondition } from '../template.js';
2
2
  export interface TextBinding {
3
- node: Text;
3
+ node: CharacterData;
4
4
  path?: string;
5
5
  parts?: CompiledAttrPart[];
6
6
  scope?: ScopeFrame;
7
7
  raw?: boolean;
8
- rawParent?: Element;
8
+ rawEnd?: Comment;
9
+ rawValue?: string;
10
+ rawOwner?: TemplateInstance;
9
11
  }
10
12
  export declare const ATTR_KIND_ATTRIBUTE = 0;
11
13
  export declare const ATTR_KIND_COMPLEX = 1;
@@ -41,7 +43,7 @@ export interface TemplateInstance {
41
43
  export interface CondBinding {
42
44
  condition: CompiledCondition;
43
45
  blockIndex: number;
44
- anchor: Comment;
46
+ anchor: Comment | null;
45
47
  scope?: ScopeFrame;
46
48
  owner: TemplateInstance;
47
49
  instance: TemplateInstance | null;
package/dist/element.js CHANGED
@@ -5,6 +5,8 @@ import { attributeNameForProperty, getObservableNames, syncAttrProperties, } fro
5
5
  const HYDRATE_ATTR = 'w-hydrate';
6
6
  const RENDER_ATTR = 'w-render';
7
7
  const HYDRATE_EAGER = 'eager';
8
+ const INTERACTION_HYDRATION = 3;
9
+ const LAZY_RENDER_INTERACTION = 4;
8
10
  const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
9
11
  let warnedMissingLazyHydrationEntry = false;
10
12
  function warnMissingLazyHydrationEntry(tag, policy) {
@@ -63,6 +65,12 @@ export class WebUIElement extends TemplateElement {
63
65
  const policy = meta?.wp;
64
66
  if (policy === undefined)
65
67
  return undefined;
68
+ if (policy === INTERACTION_HYDRATION
69
+ || policy === LAZY_RENDER_INTERACTION)
70
+ return undefined;
71
+ if (policy !== LAZY_HYDRATION_VIEWPORT
72
+ && policy !== LAZY_HYDRATION_CONTENT_VISIBILITY)
73
+ return undefined;
66
74
  if (policy === LAZY_HYDRATION_CONTENT_VISIBILITY &&
67
75
  this.getAttribute(RENDER_ATTR) === HYDRATE_EAGER)
68
76
  return undefined;
@@ -101,7 +109,7 @@ export class WebUIElement extends TemplateElement {
101
109
  return this.dispatchEvent(new CustomEvent(name, {
102
110
  bubbles: true,
103
111
  cancelable: true,
104
- composed: !!this.shadowRoot,
112
+ composed: true,
105
113
  detail,
106
114
  }));
107
115
  }