piral-ng 1.0.0-pre.2196 → 1.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/LICENSE +1 -1
- package/README.md +701 -42
- package/common/NgExtension.d.ts +14 -0
- package/common/ResourceUrlPipe.d.ts +10 -0
- package/common/SharedModule.d.ts +10 -0
- package/common/esm2022/NgExtension.mjs +47 -0
- package/common/esm2022/ResourceUrlPipe.mjs +23 -0
- package/common/esm2022/SharedModule.mjs +25 -0
- package/common/esm2022/piral-ng-common.mjs +5 -0
- package/common/esm2022/public_api.mjs +4 -0
- package/common/fesm2022/piral-ng-common.mjs +93 -0
- package/common/fesm2022/piral-ng-common.mjs.map +1 -0
- package/common/index.d.ts +1 -0
- package/common/package.json +35 -0
- package/common/piral-ng-common.js +3 -0
- package/common/public_api.d.ts +3 -0
- package/convert.d.ts +17 -7
- package/convert.js +17 -11
- package/esm/RoutingService.d.ts +13 -0
- package/esm/RoutingService.js +109 -0
- package/esm/RoutingService.js.map +1 -0
- package/esm/bootstrap.d.ts +6 -5
- package/esm/bootstrap.js +52 -87
- package/esm/bootstrap.js.map +1 -1
- package/esm/common/NgExtension.d.ts +11 -0
- package/esm/common/NgExtension.js +41 -0
- package/esm/common/NgExtension.js.map +1 -0
- package/esm/common/ResourceUrlPipe.d.ts +7 -0
- package/esm/common/ResourceUrlPipe.js +18 -0
- package/esm/common/ResourceUrlPipe.js.map +1 -0
- package/esm/common/SharedModule.d.ts +3 -0
- package/esm/common/SharedModule.js +21 -0
- package/esm/common/SharedModule.js.map +1 -0
- package/esm/common/public_api.d.ts +3 -0
- package/esm/common/public_api.js +4 -0
- package/esm/common/public_api.js.map +1 -0
- package/esm/converter.d.ts +10 -2
- package/esm/converter.js +35 -16
- package/esm/converter.js.map +1 -1
- package/esm/create.d.ts +2 -16
- package/esm/create.js +9 -13
- package/esm/create.js.map +1 -1
- package/esm/module.d.ts +24 -0
- package/esm/module.js +131 -0
- package/esm/module.js.map +1 -0
- package/esm/queue.d.ts +1 -1
- package/esm/queue.js +3 -3
- package/esm/queue.js.map +1 -1
- package/esm/startup.d.ts +8 -0
- package/esm/startup.js +115 -0
- package/esm/startup.js.map +1 -0
- package/esm/types.d.ts +71 -3
- package/esm/utils.d.ts +18 -0
- package/esm/utils.js +46 -0
- package/esm/utils.js.map +1 -0
- package/extend-webpack.js +107 -0
- package/lib/RoutingService.d.ts +13 -0
- package/lib/RoutingService.js +112 -0
- package/lib/RoutingService.js.map +1 -0
- package/lib/bootstrap.d.ts +6 -5
- package/lib/bootstrap.js +55 -91
- package/lib/bootstrap.js.map +1 -1
- package/lib/common/NgExtension.d.ts +11 -0
- package/lib/common/NgExtension.js +44 -0
- package/lib/common/NgExtension.js.map +1 -0
- package/lib/common/ResourceUrlPipe.d.ts +7 -0
- package/lib/common/ResourceUrlPipe.js +21 -0
- package/lib/common/ResourceUrlPipe.js.map +1 -0
- package/lib/common/SharedModule.d.ts +3 -0
- package/lib/common/SharedModule.js +24 -0
- package/lib/common/SharedModule.js.map +1 -0
- package/lib/common/public_api.d.ts +3 -0
- package/lib/common/public_api.js +7 -0
- package/lib/common/public_api.js.map +1 -0
- package/lib/converter.d.ts +10 -2
- package/lib/converter.js +36 -17
- package/lib/converter.js.map +1 -1
- package/lib/create.d.ts +2 -16
- package/lib/create.js +10 -14
- package/lib/create.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/module.d.ts +24 -0
- package/lib/module.js +139 -0
- package/lib/module.js.map +1 -0
- package/lib/queue.d.ts +1 -1
- package/lib/queue.js +3 -3
- package/lib/queue.js.map +1 -1
- package/lib/startup.d.ts +8 -0
- package/lib/startup.js +120 -0
- package/lib/startup.js.map +1 -0
- package/lib/types.d.ts +71 -3
- package/lib/utils.d.ts +18 -0
- package/lib/utils.js +55 -0
- package/lib/utils.js.map +1 -0
- package/package.json +51 -16
- package/src/RoutingService.ts +114 -0
- package/src/bootstrap.ts +57 -104
- package/src/common/NgExtension.ts +33 -0
- package/src/common/ResourceUrlPipe.ts +12 -0
- package/src/common/SharedModule.ts +18 -0
- package/src/common/index.d.ts +1 -0
- package/src/common/ng-package.json +11 -0
- package/src/common/package.json +19 -0
- package/src/common/piral-ng-common.js +3 -0
- package/src/common/public_api.ts +3 -0
- package/src/converter.ts +58 -20
- package/src/create.ts +6 -23
- package/src/module.ts +163 -0
- package/src/queue.ts +1 -1
- package/src/startup.ts +148 -0
- package/src/types.ts +73 -3
- package/src/utils.ts +69 -0
- package/convert.ts +0 -16
- package/esm/extension.d.ts +0 -1
- package/esm/extension.js +0 -41
- package/esm/extension.js.map +0 -1
- package/lib/extension.d.ts +0 -1
- package/lib/extension.js +0 -45
- package/lib/extension.js.map +0 -1
- package/src/extension.ts +0 -29
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Subscription } from 'rxjs';
|
|
2
|
+
import type { ComponentContext, Disposable } from 'piral-core';
|
|
3
|
+
import { Inject, Injectable, NgZone, OnDestroy, Optional } from '@angular/core';
|
|
4
|
+
import { NavigationError, NavigationStart, Router, Scroll } from '@angular/router';
|
|
5
|
+
import { BrowserPlatformLocation as ɵBrowserPlatformLocation } from '@angular/common';
|
|
6
|
+
|
|
7
|
+
let skipNavigation = false;
|
|
8
|
+
const noop = function () {};
|
|
9
|
+
const navigateByUrl = Router.prototype.navigateByUrl;
|
|
10
|
+
|
|
11
|
+
// deactivates the usual platform behavior; all these operations are performed via the RoutingService
|
|
12
|
+
// to avoid any conflict, e.g., double-booking URL changes in React and Angular
|
|
13
|
+
ɵBrowserPlatformLocation.prototype.pushState = noop;
|
|
14
|
+
ɵBrowserPlatformLocation.prototype.replaceState = noop;
|
|
15
|
+
ɵBrowserPlatformLocation.prototype.forward = noop;
|
|
16
|
+
ɵBrowserPlatformLocation.prototype.back = noop;
|
|
17
|
+
ɵBrowserPlatformLocation.prototype.historyGo = noop;
|
|
18
|
+
|
|
19
|
+
// required to detect / react to hidden URL change (see #554 for details)
|
|
20
|
+
Router.prototype.navigateByUrl = function (url, extras) {
|
|
21
|
+
skipNavigation = extras?.skipLocationChange ?? false;
|
|
22
|
+
const result = navigateByUrl.call(this, url, extras);
|
|
23
|
+
skipNavigation = false;
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function normalize(url: string) {
|
|
28
|
+
const search = url.indexOf('?');
|
|
29
|
+
const hash = url.indexOf('#');
|
|
30
|
+
|
|
31
|
+
if (search !== -1 || hash !== -1) {
|
|
32
|
+
if (search === -1) {
|
|
33
|
+
return url.substring(0, hash);
|
|
34
|
+
} else if (hash === -1) {
|
|
35
|
+
return url.substring(0, search);
|
|
36
|
+
} else {
|
|
37
|
+
return url.substring(0, Math.min(search, hash));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return url;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Injectable()
|
|
45
|
+
export class RoutingService implements OnDestroy {
|
|
46
|
+
private dispose: Disposable | undefined;
|
|
47
|
+
private subscription: Subscription | undefined;
|
|
48
|
+
private invalidRoutes: Array<string> = [];
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
@Inject('Context') public context: ComponentContext,
|
|
52
|
+
@Optional() private router: Router,
|
|
53
|
+
@Optional() private zone: NgZone,
|
|
54
|
+
) {
|
|
55
|
+
if (this.router) {
|
|
56
|
+
this.router.errorHandler = (error: Error) => {
|
|
57
|
+
if (error.message.match('Cannot match any routes')) {
|
|
58
|
+
// ignore this special error
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const skipIds: Array<number> = [];
|
|
65
|
+
const nav = this.context.navigation;
|
|
66
|
+
|
|
67
|
+
this.dispose = nav.listen(({ location }) => {
|
|
68
|
+
const path = location.pathname;
|
|
69
|
+
|
|
70
|
+
if (!this.invalidRoutes.includes(path)) {
|
|
71
|
+
const url = `${path}${location.search}${location.hash}`;
|
|
72
|
+
this.zone.run(() => navigateByUrl.call(this.router, url));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this.subscription = this.router.events.subscribe((e: NavigationError | NavigationStart | Scroll) => {
|
|
77
|
+
if (e instanceof NavigationError) {
|
|
78
|
+
const routerUrl = e.url;
|
|
79
|
+
const path = normalize(routerUrl);
|
|
80
|
+
const locationUrl = nav.url;
|
|
81
|
+
|
|
82
|
+
if (!this.invalidRoutes.includes(path)) {
|
|
83
|
+
this.invalidRoutes.push(path);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (routerUrl !== locationUrl) {
|
|
87
|
+
nav.push(routerUrl);
|
|
88
|
+
}
|
|
89
|
+
} else if (e.type === 0 && skipNavigation) {
|
|
90
|
+
skipIds.push(e.id);
|
|
91
|
+
} else if (e.type === 15) {
|
|
92
|
+
const index = skipIds.indexOf(e.routerEvent.id);
|
|
93
|
+
|
|
94
|
+
if (index === -1) {
|
|
95
|
+
// consistency check to avoid #535 and other Angular-specific issues
|
|
96
|
+
const locationUrl = nav.url;
|
|
97
|
+
const routerUrl = e.routerEvent.url;
|
|
98
|
+
|
|
99
|
+
if (routerUrl !== locationUrl) {
|
|
100
|
+
nav.push(routerUrl);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
skipIds.splice(index, 1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ngOnDestroy() {
|
|
111
|
+
this.dispose?.();
|
|
112
|
+
this.subscription?.unsubscribe();
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/bootstrap.ts
CHANGED
|
@@ -1,118 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { BaseComponentProps, ComponentContext, Disposable, PiletApi } from 'piral-core';
|
|
2
|
+
import type { BehaviorSubject } from 'rxjs';
|
|
3
|
+
import type { Type } from '@angular/core';
|
|
4
|
+
import type { NgLazyType, PrepareBootstrapResult } from './types';
|
|
5
|
+
import { createModuleInstance, getModuleInstance, defineModule, findModule, activateModuleInstance } from './module';
|
|
6
|
+
import { getAnnotations, hasSelector } from './utils';
|
|
7
|
+
import { startup } from './startup';
|
|
5
8
|
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function setComponentSelector(component: any, id: string) {
|
|
18
|
-
const annotations = component && component.__annotations__;
|
|
19
|
-
const annotation = annotations?.[0];
|
|
20
|
-
|
|
21
|
-
if (annotation && !annotation.selector) {
|
|
22
|
-
annotation.selector = `#${id}`;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
9
|
+
export async function prepareBootstrap(
|
|
10
|
+
moduleOrComponent: Type<any> | NgLazyType,
|
|
11
|
+
piral: PiletApi,
|
|
12
|
+
): Promise<PrepareBootstrapResult> {
|
|
13
|
+
if ('module' in moduleOrComponent && typeof moduleOrComponent.module === 'function') {
|
|
14
|
+
if (!(moduleOrComponent.state.current instanceof Promise)) {
|
|
15
|
+
moduleOrComponent.state.current = moduleOrComponent.module().then((result) => {
|
|
16
|
+
if (typeof result !== 'object' || !('default' in result)) {
|
|
17
|
+
throw new Error('The lazy loaded module does not `default` export a NgModule class.');
|
|
18
|
+
}
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
defineModule(result.default, moduleOrComponent.opts);
|
|
21
|
+
return findModule(result.default);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
.then((bootstrapModule) => {
|
|
34
|
-
node.removeAttribute('id');
|
|
35
|
-
return bootstrapModule;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
25
|
+
const moduleDef = await moduleOrComponent.state.current;
|
|
26
|
+
const { components } = moduleDef;
|
|
27
|
+
const component = components.find((m) => hasSelector(m, moduleOrComponent.selector));
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
moduleOrComponent: any,
|
|
43
|
-
node: HTMLElement,
|
|
44
|
-
id: string,
|
|
45
|
-
) {
|
|
46
|
-
const annotations = moduleOrComponent.__annotations__;
|
|
47
|
-
const annotation = annotations?.[0];
|
|
29
|
+
if (!component) {
|
|
30
|
+
throw new Error(`No component matching the selector "${moduleOrComponent.selector}" has been found.`);
|
|
31
|
+
}
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
// usually contains things like imports, exports, declarations, ...
|
|
51
|
-
return bootstrapModule(context, props, moduleOrComponent, node, id);
|
|
33
|
+
return [...activateModuleInstance(moduleDef, piral), component];
|
|
52
34
|
} else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function bootstrapComponent<T extends BaseComponentProps>(
|
|
59
|
-
context: any,
|
|
60
|
-
props: T,
|
|
61
|
-
component: any,
|
|
62
|
-
node: HTMLElement,
|
|
63
|
-
id: string,
|
|
64
|
-
): Promise<void | NgModuleRef<any>> {
|
|
65
|
-
const { piral } = props;
|
|
66
|
-
const declarations = [component, piral.NgExtension];
|
|
67
|
-
node.id = sanatize(id);
|
|
68
|
-
setComponentSelector(component, node.id);
|
|
35
|
+
const [annotation] = getAnnotations(moduleOrComponent);
|
|
36
|
+
const standalone = annotation?.standalone;
|
|
69
37
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
38
|
+
// first way is to directly use a module, which is the legacy way
|
|
39
|
+
// second way is to find a previously defined Angular module
|
|
40
|
+
if (annotation && annotation.bootstrap) {
|
|
41
|
+
// usually contains things like imports, exports, declarations, ...
|
|
42
|
+
const [component] = annotation.bootstrap;
|
|
43
|
+
annotation.exports = [component];
|
|
44
|
+
defineModule(moduleOrComponent);
|
|
45
|
+
return [...getModuleInstance(component, standalone, piral), component];
|
|
46
|
+
} else {
|
|
47
|
+
// usually contains things like selector, template or templateUrl, changeDetection, ...
|
|
48
|
+
const result =
|
|
49
|
+
getModuleInstance(moduleOrComponent, standalone, piral) ||
|
|
50
|
+
createModuleInstance(moduleOrComponent, standalone, piral);
|
|
51
|
+
return [...result, moduleOrComponent];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
84
54
|
}
|
|
85
55
|
|
|
86
|
-
export function
|
|
87
|
-
|
|
88
|
-
props: T,
|
|
89
|
-
BootstrapModule: any,
|
|
56
|
+
export async function bootstrap<TProps extends BaseComponentProps>(
|
|
57
|
+
result: PrepareBootstrapResult,
|
|
90
58
|
node: HTMLElement,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
node.id = sanatize(id);
|
|
59
|
+
props: BehaviorSubject<TProps>,
|
|
60
|
+
context: ComponentContext,
|
|
61
|
+
): Promise<Disposable> {
|
|
62
|
+
const [selectedModule, ngOptions, component] = result;
|
|
63
|
+
const ref = await startup(selectedModule, context, ngOptions);
|
|
97
64
|
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const [component] = annotation.bootstrap || [];
|
|
102
|
-
annotation.providers = [
|
|
103
|
-
...providers,
|
|
104
|
-
{
|
|
105
|
-
provide: 'piral',
|
|
106
|
-
useValue: piral,
|
|
107
|
-
},
|
|
108
|
-
];
|
|
109
|
-
annotation.declarations = [...declarations, piral.NgExtension];
|
|
110
|
-
|
|
111
|
-
if (component) {
|
|
112
|
-
setComponentSelector(component, node.id);
|
|
113
|
-
annotation.bootstrap = [component];
|
|
114
|
-
}
|
|
65
|
+
if (ref) {
|
|
66
|
+
ref.instance.attach(component, node, props);
|
|
67
|
+
return () => ref.instance.detach(component, node);
|
|
115
68
|
}
|
|
116
69
|
|
|
117
|
-
return
|
|
70
|
+
return () => {};
|
|
118
71
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PiletApi } from 'piral-core';
|
|
2
|
+
import { Component, ElementRef, Input, Inject, OnChanges } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
const selector = 'extension-component';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector,
|
|
8
|
+
template: '',
|
|
9
|
+
})
|
|
10
|
+
export class NgExtension implements OnChanges {
|
|
11
|
+
@Input('name') public name: string | undefined;
|
|
12
|
+
@Input('params') public params: object | undefined;
|
|
13
|
+
|
|
14
|
+
constructor(private elRef: ElementRef<HTMLElement>, @Inject('piral') private piral: PiletApi) {}
|
|
15
|
+
|
|
16
|
+
ngOnChanges() {
|
|
17
|
+
this.elRef.nativeElement.dispatchEvent(
|
|
18
|
+
new CustomEvent('extension-props-changed', {
|
|
19
|
+
detail: {
|
|
20
|
+
name: this.name,
|
|
21
|
+
params: this.params,
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ngAfterContentInit() {
|
|
28
|
+
this.piral.renderHtmlExtension(this.elRef.nativeElement, {
|
|
29
|
+
name: this.name,
|
|
30
|
+
params: this.params,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PiletApi } from 'piral-core';
|
|
2
|
+
import { Inject, Pipe, PipeTransform } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
@Pipe({ name: 'resourceUrl' })
|
|
5
|
+
export class ResourceUrlPipe implements PipeTransform {
|
|
6
|
+
constructor(@Inject('piral') private piral: PiletApi) {}
|
|
7
|
+
|
|
8
|
+
transform(value: string): string {
|
|
9
|
+
const { basePath = '/' } = this.piral.meta;
|
|
10
|
+
return basePath + value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { NgModule } from '@angular/core';
|
|
3
|
+
import { NgExtension } from './NgExtension';
|
|
4
|
+
import { ResourceUrlPipe } from './ResourceUrlPipe';
|
|
5
|
+
|
|
6
|
+
const declarationsDef = [NgExtension, ResourceUrlPipe];
|
|
7
|
+
const exportsDef = [NgExtension, ResourceUrlPipe];
|
|
8
|
+
const importsDef = [CommonModule];
|
|
9
|
+
|
|
10
|
+
@NgModule({
|
|
11
|
+
declarations: declarationsDef,
|
|
12
|
+
providers: [],
|
|
13
|
+
imports: importsDef,
|
|
14
|
+
exports: exportsDef,
|
|
15
|
+
})
|
|
16
|
+
export class SharedModule {
|
|
17
|
+
static props = {};
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './public_api';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "piral-ng-common",
|
|
3
|
+
"version": "0.15.0",
|
|
4
|
+
"description": "Common module for piral-ng.",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"author": "smapiot",
|
|
7
|
+
"main": "piral-ng-common.js",
|
|
8
|
+
"homepage": "https://piral.io",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/smapiot/piral.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/smapiot/piral/issues"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {},
|
|
18
|
+
"peerDependencies": {}
|
|
19
|
+
}
|
package/src/converter.ts
CHANGED
|
@@ -1,23 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { ForeignComponent, BaseComponentProps, Disposable } from 'piral-core';
|
|
2
|
+
import type { Type } from '@angular/core';
|
|
3
|
+
import type { NgLazyType, NgModuleDefiner, PrepareBootstrapResult } from './types';
|
|
4
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
5
|
import { enqueue } from './queue';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
import { defineModule } from './module';
|
|
7
|
+
import { bootstrap, prepareBootstrap } from './bootstrap';
|
|
8
|
+
import { NgExtension } from '../common';
|
|
9
|
+
|
|
10
|
+
export interface NgConverterOptions {}
|
|
11
|
+
|
|
12
|
+
export interface NgConverter {
|
|
13
|
+
<TProps extends BaseComponentProps>(component: any): ForeignComponent<TProps>;
|
|
14
|
+
defineModule: NgModuleDefiner;
|
|
15
|
+
Extension: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface NgState<TProps> {
|
|
19
|
+
queued: Promise<void | Disposable>;
|
|
20
|
+
props: BehaviorSubject<TProps>;
|
|
21
|
+
active: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createConverter(_: NgConverterOptions = {}): NgConverter {
|
|
25
|
+
const registry = new Map<any, PrepareBootstrapResult>();
|
|
26
|
+
const convert = <TProps extends BaseComponentProps>(component: Type<any> | NgLazyType): ForeignComponent<TProps> => ({
|
|
27
|
+
mount(el, props, ctx, locals: NgState<TProps>) {
|
|
28
|
+
locals.active = true;
|
|
29
|
+
|
|
30
|
+
if (!locals.props) {
|
|
31
|
+
locals.props = new BehaviorSubject(props);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!locals.queued) {
|
|
35
|
+
locals.queued = Promise.resolve();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
locals.queued = locals.queued.then(() =>
|
|
39
|
+
enqueue(async () => {
|
|
40
|
+
if (!registry.has(component)) {
|
|
41
|
+
registry.set(component, await prepareBootstrap(component, props.piral));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (locals.active) {
|
|
45
|
+
return await bootstrap(registry.get(component), el, locals.props, ctx);
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
update(el, props, ctx, locals: NgState<TProps>) {
|
|
51
|
+
locals.props.next(props);
|
|
52
|
+
},
|
|
53
|
+
unmount(el, locals: NgState<TProps>) {
|
|
54
|
+
locals.active = false;
|
|
55
|
+
locals.queued = locals.queued.then((dispose) => dispose && enqueue(dispose));
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
convert.defineModule = defineModule;
|
|
59
|
+
convert.Extension = NgExtension;
|
|
22
60
|
return convert;
|
|
23
61
|
}
|
package/src/create.ts
CHANGED
|
@@ -1,41 +1,24 @@
|
|
|
1
1
|
import type { PiralPlugin } from 'piral-core';
|
|
2
|
-
import { createConverter } from './converter';
|
|
3
|
-
import { createExtension } from './extension';
|
|
4
2
|
import type { PiletNgApi } from './types';
|
|
3
|
+
import { createConverter, NgConverterOptions } from './converter';
|
|
4
|
+
import { defineModule } from './module';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Available configuration options for the Angular plugin.
|
|
8
8
|
*/
|
|
9
|
-
export interface NgConfig {
|
|
10
|
-
/**
|
|
11
|
-
* Defines the name of the extension component.
|
|
12
|
-
* @default extension-component
|
|
13
|
-
*/
|
|
14
|
-
selector?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Defines the name of the root element.
|
|
17
|
-
* @default slot
|
|
18
|
-
*/
|
|
19
|
-
rootName?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Defines how the next ID for mounting is selected.
|
|
22
|
-
* By default a random number is used in conjunction with a `ng-` prefix.
|
|
23
|
-
*/
|
|
24
|
-
selectId?(): string;
|
|
25
|
-
}
|
|
9
|
+
export interface NgConfig extends NgConverterOptions {}
|
|
26
10
|
|
|
27
11
|
/**
|
|
28
12
|
* Creates the Pilet API extensions for Angular.
|
|
29
13
|
*/
|
|
30
14
|
export function createNgApi(config: NgConfig = {}): PiralPlugin<PiletNgApi> {
|
|
31
|
-
const { rootName, selector, selectId } = config;
|
|
32
|
-
|
|
33
15
|
return (context) => {
|
|
34
|
-
const convert = createConverter(
|
|
16
|
+
const convert = createConverter(config);
|
|
35
17
|
context.converters.ng = ({ component }) => convert(component);
|
|
36
18
|
|
|
37
19
|
return {
|
|
38
|
-
NgExtension:
|
|
20
|
+
NgExtension: convert.Extension,
|
|
21
|
+
defineNgModule: defineModule,
|
|
39
22
|
fromNg(component) {
|
|
40
23
|
return {
|
|
41
24
|
type: 'ng',
|