piral-ng 0.15.0-beta.4808 → 0.15.0-beta.4812
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/esm/RoutingService.js +33 -4
- package/esm/RoutingService.js.map +1 -1
- package/esm/bootstrap.d.ts +3 -2
- package/esm/bootstrap.js +41 -19
- package/esm/bootstrap.js.map +1 -1
- package/esm/converter.js +10 -5
- package/esm/converter.js.map +1 -1
- package/esm/module.d.ts +20 -1
- package/esm/module.js +28 -11
- package/esm/module.js.map +1 -1
- package/esm/startup.js +44 -36
- package/esm/startup.js.map +1 -1
- package/esm/types.d.ts +31 -3
- package/esm/utils.d.ts +2 -0
- package/esm/utils.js +7 -0
- package/esm/utils.js.map +1 -1
- package/lib/RoutingService.js +33 -4
- package/lib/RoutingService.js.map +1 -1
- package/lib/bootstrap.d.ts +3 -2
- package/lib/bootstrap.js +41 -19
- package/lib/bootstrap.js.map +1 -1
- package/lib/converter.js +10 -5
- package/lib/converter.js.map +1 -1
- package/lib/module.d.ts +20 -1
- package/lib/module.js +31 -12
- package/lib/module.js.map +1 -1
- package/lib/startup.js +43 -35
- package/lib/startup.js.map +1 -1
- package/lib/types.d.ts +31 -3
- package/lib/utils.d.ts +2 -0
- package/lib/utils.js +10 -1
- package/lib/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/RoutingService.ts +36 -4
- package/src/bootstrap.ts +48 -21
- package/src/converter.ts +13 -8
- package/src/module.ts +32 -13
- package/src/startup.ts +51 -39
- package/src/types.ts +29 -3
- package/src/utils.ts +9 -0
package/src/bootstrap.ts
CHANGED
|
@@ -1,28 +1,55 @@
|
|
|
1
1
|
import type { BaseComponentProps, ComponentContext, Disposable, PiletApi } from 'piral-core';
|
|
2
2
|
import type { BehaviorSubject } from 'rxjs';
|
|
3
|
-
import type {
|
|
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';
|
|
4
7
|
import { startup } from './startup';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
|
|
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
|
+
}
|
|
19
|
+
|
|
20
|
+
defineModule(result.default, moduleOrComponent.opts);
|
|
21
|
+
return findModule(result.default);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const moduleDef = await moduleOrComponent.state.current;
|
|
26
|
+
const { components } = moduleDef;
|
|
27
|
+
const component = components.find((m) => hasSelector(m, moduleOrComponent.selector));
|
|
28
|
+
|
|
29
|
+
if (!component) {
|
|
30
|
+
throw new Error(`No component matching the selector "${moduleOrComponent.selector}" has been found.`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return [...activateModuleInstance(moduleDef, piral), component];
|
|
20
34
|
} else {
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
const [annotation] = getAnnotations(moduleOrComponent);
|
|
36
|
+
const standalone = annotation?.standalone;
|
|
37
|
+
|
|
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
|
+
}
|
|
26
53
|
}
|
|
27
54
|
}
|
|
28
55
|
|
package/src/converter.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ForeignComponent, BaseComponentProps, Disposable } from 'piral-core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Type } from '@angular/core';
|
|
3
|
+
import type { NgLazyType, NgModuleDefiner, PrepareBootstrapResult } from './types';
|
|
3
4
|
import { BehaviorSubject } from 'rxjs';
|
|
4
5
|
import { NgExtension } from './NgExtension';
|
|
5
6
|
import { enqueue } from './queue';
|
|
@@ -22,14 +23,10 @@ interface NgState<TProps> {
|
|
|
22
23
|
|
|
23
24
|
export function createConverter(_: NgConverterOptions = {}): NgConverter {
|
|
24
25
|
const registry = new Map<any, PrepareBootstrapResult>();
|
|
25
|
-
const convert = <TProps extends BaseComponentProps>(component: any): ForeignComponent<TProps> => ({
|
|
26
|
+
const convert = <TProps extends BaseComponentProps>(component: Type<any> | NgLazyType): ForeignComponent<TProps> => ({
|
|
26
27
|
mount(el, props, ctx, locals: NgState<TProps>) {
|
|
27
28
|
locals.active = true;
|
|
28
29
|
|
|
29
|
-
if (!registry.has(component)) {
|
|
30
|
-
registry.set(component, prepareBootstrap(component, props.piral));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
30
|
if (!locals.props) {
|
|
34
31
|
locals.props = new BehaviorSubject(props);
|
|
35
32
|
}
|
|
@@ -39,7 +36,15 @@ export function createConverter(_: NgConverterOptions = {}): NgConverter {
|
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
locals.queued = locals.queued.then(() =>
|
|
42
|
-
enqueue(() =>
|
|
39
|
+
enqueue(async () => {
|
|
40
|
+
if (!registry.has(component)) {
|
|
41
|
+
registry.set(component, await prepareBootstrap(component, props.piral));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (locals.active) {
|
|
45
|
+
bootstrap(registry.get(component), el, locals.props, ctx);
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
43
48
|
);
|
|
44
49
|
},
|
|
45
50
|
update(el, props, ctx, locals: NgState<TProps>) {
|
|
@@ -47,7 +52,7 @@ export function createConverter(_: NgConverterOptions = {}): NgConverter {
|
|
|
47
52
|
},
|
|
48
53
|
unmount(el, locals: NgState<TProps>) {
|
|
49
54
|
locals.active = false;
|
|
50
|
-
locals.queued = locals.queued.then((dispose) =>
|
|
55
|
+
locals.queued = locals.queued.then((dispose) => dispose && enqueue(dispose));
|
|
51
56
|
},
|
|
52
57
|
});
|
|
53
58
|
convert.defineModule = defineModule;
|
package/src/module.ts
CHANGED
|
@@ -89,15 +89,19 @@ function instantiateModule(moduleDef: ModuleDefinition, piral: PiletApi) {
|
|
|
89
89
|
return BootstrapModule;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export function
|
|
92
|
+
export function activateModuleInstance(moduleDef: ModuleDefinition, piral: PiletApi): ModuleInstanceResult {
|
|
93
|
+
if (!moduleDef.active) {
|
|
94
|
+
moduleDef.active = instantiateModule(moduleDef, piral);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return [moduleDef.active, moduleDef.opts];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getModuleInstance(component: any, standalone: boolean, piral: PiletApi) {
|
|
93
101
|
const [moduleDef] = availableModules.filter((m) => m.components.includes(component));
|
|
94
102
|
|
|
95
103
|
if (moduleDef) {
|
|
96
|
-
|
|
97
|
-
moduleDef.active = instantiateModule(moduleDef, piral);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return [moduleDef.active, moduleDef.opts];
|
|
104
|
+
return activateModuleInstance(moduleDef, piral);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
if (process.env.NODE_ENV === 'development') {
|
|
@@ -131,12 +135,27 @@ export function createModuleInstance(component: any, standalone: boolean, piral:
|
|
|
131
135
|
return getModuleInstance(component, standalone, piral);
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
export function findModule(module: any) {
|
|
139
|
+
return availableModules.find(m => m.module === module);
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
export function defineModule(module: any, opts: NgOptions = undefined) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
if (typeof module !== 'function') {
|
|
144
|
+
const [annotation] = getAnnotations(module);
|
|
145
|
+
availableModules.push({
|
|
146
|
+
active: undefined,
|
|
147
|
+
components: findComponents(annotation.exports),
|
|
148
|
+
module,
|
|
149
|
+
opts,
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
const state = {
|
|
153
|
+
current: undefined,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return (selector: string) => ({
|
|
157
|
+
component: { selector, module, opts, state },
|
|
158
|
+
type: 'ng' as const,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
142
161
|
}
|
package/src/startup.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { NgOptions } from './types';
|
|
|
3
3
|
import { enableProdMode, NgModuleRef, NgZone, PlatformRef } from '@angular/core';
|
|
4
4
|
import { APP_BASE_HREF } from '@angular/common';
|
|
5
5
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
6
|
-
import { getNgVersion } from './utils';
|
|
6
|
+
import { getId, getNgVersion } from './utils';
|
|
7
7
|
|
|
8
8
|
function getVersionHandler(versions: Record<string, () => void>) {
|
|
9
9
|
const major = getNgVersion();
|
|
@@ -13,15 +13,55 @@ function getVersionHandler(versions: Record<string, () => void>) {
|
|
|
13
13
|
|
|
14
14
|
const runningModules: Array<[any, NgModuleInt, PlatformRef]> = [];
|
|
15
15
|
|
|
16
|
+
function startNew(BootstrapModule: any, context: ComponentContext, ngOptions?: NgOptions) {
|
|
17
|
+
const path = context.publicPath || '/';
|
|
18
|
+
const platform = platformBrowserDynamic([
|
|
19
|
+
{ provide: 'Context', useValue: context },
|
|
20
|
+
{ provide: APP_BASE_HREF, useValue: path },
|
|
21
|
+
]);
|
|
22
|
+
const id = getId();
|
|
23
|
+
const zoneIdentifier = `piral-ng:${id}`;
|
|
24
|
+
|
|
25
|
+
// This is a hack, since NgZone doesn't allow you to configure the property that identifies your zone.
|
|
26
|
+
// See:
|
|
27
|
+
// - https://github.com/PlaceMe-SAS/single-spa-angular-cli/issues/33
|
|
28
|
+
// - https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L144
|
|
29
|
+
// - https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L257
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
NgZone.isInAngularZone = () => window.Zone.current._properties[zoneIdentifier] === true;
|
|
32
|
+
|
|
33
|
+
return platform
|
|
34
|
+
.bootstrapModule(BootstrapModule, ngOptions)
|
|
35
|
+
.catch((err) => console.log(err))
|
|
36
|
+
.then((instance: NgModuleInt) => {
|
|
37
|
+
if (instance) {
|
|
38
|
+
const zone = instance.injector.get(NgZone);
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
const z = zone?._inner ?? zone?.inner;
|
|
41
|
+
|
|
42
|
+
if (z && '_properties' in z) {
|
|
43
|
+
z._properties[zoneIdentifier] = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
runningModules.push([BootstrapModule, instance, platform]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return instance;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
16
53
|
export type NgModuleInt = NgModuleRef<any> & { _destroyed: boolean };
|
|
17
54
|
|
|
18
55
|
export function teardown(BootstrapModule: any) {
|
|
19
56
|
const runningModuleIndex = runningModules.findIndex(([ref]) => ref === BootstrapModule);
|
|
20
57
|
|
|
21
58
|
if (runningModuleIndex !== -1) {
|
|
22
|
-
const [
|
|
59
|
+
const [, , platform] = runningModules[runningModuleIndex];
|
|
23
60
|
runningModules.splice(runningModuleIndex, 1);
|
|
24
|
-
|
|
61
|
+
|
|
62
|
+
if (!platform.destroyed) {
|
|
63
|
+
platform.destroy();
|
|
64
|
+
}
|
|
25
65
|
}
|
|
26
66
|
}
|
|
27
67
|
|
|
@@ -33,44 +73,16 @@ export function startup(
|
|
|
33
73
|
const runningModule = runningModules.find(([ref]) => ref === BootstrapModule);
|
|
34
74
|
|
|
35
75
|
if (runningModule) {
|
|
36
|
-
const [, instance] = runningModule;
|
|
37
|
-
return Promise.resolve(instance);
|
|
38
|
-
} else {
|
|
39
|
-
const path = context.publicPath || '/';
|
|
40
|
-
const platform = platformBrowserDynamic([
|
|
41
|
-
{ provide: 'Context', useValue: context },
|
|
42
|
-
{ provide: APP_BASE_HREF, useValue: path },
|
|
43
|
-
]);
|
|
44
|
-
const id = Math.random().toString(36);
|
|
45
|
-
const zoneIdentifier = `piral-ng:${id}`;
|
|
46
|
-
|
|
47
|
-
// This is a hack, since NgZone doesn't allow you to configure the property that identifies your zone.
|
|
48
|
-
// See:
|
|
49
|
-
// - https://github.com/PlaceMe-SAS/single-spa-angular-cli/issues/33
|
|
50
|
-
// - https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L144
|
|
51
|
-
// - https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L257
|
|
52
|
-
// @ts-ignore
|
|
53
|
-
NgZone.isInAngularZone = () => window.Zone.current._properties[zoneIdentifier] === true;
|
|
54
|
-
|
|
55
|
-
return platform
|
|
56
|
-
.bootstrapModule(BootstrapModule, ngOptions)
|
|
57
|
-
.catch((err) => console.log(err))
|
|
58
|
-
.then((instance: NgModuleInt) => {
|
|
59
|
-
if (instance) {
|
|
60
|
-
const zone = instance.injector.get(NgZone);
|
|
61
|
-
// @ts-ignore
|
|
62
|
-
const z = zone?._inner ?? zone?.inner;
|
|
63
|
-
|
|
64
|
-
if (z && '_properties' in z) {
|
|
65
|
-
z._properties[zoneIdentifier] = true;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
runningModules.push([BootstrapModule, instance, platform]);
|
|
69
|
-
}
|
|
76
|
+
const [, instance, platform] = runningModule;
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
if (platform.destroyed) {
|
|
79
|
+
teardown(BootstrapModule);
|
|
80
|
+
} else {
|
|
81
|
+
return Promise.resolve(instance);
|
|
82
|
+
}
|
|
73
83
|
}
|
|
84
|
+
|
|
85
|
+
return startNew(BootstrapModule, context, ngOptions);
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
if (process.env.NODE_ENV === 'development') {
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PlatformRef, NgModuleRef } from '@angular/core';
|
|
2
2
|
import type { ForeignComponent } from 'piral-core';
|
|
3
|
+
import type { Type } from '@angular/core';
|
|
3
4
|
|
|
4
5
|
declare module 'piral-core/lib/types/custom' {
|
|
5
6
|
interface PiletCustomApi extends PiletNgApi {}
|
|
@@ -23,6 +24,24 @@ export type PrepareBootstrapResult = [...ModuleInstanceResult, any];
|
|
|
23
24
|
|
|
24
25
|
export type NgModuleInt = NgModuleRef<any> & { _destroyed: boolean };
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Gives you the ability to use a component from a lazy loaded module.
|
|
29
|
+
*/
|
|
30
|
+
export interface NgComponentLoader {
|
|
31
|
+
/**
|
|
32
|
+
* Uses a component from a lazy loaded module.
|
|
33
|
+
* @param selector The selector defined for the component to load.
|
|
34
|
+
*/
|
|
35
|
+
(selector: string): NgComponent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface NgLazyType {
|
|
39
|
+
selector: string;
|
|
40
|
+
module: () => Promise<{ default: Type<any> }>;
|
|
41
|
+
opts: NgOptions;
|
|
42
|
+
state: any;
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
/**
|
|
27
46
|
* Represents the interface implemented by a module definer function.
|
|
28
47
|
*/
|
|
@@ -32,14 +51,21 @@ export interface NgModuleDefiner {
|
|
|
32
51
|
* @param ngModule The module to use for running Angular.
|
|
33
52
|
* @param opts The options to pass when bootstrapping.
|
|
34
53
|
*/
|
|
35
|
-
(module:
|
|
54
|
+
<T>(module: Type<T>, opts?: NgOptions): void;
|
|
55
|
+
/**
|
|
56
|
+
* Defines the module to lazy load for bootstrapping the Angular pilet.
|
|
57
|
+
* @param getModule The module lazy loader to use for running Angular.
|
|
58
|
+
* @param opts The options to pass when bootstrapping.
|
|
59
|
+
* @returns The module ID to be used to reference components.
|
|
60
|
+
*/
|
|
61
|
+
<T>(getModule: () => Promise<{ default: Type<T> }>, opts?: NgOptions): NgComponentLoader;
|
|
36
62
|
}
|
|
37
63
|
|
|
38
64
|
export interface NgComponent {
|
|
39
65
|
/**
|
|
40
66
|
* The component root.
|
|
41
67
|
*/
|
|
42
|
-
component: any;
|
|
68
|
+
component: Type<any> | NgLazyType;
|
|
43
69
|
/**
|
|
44
70
|
* The type of the Angular component.
|
|
45
71
|
*/
|
|
@@ -62,7 +88,7 @@ export interface PiletNgApi {
|
|
|
62
88
|
* @param component The component root.
|
|
63
89
|
* @returns The Piral Ng component.
|
|
64
90
|
*/
|
|
65
|
-
fromNg(component:
|
|
91
|
+
fromNg<T>(component: Type<T>): NgComponent;
|
|
66
92
|
/**
|
|
67
93
|
* Angular component for displaying extensions of the given name.
|
|
68
94
|
*/
|
package/src/utils.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface NgAnnotation {
|
|
|
12
12
|
selector: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export function getId() {
|
|
16
|
+
return Math.random().toString(36);
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
export function getNgVersion() {
|
|
16
20
|
return VERSION.major || VERSION.full.split('.')[0];
|
|
17
21
|
}
|
|
@@ -39,6 +43,11 @@ export function getAnnotations(component: any): Array<NgAnnotation> {
|
|
|
39
43
|
return annotations || [];
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
export function hasSelector(component: any, selector: string) {
|
|
47
|
+
const [annotation] = getAnnotations(component);
|
|
48
|
+
return annotation && annotation.selector === selector;
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
export function findComponents(exports: Array<any>): Array<any> {
|
|
43
52
|
const components = [];
|
|
44
53
|
|