@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,9 +1,15 @@
1
1
  import { getTemplate, prepareAssetTemplateData, registerTemplateData, } from '../template.js';
2
- import { readComponentAssetModule, sameComponents, validateAsset, } from './asset.js';
3
- import { prepareAssetStyles, readNonce, registerAssetStyles, } from './resources.js';
2
+ import { prepareAssetComponentStyles, readComponentAssetModule, sameComponents, validateAsset, } from './asset.js';
3
+ import { hasRegisteredComponentStyleResource, registerPreparedComponentStyles, sameComponentStyleClosure, sameComponentStyleResource, validateComponentStylesRegistration, } from '../element/styles.js';
4
+ import { prepareComponentStyleLinks, prepareRegisteredLinkStyles, } from '../element/link-styles.js';
4
5
  const assetModulePromises = new Map();
5
- export function loadComponentAsset(tag, url) {
6
- const assetUrl = new URL(url, document.baseURI);
6
+ export function loadComponentAsset(tag, source) {
7
+ if (typeof source === 'function') {
8
+ return Promise.resolve()
9
+ .then(source)
10
+ .then(imported => registerRootAsset(tag, `component asset loader for <${tag}>`, imported));
11
+ }
12
+ const assetUrl = new URL(source, document.baseURI);
7
13
  const href = assetUrl.href;
8
14
  return loadAssetModule(href, () => import(assetUrl.href))
9
15
  .then(imported => registerRootAsset(tag, href, imported));
@@ -17,10 +23,27 @@ async function registerRootAsset(expectedRoot, href, imported) {
17
23
  validateExternalComponents(asset);
18
24
  const root = prepareComponentPayload(asset);
19
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;
20
33
  for (let i = 0; i < chunks.length; i++) {
21
- registerComponentPayload(chunks[i]);
34
+ const ready = chunks[i].linkStyles;
35
+ if (!ready)
36
+ continue;
37
+ (pendingStyles ??= []).push(ready);
22
38
  }
23
- registerComponentPayload(root);
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);
24
47
  }
25
48
  function loadAssetModule(href, load) {
26
49
  let promise = assetModulePromises.get(href);
@@ -57,19 +80,83 @@ function importAndPrepareChunk(assetImport) {
57
80
  });
58
81
  }
59
82
  function prepareComponentPayload(asset) {
83
+ const componentStyles = prepareAssetComponentStyles(asset.componentStyles);
60
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
+ }
61
94
  return {
62
95
  asset,
63
- styles: prepareAssetStyles(asset.templateStyles),
96
+ componentStyles,
97
+ linkStyles,
64
98
  };
65
99
  }
66
- function registerComponentPayload(prepared) {
67
- const { asset, styles } = prepared;
100
+ function registerComponentResources(prepared) {
101
+ registerPreparedComponentStyles(prepared.componentStyles);
102
+ }
103
+ function registerComponentTemplates(prepared) {
104
+ const { asset } = prepared;
68
105
  if (componentsAlreadyRegistered(asset.components))
69
106
  return;
70
- registerAssetStyles(styles, readNonce());
71
107
  registerTemplateData(asset.templates, asset.templateFunctions);
72
- validateRequiredComponents(asset);
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.`);
73
160
  }
74
161
  function componentsAlreadyRegistered(components) {
75
162
  if (components.length === 0)
@@ -91,14 +178,3 @@ function validateExternalComponents(asset) {
91
178
  return;
92
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.`);
93
180
  }
94
- function validateRequiredComponents(asset) {
95
- const missing = [];
96
- for (let i = 0; i < asset.requiredComponents.length; i++) {
97
- const component = asset.requiredComponents[i];
98
- if (!getTemplate(component))
99
- missing.push(component);
100
- }
101
- if (missing.length === 0)
102
- return;
103
- throw new Error(`[WebUI] Component asset is missing required template${missing.length === 1 ? '' : 's'}: ${missing.map(tag => `<${tag}>`).join(', ')}. Ensure the application entry bundle and protocol are loaded before deferred component assets.`);
104
- }
@@ -1,6 +1,7 @@
1
1
  export type ComponentAssetState = Record<string, unknown>;
2
+ export type ComponentAssetSource = string | URL | (() => Promise<unknown>);
2
3
  export interface ComponentAssetManifestEntry<Data extends ComponentAssetState = ComponentAssetState> {
3
- asset: string | URL;
4
+ asset: ComponentAssetSource;
4
5
  module?: () => Promise<unknown>;
5
6
  data?: () => Promise<Data>;
6
7
  }
@@ -1,4 +1,4 @@
1
1
  import type { ComponentAssetManifest, ComponentAssetRegistry } from './component-asset/manifest.js';
2
2
  export type { ComponentAsset, ComponentAssetImport, } from './component-asset/asset.js';
3
- export type { ComponentAssetCreateOptions, ComponentAssetManifest, ComponentAssetManifestEntry, ComponentAssetPreload, ComponentAssetRegistry, ComponentAssetState, } from './component-asset/manifest.js';
3
+ export type { ComponentAssetCreateOptions, ComponentAssetManifest, ComponentAssetManifestEntry, ComponentAssetPreload, ComponentAssetRegistry, ComponentAssetSource, ComponentAssetState, } from './component-asset/manifest.js';
4
4
  export declare function defineComponentAssets(manifest: ComponentAssetManifest): ComponentAssetRegistry;
@@ -1,4 +1,6 @@
1
1
  import { loadComponentAsset } from './component-asset/loader.js';
2
+ import { takeGeneratedComponentAssetStyles } from './component-asset/generated-manifest.js';
3
+ import { preloadComponentAssetStyles } from './element/link-styles.js';
2
4
  export function defineComponentAssets(manifest) {
3
5
  const preloads = new Map();
4
6
  function preload(tag) {
@@ -9,6 +11,10 @@ export function defineComponentAssets(manifest) {
9
11
  if (!entry) {
10
12
  throw new Error(`[WebUI] No component asset manifest entry for <${tag}>.`);
11
13
  }
14
+ const styles = takeGeneratedComponentAssetStyles(tag);
15
+ if (styles) {
16
+ preloadComponentAssetStyles(styles);
17
+ }
12
18
  const next = {
13
19
  asset: loadComponentAsset(tag, entry.asset),
14
20
  };
@@ -18,8 +24,12 @@ export function defineComponentAssets(manifest) {
18
24
  if (entry.data) {
19
25
  next.data = entry.data();
20
26
  }
21
- next.asset.catch(() => { });
22
- next.module?.catch(() => { });
27
+ const evictFailedResources = () => {
28
+ if (preloads.get(tag) === next)
29
+ preloads.delete(tag);
30
+ };
31
+ next.asset.catch(evictFailedResources);
32
+ next.module?.catch(evictFailedResources);
23
33
  next.data?.catch(() => { });
24
34
  preloads.set(tag, next);
25
35
  return next;
@@ -0,0 +1,20 @@
1
+ import type { TemplateBlockMeta, TemplateMeta } from '../template-types.js';
2
+ import type { ComponentStyles } from './styles.js';
3
+ interface ElementBinding {
4
+ readonly element: Element;
5
+ }
6
+ interface DeferredContentCallbacks {
7
+ readonly afterAppend?: () => void;
8
+ readonly beforeAppend?: () => void;
9
+ readonly hasAuthorHydrationLifecycle?: boolean;
10
+ }
11
+ export declare function prepareTemplateLinkStyles(meta: TemplateBlockMeta): Promise<void> | undefined;
12
+ export declare function prepareRegisteredLinkStyles(templates: Record<string, TemplateMeta>): Promise<void> | undefined;
13
+ export declare function prepareComponentStyleLinks(styles: ComponentStyles): Promise<void> | undefined;
14
+ export declare function preloadComponentAssetStyles(hrefs: readonly string[]): Promise<void> | undefined;
15
+ export declare function installComponentLinkStyles(host: HTMLElement, root: ShadowRoot, links: readonly HTMLLinkElement[]): void;
16
+ export declare function templateMayContainLinkStyles(meta: TemplateBlockMeta): boolean;
17
+ export declare function installTemplateLinkStyles(host: HTMLElement, meta: TemplateMeta, shadowRoot: ShadowRoot | null, stagingRoot: ParentNode, bindings: readonly ElementBinding[], deferredCallbacks?: DeferredContentCallbacks): boolean;
18
+ export declare function cancelTemplateLinkStyleMount(root: ShadowRoot): boolean;
19
+ export declare function rewriteCssUrls(cssText: string, stylesheetHref: string): string | undefined;
20
+ export {};