@openmrs/esm-routes 6.3.1-pre.3250 → 7.0.1-pre.3256
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/.turbo/turbo-build.log +1 -1
- package/dist/loaders/components.js +33 -71
- package/dist/loaders/helpers.d.ts +0 -2
- package/dist/loaders/helpers.js +0 -11
- package/dist/loaders/index.d.ts +1 -1
- package/dist/loaders/index.js +1 -1
- package/dist/loaders/load-lifecycles.d.ts +18 -0
- package/dist/loaders/{app.js → load-lifecycles.js} +32 -27
- package/dist/loaders/pages.js +2 -2
- package/dist/public.d.ts +0 -1
- package/dist/public.js +0 -1
- package/package.json +8 -8
- package/src/loaders/components.ts +33 -81
- package/src/loaders/helpers.ts +0 -14
- package/src/loaders/index.ts +1 -1
- package/src/loaders/{app.ts → load-lifecycles.ts} +36 -30
- package/src/loaders/pages.ts +2 -2
- package/src/public.ts +0 -1
- package/dist/loaders/app.d.ts +0 -40
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { attach, registerExtension, registerModal, registerWorkspace, registerWorkspaceGroup } from "@openmrs/esm-extensions";
|
|
2
|
-
import { getLoader } from "./app.js";
|
|
3
2
|
import { registerFeatureFlag } from "@openmrs/esm-feature-flags";
|
|
3
|
+
import { loadLifeCycles } from "./load-lifecycles.js";
|
|
4
4
|
/**
|
|
5
5
|
* This function registers an extension definition with the framework and will
|
|
6
6
|
* attach the extension to any configured slots.
|
|
@@ -22,35 +22,22 @@ a 'slot' property. Only the 'slots' property will be honored.`);
|
|
|
22
22
|
const slots = extension.slots ? extension.slots : extension.slot ? [
|
|
23
23
|
extension.slot
|
|
24
24
|
] : [];
|
|
25
|
-
if (!extension.component
|
|
25
|
+
if (!extension.component) {
|
|
26
26
|
console.error(`The extension ${name} from ${appName} is missing a 'component' entry and thus cannot be registered.
|
|
27
27
|
To fix this, ensure that you define a 'component' field inside the extension definition.`, extension);
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
if (loader) {
|
|
42
|
-
registerExtension({
|
|
43
|
-
name,
|
|
44
|
-
load: loader,
|
|
45
|
-
meta: extension.meta || {},
|
|
46
|
-
order: extension.order,
|
|
47
|
-
moduleName: appName,
|
|
48
|
-
privileges: extension.privileges,
|
|
49
|
-
online: extension.online ?? true,
|
|
50
|
-
offline: extension.offline ?? false,
|
|
51
|
-
featureFlag: extension.featureFlag
|
|
52
|
-
});
|
|
53
|
-
}
|
|
30
|
+
registerExtension({
|
|
31
|
+
name,
|
|
32
|
+
load: ()=>loadLifeCycles(appName, extension.component),
|
|
33
|
+
meta: extension.meta || {},
|
|
34
|
+
order: extension.order,
|
|
35
|
+
moduleName: appName,
|
|
36
|
+
privileges: extension.privileges,
|
|
37
|
+
online: extension.online ?? true,
|
|
38
|
+
offline: extension.offline ?? false,
|
|
39
|
+
featureFlag: extension.featureFlag
|
|
40
|
+
});
|
|
54
41
|
for (const slot of slots){
|
|
55
42
|
attach(slot, name);
|
|
56
43
|
}
|
|
@@ -68,29 +55,16 @@ registered. To fix this, ensure that you define the "name" field inside the
|
|
|
68
55
|
modal definition.`, modal);
|
|
69
56
|
return;
|
|
70
57
|
}
|
|
71
|
-
if (!modal.component
|
|
58
|
+
if (!modal.component) {
|
|
72
59
|
console.error(`The modal ${name} from ${appName} is missing a 'component' entry and thus cannot be registered.
|
|
73
60
|
To fix this, ensure that you define a 'component' field inside the modal definition.`, modal);
|
|
74
61
|
return;
|
|
75
62
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
console.error(`The modal ${name} from ${appName} declares a 'load' property that is not a function. This is not
|
|
82
|
-
supported, so the modal will not be loaded.`);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
loader = modal.load;
|
|
86
|
-
}
|
|
87
|
-
if (loader) {
|
|
88
|
-
registerModal({
|
|
89
|
-
name,
|
|
90
|
-
load: loader,
|
|
91
|
-
moduleName: appName
|
|
92
|
-
});
|
|
93
|
-
}
|
|
63
|
+
registerModal({
|
|
64
|
+
name,
|
|
65
|
+
moduleName: appName,
|
|
66
|
+
load: ()=>loadLifeCycles(appName, modal.component)
|
|
67
|
+
});
|
|
94
68
|
}
|
|
95
69
|
/**
|
|
96
70
|
* This function registers a workspace definition with the framework so that it can be launched.
|
|
@@ -110,36 +84,24 @@ To fix this, ensure that you define the "name" field inside the workspace defini
|
|
|
110
84
|
To fix this, ensure that you define the "title" field inside the workspace definition.`, workspace);
|
|
111
85
|
return;
|
|
112
86
|
}
|
|
113
|
-
if (!workspace.component
|
|
87
|
+
if (!workspace.component) {
|
|
114
88
|
console.error(`The workspace ${name} from ${appName} is missing a 'component' entry and thus cannot be registered.
|
|
115
89
|
To fix this, ensure that you define a 'component' field inside the workspace definition.`, workspace);
|
|
116
90
|
return;
|
|
117
91
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
name,
|
|
132
|
-
title,
|
|
133
|
-
load: loader,
|
|
134
|
-
moduleName: appName,
|
|
135
|
-
type: workspace.type,
|
|
136
|
-
canHide: workspace.canHide,
|
|
137
|
-
canMaximize: workspace.canMaximize,
|
|
138
|
-
width: workspace.width,
|
|
139
|
-
preferredWindowSize: workspace.preferredWindowSize,
|
|
140
|
-
groups: workspace.groups
|
|
141
|
-
});
|
|
142
|
-
}
|
|
92
|
+
registerWorkspace({
|
|
93
|
+
name,
|
|
94
|
+
title,
|
|
95
|
+
component: workspace.component,
|
|
96
|
+
moduleName: appName,
|
|
97
|
+
type: workspace.type,
|
|
98
|
+
canHide: workspace.canHide,
|
|
99
|
+
canMaximize: workspace.canMaximize,
|
|
100
|
+
width: workspace.width,
|
|
101
|
+
preferredWindowSize: workspace.preferredWindowSize,
|
|
102
|
+
groups: workspace.groups,
|
|
103
|
+
load: ()=>loadLifeCycles(appName, workspace.component)
|
|
104
|
+
});
|
|
143
105
|
for (const group of workspace.groups || []){
|
|
144
106
|
registerWorkspaceGroup({
|
|
145
107
|
name: group,
|
package/dist/loaders/helpers.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
export const emptyLifecycle = {
|
|
2
|
-
bootstrap () {
|
|
3
|
-
return Promise.resolve();
|
|
4
|
-
},
|
|
5
|
-
mount () {
|
|
6
|
-
return Promise.resolve();
|
|
7
|
-
},
|
|
8
|
-
unmount () {
|
|
9
|
-
return Promise.resolve();
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
1
|
export function routeRegex(regex, location) {
|
|
13
2
|
const result = regex.test(location.pathname.replace(window.getOpenmrsSpaBase(), ''));
|
|
14
3
|
return result;
|
package/dist/loaders/index.d.ts
CHANGED
package/dist/loaders/index.js
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type LifeCycles } from 'single-spa';
|
|
2
|
+
/**
|
|
3
|
+
* This function loads a single-spa Lifecycles from the given app. The Lifecycles should be
|
|
4
|
+
* created using `getAsyncLifecycle()` or `getSyncLifecycle()` and exported in the app's
|
|
5
|
+
* index.ts file.
|
|
6
|
+
* The function is lazy and ensures that the appropriate module is loaded,
|
|
7
|
+
* that the module's `startupApp()` is called before the component is loaded. It
|
|
8
|
+
* then calls the component function, which should return either a single-spa
|
|
9
|
+
* {@link LifeCycles} object or a {@link Promise} that will resolve to such an object.
|
|
10
|
+
*
|
|
11
|
+
* @param routesAppName The app name of the routes.json file defining the component to load
|
|
12
|
+
*
|
|
13
|
+
* @param fullComponentName The name of the component to load. Note that this can optionally
|
|
14
|
+
* include the appName to load the component from (in the format `${appName}#${componentName}`),
|
|
15
|
+
* or omitted to implicitly use routesAppName
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadLifeCycles(routesAppName: string, fullComponentName: string): Promise<LifeCycles>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { importDynamic } from "@openmrs/esm-dynamic-loading";
|
|
2
2
|
import { registerModuleLoad } from "@openmrs/esm-config";
|
|
3
|
-
import { emptyLifecycle } from "./helpers.js";
|
|
4
3
|
/**
|
|
5
4
|
* @internal
|
|
6
5
|
*
|
|
@@ -9,44 +8,50 @@ import { emptyLifecycle } from "./helpers.js";
|
|
|
9
8
|
* Values are promises to support asynchronous loading of the same app multiple times
|
|
10
9
|
*/ const initializedApps = new Map();
|
|
11
10
|
/**
|
|
12
|
-
* This function
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* The
|
|
11
|
+
* This function loads a single-spa Lifecycles from the given app. The Lifecycles should be
|
|
12
|
+
* created using `getAsyncLifecycle()` or `getSyncLifecycle()` and exported in the app's
|
|
13
|
+
* index.ts file.
|
|
14
|
+
* The function is lazy and ensures that the appropriate module is loaded,
|
|
16
15
|
* that the module's `startupApp()` is called before the component is loaded. It
|
|
17
16
|
* then calls the component function, which should return either a single-spa
|
|
18
17
|
* {@link LifeCycles} object or a {@link Promise} that will resolve to such an object.
|
|
19
18
|
*
|
|
20
|
-
* React-based pages or extensions should generally use the framework's
|
|
21
|
-
* `getAsyncLifecycle()` or `getSyncLifecycle()` functions.
|
|
22
|
-
*
|
|
23
19
|
* @param routesAppName The app name of the routes.json file defining the component to load
|
|
24
20
|
*
|
|
25
21
|
* @param fullComponentName The name of the component to load. Note that this can optionally
|
|
26
22
|
* include the appName to load the component from (in the format `${appName}#${componentName}`),
|
|
27
23
|
* or omitted to implicitly use routesAppName
|
|
28
24
|
*
|
|
29
|
-
*/ export function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
*/ export async function loadLifeCycles(routesAppName, fullComponentName) {
|
|
26
|
+
const poundIndex = fullComponentName.indexOf('#');
|
|
27
|
+
const isNamespaced = poundIndex >= 0;
|
|
28
|
+
const appName = isNamespaced ? fullComponentName.substring(0, poundIndex) : routesAppName;
|
|
29
|
+
const componentName = isNamespaced ? fullComponentName.substring(poundIndex + 1) : fullComponentName;
|
|
30
|
+
const module = await importDynamic(appName);
|
|
31
|
+
if (module && Object.hasOwn(module, componentName) && typeof module[componentName] === 'function') {
|
|
32
|
+
return initializeApp(appName, module).then(()=>module[componentName]());
|
|
33
|
+
} else {
|
|
34
|
+
if (!module) {
|
|
35
|
+
console.warn(`Unknown app ${appName} for ${fullComponentName} defined in ${routesAppName}'s routes.json`);
|
|
36
|
+
} else if (module && Object.hasOwn(module, componentName)) {
|
|
37
|
+
console.warn(`The export ${fullComponentName}, defined in ${routesAppName}'s routes.json, is not a function`);
|
|
38
38
|
} else {
|
|
39
|
-
|
|
40
|
-
console.warn(`Unknown app ${appName} for ${fullComponentName} defined in ${routesAppName}'s routes.json`);
|
|
41
|
-
} else if (module && Object.hasOwn(module, componentName)) {
|
|
42
|
-
console.warn(`The export ${fullComponentName}, defined in ${routesAppName}'s routes.json, is not a function`);
|
|
43
|
-
} else {
|
|
44
|
-
console.warn(`${appName} does not define a component called "${componentName}", referenced in ${routesAppName}'s routes.json. This cannot be loaded.`);
|
|
45
|
-
}
|
|
39
|
+
console.warn(`${appName} does not define a component called "${componentName}", referenced in ${routesAppName}'s routes.json. This cannot be loaded.`);
|
|
46
40
|
}
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
}
|
|
42
|
+
return emptyLifecycle;
|
|
49
43
|
}
|
|
44
|
+
const emptyLifecycle = {
|
|
45
|
+
bootstrap () {
|
|
46
|
+
return Promise.resolve();
|
|
47
|
+
},
|
|
48
|
+
mount () {
|
|
49
|
+
return Promise.resolve();
|
|
50
|
+
},
|
|
51
|
+
unmount () {
|
|
52
|
+
return Promise.resolve();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
50
55
|
/**
|
|
51
56
|
* This function can be used to manually initialize an application without waiting for the
|
|
52
57
|
* framework to load it. This can sometimes be helpful if the framework doesn't load code
|
|
@@ -60,7 +65,7 @@ import { emptyLifecycle } from "./helpers.js";
|
|
|
60
65
|
* @param module The loaded code module, if available; if this parameter is omitted, this
|
|
61
66
|
* function will load it.
|
|
62
67
|
* @returns a Promise which completes once the app has been loaded and initialized
|
|
63
|
-
*/
|
|
68
|
+
*/ async function initializeApp(appName, module) {
|
|
64
69
|
if (!(appName in initializedApps)) {
|
|
65
70
|
let _module = module ?? await importDynamic(appName);
|
|
66
71
|
await (initializedApps[appName] = new Promise((resolve, reject)=>{
|
package/dist/loaders/pages.js
CHANGED
|
@@ -2,8 +2,8 @@ import { pathToActiveWhen, registerApplication } from "single-spa";
|
|
|
2
2
|
import { registerModuleWithConfigSystem } from "@openmrs/esm-config";
|
|
3
3
|
import { getFeatureFlag } from "@openmrs/esm-feature-flags";
|
|
4
4
|
import { routeRegex } from "./helpers.js";
|
|
5
|
-
import { getLoader } from "./app.js";
|
|
6
5
|
import { tryRegisterExtension, tryRegisterFeatureFlag, tryRegisterModal, tryRegisterWorkspace, tryRegisterWorkspaceGroup } from "./components.js";
|
|
6
|
+
import { loadLifeCycles } from "./load-lifecycles.js";
|
|
7
7
|
// this is the global holder of all pages registered in the app
|
|
8
8
|
const pages = [];
|
|
9
9
|
/**
|
|
@@ -174,6 +174,6 @@ To fix this, ensure that you define the "component" field inside the page defini
|
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
176
|
const activityFn = wrapPageActivityFn(getActivityFn(route), page);
|
|
177
|
-
const loader =
|
|
177
|
+
const loader = ()=>loadLifeCycles(page.appName, page.component);
|
|
178
178
|
registerApplication(appName, loader, activityFn);
|
|
179
179
|
}
|
package/dist/public.d.ts
CHANGED
package/dist/public.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-routes",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1-pre.3256",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Utilities for working with the routes registry",
|
|
6
6
|
"type": "module",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"single-spa": "6.x"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@openmrs/esm-config": "
|
|
58
|
-
"@openmrs/esm-dynamic-loading": "
|
|
59
|
-
"@openmrs/esm-extensions": "
|
|
60
|
-
"@openmrs/esm-feature-flags": "
|
|
61
|
-
"@openmrs/esm-globals": "
|
|
62
|
-
"@openmrs/esm-utils": "
|
|
57
|
+
"@openmrs/esm-config": "7.0.1-pre.3256",
|
|
58
|
+
"@openmrs/esm-dynamic-loading": "7.0.1-pre.3256",
|
|
59
|
+
"@openmrs/esm-extensions": "7.0.1-pre.3256",
|
|
60
|
+
"@openmrs/esm-feature-flags": "7.0.1-pre.3256",
|
|
61
|
+
"@openmrs/esm-globals": "7.0.1-pre.3256",
|
|
62
|
+
"@openmrs/esm-utils": "7.0.1-pre.3256",
|
|
63
63
|
"@swc/cli": "^0.7.7",
|
|
64
64
|
"@swc/core": "^1.11.29",
|
|
65
65
|
"concurrently": "^9.1.2",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"vitest": "^3.1.4",
|
|
71
71
|
"vitest-fetch-mock": "^0.4.5"
|
|
72
72
|
},
|
|
73
|
-
"stableVersion": "
|
|
73
|
+
"stableVersion": "7.0.0"
|
|
74
74
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attach,
|
|
3
|
-
type ExtensionRegistration,
|
|
4
3
|
registerExtension,
|
|
5
4
|
registerModal,
|
|
6
5
|
registerWorkspace,
|
|
@@ -13,8 +12,8 @@ import {
|
|
|
13
12
|
type WorkspaceDefinition,
|
|
14
13
|
type WorkspaceGroupDefinition,
|
|
15
14
|
} from '@openmrs/esm-globals';
|
|
16
|
-
import { getLoader } from './app';
|
|
17
15
|
import { registerFeatureFlag } from '@openmrs/esm-feature-flags';
|
|
16
|
+
import { loadLifeCycles } from './load-lifecycles';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* This function registers an extension definition with the framework and will
|
|
@@ -43,7 +42,7 @@ a 'slot' property. Only the 'slots' property will be honored.`,
|
|
|
43
42
|
}
|
|
44
43
|
const slots = extension.slots ? extension.slots : extension.slot ? [extension.slot] : [];
|
|
45
44
|
|
|
46
|
-
if (!extension.component
|
|
45
|
+
if (!extension.component) {
|
|
47
46
|
console.error(
|
|
48
47
|
`The extension ${name} from ${appName} is missing a 'component' entry and thus cannot be registered.
|
|
49
48
|
To fix this, ensure that you define a 'component' field inside the extension definition.`,
|
|
@@ -52,33 +51,17 @@ To fix this, ensure that you define a 'component' field inside the extension def
|
|
|
52
51
|
return;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
loader = extension.load;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (loader) {
|
|
70
|
-
registerExtension({
|
|
71
|
-
name,
|
|
72
|
-
load: loader,
|
|
73
|
-
meta: extension.meta || {},
|
|
74
|
-
order: extension.order,
|
|
75
|
-
moduleName: appName,
|
|
76
|
-
privileges: extension.privileges,
|
|
77
|
-
online: extension.online ?? true,
|
|
78
|
-
offline: extension.offline ?? false,
|
|
79
|
-
featureFlag: extension.featureFlag,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
54
|
+
registerExtension({
|
|
55
|
+
name,
|
|
56
|
+
load: () => loadLifeCycles(appName, extension.component),
|
|
57
|
+
meta: extension.meta || {},
|
|
58
|
+
order: extension.order,
|
|
59
|
+
moduleName: appName,
|
|
60
|
+
privileges: extension.privileges,
|
|
61
|
+
online: extension.online ?? true,
|
|
62
|
+
offline: extension.offline ?? false,
|
|
63
|
+
featureFlag: extension.featureFlag,
|
|
64
|
+
});
|
|
82
65
|
|
|
83
66
|
for (const slot of slots) {
|
|
84
67
|
attach(slot, name);
|
|
@@ -103,7 +86,7 @@ modal definition.`,
|
|
|
103
86
|
return;
|
|
104
87
|
}
|
|
105
88
|
|
|
106
|
-
if (!modal.component
|
|
89
|
+
if (!modal.component) {
|
|
107
90
|
console.error(
|
|
108
91
|
`The modal ${name} from ${appName} is missing a 'component' entry and thus cannot be registered.
|
|
109
92
|
To fix this, ensure that you define a 'component' field inside the modal definition.`,
|
|
@@ -112,27 +95,11 @@ To fix this, ensure that you define a 'component' field inside the modal definit
|
|
|
112
95
|
return;
|
|
113
96
|
}
|
|
114
97
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
console.error(
|
|
121
|
-
`The modal ${name} from ${appName} declares a 'load' property that is not a function. This is not
|
|
122
|
-
supported, so the modal will not be loaded.`,
|
|
123
|
-
);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
loader = modal.load;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (loader) {
|
|
130
|
-
registerModal({
|
|
131
|
-
name,
|
|
132
|
-
load: loader,
|
|
133
|
-
moduleName: appName,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
98
|
+
registerModal({
|
|
99
|
+
name,
|
|
100
|
+
moduleName: appName,
|
|
101
|
+
load: () => loadLifeCycles(appName, modal.component),
|
|
102
|
+
});
|
|
136
103
|
}
|
|
137
104
|
|
|
138
105
|
/**
|
|
@@ -162,7 +129,7 @@ To fix this, ensure that you define the "title" field inside the workspace defin
|
|
|
162
129
|
return;
|
|
163
130
|
}
|
|
164
131
|
|
|
165
|
-
if (!workspace.component
|
|
132
|
+
if (!workspace.component) {
|
|
166
133
|
console.error(
|
|
167
134
|
`The workspace ${name} from ${appName} is missing a 'component' entry and thus cannot be registered.
|
|
168
135
|
To fix this, ensure that you define a 'component' field inside the workspace definition.`,
|
|
@@ -171,34 +138,19 @@ To fix this, ensure that you define a 'component' field inside the workspace def
|
|
|
171
138
|
return;
|
|
172
139
|
}
|
|
173
140
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (loader) {
|
|
189
|
-
registerWorkspace({
|
|
190
|
-
name,
|
|
191
|
-
title,
|
|
192
|
-
load: loader,
|
|
193
|
-
moduleName: appName,
|
|
194
|
-
type: workspace.type,
|
|
195
|
-
canHide: workspace.canHide,
|
|
196
|
-
canMaximize: workspace.canMaximize,
|
|
197
|
-
width: workspace.width,
|
|
198
|
-
preferredWindowSize: workspace.preferredWindowSize,
|
|
199
|
-
groups: workspace.groups,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
141
|
+
registerWorkspace({
|
|
142
|
+
name,
|
|
143
|
+
title,
|
|
144
|
+
component: workspace.component,
|
|
145
|
+
moduleName: appName,
|
|
146
|
+
type: workspace.type,
|
|
147
|
+
canHide: workspace.canHide,
|
|
148
|
+
canMaximize: workspace.canMaximize,
|
|
149
|
+
width: workspace.width,
|
|
150
|
+
preferredWindowSize: workspace.preferredWindowSize,
|
|
151
|
+
groups: workspace.groups,
|
|
152
|
+
load: () => loadLifeCycles(appName, workspace.component),
|
|
153
|
+
});
|
|
202
154
|
|
|
203
155
|
for (const group of workspace.groups || []) {
|
|
204
156
|
registerWorkspaceGroup({
|
package/src/loaders/helpers.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
import { type LifeCycles } from 'single-spa';
|
|
2
|
-
|
|
3
|
-
export const emptyLifecycle: LifeCycles<never> = {
|
|
4
|
-
bootstrap() {
|
|
5
|
-
return Promise.resolve();
|
|
6
|
-
},
|
|
7
|
-
mount() {
|
|
8
|
-
return Promise.resolve();
|
|
9
|
-
},
|
|
10
|
-
unmount() {
|
|
11
|
-
return Promise.resolve();
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
1
|
export function routeRegex(regex: RegExp, location: Location) {
|
|
16
2
|
const result = regex.test(location.pathname.replace(window.getOpenmrsSpaBase(), ''));
|
|
17
3
|
return result;
|
package/src/loaders/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { importDynamic } from '@openmrs/esm-dynamic-loading';
|
|
2
|
-
import { registerModuleLoad } from '@openmrs/esm-config';
|
|
3
2
|
import { type LifeCycles } from 'single-spa';
|
|
4
|
-
import {
|
|
3
|
+
import { registerModuleLoad } from '@openmrs/esm-config';
|
|
5
4
|
|
|
6
5
|
// Couldn't figure out how to express this as an interface
|
|
7
6
|
type Module = Omit<Record<string, () => LifeCycles | Promise<LifeCycles>>, 'startupApp'> & {
|
|
@@ -18,17 +17,14 @@ type Module = Omit<Record<string, () => LifeCycles | Promise<LifeCycles>>, 'star
|
|
|
18
17
|
const initializedApps = new Map<string, Promise<unknown>>();
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
|
-
* This function
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* The
|
|
20
|
+
* This function loads a single-spa Lifecycles from the given app. The Lifecycles should be
|
|
21
|
+
* created using `getAsyncLifecycle()` or `getSyncLifecycle()` and exported in the app's
|
|
22
|
+
* index.ts file.
|
|
23
|
+
* The function is lazy and ensures that the appropriate module is loaded,
|
|
25
24
|
* that the module's `startupApp()` is called before the component is loaded. It
|
|
26
25
|
* then calls the component function, which should return either a single-spa
|
|
27
26
|
* {@link LifeCycles} object or a {@link Promise} that will resolve to such an object.
|
|
28
27
|
*
|
|
29
|
-
* React-based pages or extensions should generally use the framework's
|
|
30
|
-
* `getAsyncLifecycle()` or `getSyncLifecycle()` functions.
|
|
31
|
-
*
|
|
32
28
|
* @param routesAppName The app name of the routes.json file defining the component to load
|
|
33
29
|
*
|
|
34
30
|
* @param fullComponentName The name of the component to load. Note that this can optionally
|
|
@@ -36,33 +32,43 @@ const initializedApps = new Map<string, Promise<unknown>>();
|
|
|
36
32
|
* or omitted to implicitly use routesAppName
|
|
37
33
|
*
|
|
38
34
|
*/
|
|
39
|
-
export function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const componentName = isNamespaced ? fullComponentName.substring(poundIndex + 1) : fullComponentName;
|
|
35
|
+
export async function loadLifeCycles(routesAppName: string, fullComponentName: string): Promise<LifeCycles> {
|
|
36
|
+
const poundIndex = fullComponentName.indexOf('#');
|
|
37
|
+
const isNamespaced = poundIndex >= 0;
|
|
38
|
+
const appName = isNamespaced ? fullComponentName.substring(0, poundIndex) : routesAppName;
|
|
39
|
+
const componentName = isNamespaced ? fullComponentName.substring(poundIndex + 1) : fullComponentName;
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
const module = await importDynamic<Module>(appName);
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
if (module && Object.hasOwn(module, componentName) && typeof module[componentName] === 'function') {
|
|
44
|
+
return initializeApp(appName, module).then(() => module[componentName]());
|
|
45
|
+
} else {
|
|
46
|
+
if (!module) {
|
|
47
|
+
console.warn(`Unknown app ${appName} for ${fullComponentName} defined in ${routesAppName}'s routes.json`);
|
|
48
|
+
} else if (module && Object.hasOwn(module, componentName)) {
|
|
49
|
+
console.warn(`The export ${fullComponentName}, defined in ${routesAppName}'s routes.json, is not a function`);
|
|
50
50
|
} else {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
console.warn(`The export ${fullComponentName}, defined in ${routesAppName}'s routes.json, is not a function`);
|
|
55
|
-
} else {
|
|
56
|
-
console.warn(
|
|
57
|
-
`${appName} does not define a component called "${componentName}", referenced in ${routesAppName}'s routes.json. This cannot be loaded.`,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
51
|
+
console.warn(
|
|
52
|
+
`${appName} does not define a component called "${componentName}", referenced in ${routesAppName}'s routes.json. This cannot be loaded.`,
|
|
53
|
+
);
|
|
60
54
|
}
|
|
55
|
+
}
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
};
|
|
57
|
+
return emptyLifecycle;
|
|
64
58
|
}
|
|
65
59
|
|
|
60
|
+
const emptyLifecycle: LifeCycles<never> = {
|
|
61
|
+
bootstrap() {
|
|
62
|
+
return Promise.resolve();
|
|
63
|
+
},
|
|
64
|
+
mount() {
|
|
65
|
+
return Promise.resolve();
|
|
66
|
+
},
|
|
67
|
+
unmount() {
|
|
68
|
+
return Promise.resolve();
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
66
72
|
/**
|
|
67
73
|
* This function can be used to manually initialize an application without waiting for the
|
|
68
74
|
* framework to load it. This can sometimes be helpful if the framework doesn't load code
|
|
@@ -77,7 +83,7 @@ export function getLoader(routesAppName: string, fullComponentName: string): ()
|
|
|
77
83
|
* function will load it.
|
|
78
84
|
* @returns a Promise which completes once the app has been loaded and initialized
|
|
79
85
|
*/
|
|
80
|
-
|
|
86
|
+
async function initializeApp(appName: string, module?: Module) {
|
|
81
87
|
if (!(appName in initializedApps)) {
|
|
82
88
|
let _module: Module = module ?? (await importDynamic<Module>(appName));
|
|
83
89
|
|
package/src/loaders/pages.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from '@openmrs/esm-globals';
|
|
13
13
|
import { getFeatureFlag } from '@openmrs/esm-feature-flags';
|
|
14
14
|
import { routeRegex } from './helpers';
|
|
15
|
-
import { getLoader } from './app';
|
|
16
15
|
import {
|
|
17
16
|
tryRegisterExtension,
|
|
18
17
|
tryRegisterFeatureFlag,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
tryRegisterWorkspace,
|
|
21
20
|
tryRegisterWorkspaceGroup,
|
|
22
21
|
} from './components';
|
|
22
|
+
import { loadLifeCycles } from './load-lifecycles';
|
|
23
23
|
|
|
24
24
|
// this is the global holder of all pages registered in the app
|
|
25
25
|
const pages: Array<RegisteredPageDefinition> = [];
|
|
@@ -257,6 +257,6 @@ To fix this, ensure that you define the "component" field inside the page defini
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
const activityFn = wrapPageActivityFn(getActivityFn(route), page);
|
|
260
|
-
const loader =
|
|
260
|
+
const loader = () => loadLifeCycles(page.appName, page.component);
|
|
261
261
|
registerApplication(appName, loader, activityFn);
|
|
262
262
|
}
|
package/src/public.ts
CHANGED
package/dist/loaders/app.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { type LifeCycles } from 'single-spa';
|
|
2
|
-
type Module = Omit<Record<string, () => LifeCycles | Promise<LifeCycles>>, 'startupApp'> & {
|
|
3
|
-
startupApp?: () => unknown;
|
|
4
|
-
};
|
|
5
|
-
/**
|
|
6
|
-
* This function creates a loader function suitable for use in either a single-spa
|
|
7
|
-
* application or parcel.
|
|
8
|
-
*
|
|
9
|
-
* The returned function is lazy and ensures that the appropriate module is loaded,
|
|
10
|
-
* that the module's `startupApp()` is called before the component is loaded. It
|
|
11
|
-
* then calls the component function, which should return either a single-spa
|
|
12
|
-
* {@link LifeCycles} object or a {@link Promise} that will resolve to such an object.
|
|
13
|
-
*
|
|
14
|
-
* React-based pages or extensions should generally use the framework's
|
|
15
|
-
* `getAsyncLifecycle()` or `getSyncLifecycle()` functions.
|
|
16
|
-
*
|
|
17
|
-
* @param routesAppName The app name of the routes.json file defining the component to load
|
|
18
|
-
*
|
|
19
|
-
* @param fullComponentName The name of the component to load. Note that this can optionally
|
|
20
|
-
* include the appName to load the component from (in the format `${appName}#${componentName}`),
|
|
21
|
-
* or omitted to implicitly use routesAppName
|
|
22
|
-
*
|
|
23
|
-
*/
|
|
24
|
-
export declare function getLoader(routesAppName: string, fullComponentName: string): () => Promise<LifeCycles>;
|
|
25
|
-
/**
|
|
26
|
-
* This function can be used to manually initialize an application without waiting for the
|
|
27
|
-
* framework to load it. This can sometimes be helpful if the framework doesn't load code
|
|
28
|
-
* at the point you think is appropriate.
|
|
29
|
-
*
|
|
30
|
-
* This will ensure that the module is available and that it's `startupApp()` function (if
|
|
31
|
-
* any) will be called. Note that these are only guaranteed to be complete once the Promise
|
|
32
|
-
* returned from the function completes.
|
|
33
|
-
*
|
|
34
|
-
* @param appName The name of the app to initialize
|
|
35
|
-
* @param module The loaded code module, if available; if this parameter is omitted, this
|
|
36
|
-
* function will load it.
|
|
37
|
-
* @returns a Promise which completes once the app has been loaded and initialized
|
|
38
|
-
*/
|
|
39
|
-
export declare function initializeApp(appName: string, module?: Module): Promise<void>;
|
|
40
|
-
export {};
|