piral-blazor 1.0.0-pre.2296 → 1.0.1-beta.5640

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/LICENSE +1 -1
  2. package/README.md +51 -9
  3. package/convert.d.ts +13 -8
  4. package/convert.js +17 -12
  5. package/esm/converter.d.ts +10 -4
  6. package/esm/converter.js +164 -50
  7. package/esm/converter.js.map +1 -1
  8. package/esm/create.d.ts +21 -1
  9. package/esm/create.js +29 -15
  10. package/esm/create.js.map +1 -1
  11. package/esm/dependencies.d.ts +6 -3
  12. package/esm/dependencies.js +104 -14
  13. package/esm/dependencies.js.map +1 -1
  14. package/esm/events.d.ts +6 -0
  15. package/esm/events.js +145 -0
  16. package/esm/events.js.map +1 -0
  17. package/esm/interop.d.ts +29 -12
  18. package/esm/interop.js +181 -119
  19. package/esm/interop.js.map +1 -1
  20. package/esm/navigation.d.ts +2 -0
  21. package/esm/navigation.js +30 -0
  22. package/esm/navigation.js.map +1 -0
  23. package/esm/types.d.ts +97 -4
  24. package/infra.codegen +32 -23
  25. package/lib/converter.d.ts +10 -4
  26. package/lib/converter.js +164 -50
  27. package/lib/converter.js.map +1 -1
  28. package/lib/create.d.ts +21 -1
  29. package/lib/create.js +31 -17
  30. package/lib/create.js.map +1 -1
  31. package/lib/dependencies.d.ts +6 -3
  32. package/lib/dependencies.js +104 -14
  33. package/lib/dependencies.js.map +1 -1
  34. package/lib/events.d.ts +6 -0
  35. package/lib/events.js +154 -0
  36. package/lib/events.js.map +1 -0
  37. package/lib/index.js +1 -1
  38. package/lib/interop.d.ts +29 -12
  39. package/lib/interop.js +196 -124
  40. package/lib/interop.js.map +1 -1
  41. package/lib/navigation.d.ts +2 -0
  42. package/lib/navigation.js +35 -0
  43. package/lib/navigation.js.map +1 -0
  44. package/lib/types.d.ts +97 -4
  45. package/package.json +25 -9
  46. package/src/converter.ts +233 -63
  47. package/src/create.ts +53 -9
  48. package/src/dependencies.ts +122 -14
  49. package/src/events.ts +174 -0
  50. package/src/interop.ts +228 -117
  51. package/src/navigation.ts +36 -0
  52. package/src/types.ts +115 -4
  53. package/convert.ts +0 -17
@@ -1,24 +1,132 @@
1
- import { addReference } from './interop';
1
+ import type { PiletMetadata } from 'piral-core';
2
+ import { loadBlazorPilet, loadResource, loadResourceWithSymbol, unloadBlazorPilet, unloadResource } from './interop';
2
3
  import type { createConverter } from './converter';
4
+ import type { BlazorDependencyLoader, BlazorRootConfig } from './types';
3
5
 
4
- export function createDependencyLoader(convert: ReturnType<typeof createConverter>, lazy = true) {
5
- let dependency: () => Promise<any>;
6
+ const loadedDependencies = (window.$blazorDependencies ??= []);
7
+ const depsWithPrios = (window.$blazorDependencyPrios ??= []);
8
+
9
+ export function createDependencyLoader(convert: ReturnType<typeof createConverter>) {
10
+ const definedBlazorReferences: Array<string> = [];
11
+ const loadedBlazorPilets: Array<string> = [];
12
+ let dependency: BlazorDependencyLoader;
6
13
 
7
14
  return {
8
15
  getDependency() {
9
16
  return dependency;
10
17
  },
11
- defineBlazorReferences(references) {
12
- const load = () =>
13
- Promise.all(
14
- references.map((reference) =>
15
- fetch(reference)
16
- .then((res) => res.blob())
17
- .then(addReference),
18
- ),
19
- );
20
- let result = !lazy && convert.loader.then(load);
21
- dependency = () => result || (result = load());
18
+ defineBlazorReferences(references: Array<string>, meta: Partial<PiletMetadata> = {}, satellites = {}, prio = 0) {
19
+ prio = Math.max(prio, 0);
20
+
21
+ const depWithPrio = {
22
+ prio,
23
+ load() {
24
+ return Promise.resolve();
25
+ },
26
+ };
27
+
28
+ let result: false | Promise<void> = false;
29
+ const load = async ([_, capabilities]: BlazorRootConfig) => {
30
+ // let others finish first
31
+ await Promise.all(depsWithPrios.filter((m) => m.prio > prio).map((m) => m.load()));
32
+
33
+ window.dispatchEvent(new CustomEvent('loading-blazor-pilet', { detail: meta }));
34
+
35
+ if (capabilities.includes('load')) {
36
+ // new loading mechanism
37
+
38
+ if (!capabilities.includes('language')) {
39
+ satellites = undefined;
40
+ }
41
+
42
+ const dependencies = references.filter((m) => m.endsWith('.dll'));
43
+ const dllUrl = dependencies.pop();
44
+ const piletName = dllUrl.substring(0, dllUrl.length - 4);
45
+ const piletPdb = `${piletName}.pdb`;
46
+ const pdbUrl = references.find((m) => m === piletPdb);
47
+ const id = Math.random().toString(26).substring(2);
48
+
49
+ await loadBlazorPilet(id, {
50
+ name: meta.name || '(unknown)',
51
+ version: meta.version || '0.0.0',
52
+ config: JSON.stringify(meta.config || {}),
53
+ baseUrl: meta.basePath || dllUrl.substring(0, dllUrl.lastIndexOf('/')).replace('/_framework/', '/'),
54
+ dependencies,
55
+ satellites,
56
+ dllUrl,
57
+ pdbUrl,
58
+ });
59
+
60
+ loadedBlazorPilets.push(id);
61
+ } else {
62
+ // old loading mechanism
63
+
64
+ for (const dllUrl of references) {
65
+ const dllName = dllUrl.substring(dllUrl.lastIndexOf('/') + 1);
66
+
67
+ if (dllUrl.endsWith('.dll')) {
68
+ const entry = loadedDependencies.find((m) => m.name === dllName);
69
+
70
+ if (entry) {
71
+ entry.count++;
72
+ await entry.promise;
73
+ } else {
74
+ const urlWithoutExtension = dllUrl.substring(0, dllUrl.length - 4);
75
+ const pdbName = `${urlWithoutExtension}.pdb`;
76
+ const pdbUrl = references.find((m) => m === pdbName);
77
+ const promise = pdbUrl ? loadResourceWithSymbol(dllUrl, pdbUrl) : loadResource(dllUrl);
78
+
79
+ loadedDependencies.push({
80
+ name: dllName,
81
+ url: dllUrl,
82
+ count: 1,
83
+ promise,
84
+ });
85
+
86
+ await promise;
87
+ }
88
+
89
+ definedBlazorReferences.push(dllName);
90
+ }
91
+ }
92
+ }
93
+
94
+ // inform remaining that this one finished
95
+ window.dispatchEvent(new CustomEvent('loaded-blazor-pilet', { detail: meta }));
96
+ };
97
+
98
+ depWithPrio.load = () => {
99
+ if (!result) {
100
+ result = convert.loader.then(load);
101
+ }
102
+
103
+ return result;
104
+ };
105
+ result = !convert.lazy && convert.loader.then(load);
106
+ dependency = (config) => result || (result = load(config));
107
+
108
+ if (prio) {
109
+ depsWithPrios.push(depWithPrio);
110
+ }
111
+ },
112
+ async releaseBlazorReferences() {
113
+ const references = definedBlazorReferences.splice(0, definedBlazorReferences.length);
114
+ const ids = loadedBlazorPilets.splice(0, loadedBlazorPilets.length);
115
+
116
+ // old way of loading
117
+ for (const reference of references) {
118
+ const entry = loadedDependencies.find((m) => m.name === reference);
119
+
120
+ if (--entry.count === 0) {
121
+ loadedDependencies.splice(loadedDependencies.indexOf(entry), 1);
122
+ await unloadResource(entry.url);
123
+ }
124
+ }
125
+
126
+ // new way of loading
127
+ for (const id of ids) {
128
+ await unloadBlazorPilet(id);
129
+ }
22
130
  },
23
131
  };
24
132
  }
package/src/events.ts ADDED
@@ -0,0 +1,174 @@
1
+ import type { ExtensionRegistration } from 'piral-core';
2
+ import { isInternalNavigation, performInternalNavigation } from './navigation';
3
+
4
+ const blazorRootId = 'blazor-root';
5
+ const eventParents: Array<HTMLElement> = [];
6
+
7
+ const globalEventNames = [
8
+ 'abort',
9
+ 'blur',
10
+ 'change',
11
+ 'error',
12
+ 'focus',
13
+ 'load',
14
+ 'loadend',
15
+ 'loadstart',
16
+ 'mouseenter',
17
+ 'mouseleave',
18
+ 'progress',
19
+ 'reset',
20
+ 'scroll',
21
+ 'submit',
22
+ 'unload',
23
+ 'DOMNodeInsertedIntoDocument',
24
+ 'DOMNodeRemovedFromDocument',
25
+ 'click',
26
+ 'dblclick',
27
+ 'mousedown',
28
+ 'mousemove',
29
+ 'mouseup',
30
+ ];
31
+
32
+ const eventNames = {
33
+ render: 'render-blazor-extension',
34
+ navigate: 'navigate-blazor',
35
+ piral: 'piral-blazor',
36
+ };
37
+
38
+ function isRooted(target: HTMLElement) {
39
+ let parent = target.parentElement;
40
+
41
+ while (parent) {
42
+ if (parent.id === blazorRootId) {
43
+ return true;
44
+ }
45
+
46
+ parent = parent.parentElement;
47
+ }
48
+
49
+ return false;
50
+ }
51
+
52
+ function findTarget(target: HTMLElement = document.body) {
53
+ if (eventParents.length === 0) {
54
+ return target;
55
+ } else if (target === document.body) {
56
+ return eventParents[0];
57
+ } else {
58
+ return target;
59
+ }
60
+ }
61
+
62
+ function dispatchToRoot(event: any) {
63
+ isInternalNavigation(event) && performInternalNavigation(event);
64
+
65
+ // the mutation event cannot be cloned (at least in Webkit-based browsers)
66
+ if (!(event instanceof MutationEvent) && !event.processed) {
67
+ const eventClone = new event.constructor(event.type, event);
68
+ document.getElementById(blazorRootId)?.dispatchEvent(eventClone);
69
+ // make sure to only process every event once; even though multiple boundaries might be active
70
+ event.processed = true;
71
+ }
72
+ }
73
+
74
+ export function emitRenderEvent(
75
+ source: HTMLElement,
76
+ name: string,
77
+ params: any,
78
+ sourceRef: any,
79
+ fallbackComponent: string | null,
80
+ ) {
81
+ const target = findTarget(source);
82
+ const empty = typeof fallbackComponent === 'string' ? () => {} : undefined;
83
+ const order =
84
+ typeof sourceRef !== 'undefined'
85
+ ? (elements: Array<ExtensionRegistration>) => {
86
+ const oldItems = elements.map((el, id) => ({
87
+ id,
88
+ pilet: el.pilet,
89
+ defaults: el.defaults ?? {},
90
+ }));
91
+ const newItems: Array<{ id: number }> = sourceRef.invokeMethod('Order', oldItems);
92
+ return newItems.map(({ id }) => elements[id]).filter(Boolean);
93
+ }
94
+ : undefined;
95
+ const eventInit = {
96
+ bubbles: true,
97
+ detail: {
98
+ target,
99
+ props: {
100
+ name,
101
+ params,
102
+ empty,
103
+ order,
104
+ },
105
+ },
106
+ };
107
+ const delayEmit = () =>
108
+ requestAnimationFrame(() => {
109
+ if (!isRooted(target)) {
110
+ target.dispatchEvent(new CustomEvent(eventNames.render, eventInit));
111
+ } else {
112
+ delayEmit();
113
+ }
114
+ });
115
+ delayEmit();
116
+ }
117
+
118
+ export function emitPiralEvent(type: string, args: any) {
119
+ document.body.dispatchEvent(
120
+ new CustomEvent(eventNames.piral, {
121
+ bubbles: false,
122
+ detail: {
123
+ type,
124
+ args,
125
+ },
126
+ }),
127
+ );
128
+ }
129
+
130
+ export function emitNavigateEvent(source: HTMLElement, to: string, replace = false, state?: any) {
131
+ findTarget(source).dispatchEvent(
132
+ new CustomEvent(eventNames.navigate, {
133
+ bubbles: true,
134
+ detail: {
135
+ to,
136
+ replace,
137
+ state,
138
+ },
139
+ }),
140
+ );
141
+ }
142
+
143
+ export function attachEvents(
144
+ host: HTMLElement,
145
+ render: (ev: CustomEvent) => void,
146
+ navigate: (ev: CustomEvent) => void,
147
+ forward: (ev: CustomEvent) => void,
148
+ ) {
149
+ eventParents.push(host);
150
+ host.addEventListener(eventNames.render, render, false);
151
+ host.addEventListener(eventNames.navigate, navigate, false);
152
+
153
+ if (eventParents.length === 1) {
154
+ document.body.addEventListener(eventNames.piral, forward, false);
155
+ }
156
+
157
+ return () => {
158
+ eventParents.splice(eventParents.indexOf(host), 1);
159
+ host.removeEventListener(eventNames.render, render, false);
160
+ host.removeEventListener(eventNames.navigate, navigate, false);
161
+
162
+ if (eventParents.length === 0) {
163
+ document.body.removeEventListener(eventNames.piral, forward, false);
164
+ }
165
+ };
166
+ }
167
+
168
+ export function addGlobalEventListeners(el: HTMLElement) {
169
+ globalEventNames.forEach((eventName) => el.addEventListener(eventName, dispatchToRoot));
170
+ }
171
+
172
+ export function removeGlobalEventListeners(el: HTMLElement) {
173
+ globalEventNames.forEach((eventName) => el.removeEventListener(eventName, dispatchToRoot));
174
+ }