@openmrs/esm-app-shell 9.0.3-pre.4533 → 9.0.3-pre.4550
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 +3 -3
- package/dist/{bd6bc6523637229c.js.map → 1ab781d701d49ae6.js.map} +1 -1
- package/dist/89e1c6e74f473c5c.js +1 -0
- package/dist/89e1c6e74f473c5c.js.map +1 -0
- package/dist/{2d50233a243c3173.js → c8a447a932d1ebef.js} +1 -1
- package/dist/c8a447a932d1ebef.js.map +1 -0
- package/dist/index.html +1 -1
- package/dist/openmrs.8510b4d06a7d291b.js +5 -0
- package/dist/openmrs.8510b4d06a7d291b.js.map +1 -0
- package/lib/4925fcf077dcfe9f.js +42 -0
- package/lib/{cca0d70ddc22e728.js → 81012d11162cd749.js} +1 -1
- package/lib/{096d8819045782aa.js → 8fc619183a4e4ee5.js} +20 -15
- package/lib/openmrs.js +3 -7
- package/package.json +3 -4
- package/rspack.config.js +35 -1
- package/src/index.ts +60 -20
- package/src/locale.ts +6 -1
- package/src/run.ts +18 -103
- package/src/service-worker/message.ts +15 -0
- package/tsconfig.json +1 -1
- package/dist/2d50233a243c3173.js.map +0 -1
- package/dist/dd18f784d87a6e9d.js +0 -1
- package/dist/dd18f784d87a6e9d.js.map +0 -1
- package/dist/openmrs.e015dc44a897a08f.js +0 -5
- package/dist/openmrs.e015dc44a897a08f.js.map +0 -1
- package/lib/83a407c428ad1840.js +0 -42
- /package/dist/{bd6bc6523637229c.js → 1ab781d701d49ae6.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-app-shell",
|
|
3
|
-
"version": "9.0.3-pre.
|
|
3
|
+
"version": "9.0.3-pre.4550",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"main": "dist/openmrs.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@carbon/react": "^1.92.1",
|
|
37
37
|
"@internationalized/date": "^3.8.0",
|
|
38
|
-
"@openmrs/esm-framework": "9.0.3-pre.
|
|
39
|
-
"@openmrs/esm-styleguide": "9.0.3-pre.
|
|
38
|
+
"@openmrs/esm-framework": "9.0.3-pre.4550",
|
|
39
|
+
"@openmrs/esm-styleguide": "9.0.3-pre.4550",
|
|
40
40
|
"@rspack/cli": "1.7.9",
|
|
41
41
|
"@rspack/core": "1.7.9",
|
|
42
42
|
"dayjs": "^1.11.13",
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"html-webpack-plugin": "5.6.6",
|
|
45
45
|
"i18next": "^25.5.3",
|
|
46
46
|
"i18next-browser-languagedetector": "^8.2.0",
|
|
47
|
-
"import-map-overrides": "^3.0.0",
|
|
48
47
|
"lodash-es": "^4.17.21",
|
|
49
48
|
"mini-css-extract-plugin": "2.9.1",
|
|
50
49
|
"react": "^18.3.1",
|
package/rspack.config.js
CHANGED
|
@@ -30,7 +30,40 @@ const openmrsPublicPath = removeTrailingSlash(process.env.OMRS_PUBLIC_PATH || '/
|
|
|
30
30
|
const openmrsProxyTarget = process.env.OMRS_PROXY_TARGET || 'https://dev3.openmrs.org/';
|
|
31
31
|
const openmrsPageTitle = process.env.OMRS_PAGE_TITLE || 'OpenMRS';
|
|
32
32
|
const openmrsFavicon = process.env.OMRS_FAVICON || `${openmrsPublicPath}/favicon.ico`;
|
|
33
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the target environment from OMRS_ENV, falling back to NODE_ENV / build mode.
|
|
35
|
+
*
|
|
36
|
+
* Accepts aliases ("prod" → "production", "dev" → "development") and defaults
|
|
37
|
+
* to "production" when nothing is set — so dev features are never accidentally
|
|
38
|
+
* enabled in an unconfigured build.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} buildMode rspack/webpack build mode ("production" | "development")
|
|
41
|
+
* @returns {"production" | "development" | "test"}
|
|
42
|
+
*/
|
|
43
|
+
function resolveEnvironment(buildMode) {
|
|
44
|
+
const raw = process.env.OMRS_ENV;
|
|
45
|
+
|
|
46
|
+
if (raw) {
|
|
47
|
+
switch (raw) {
|
|
48
|
+
case 'production':
|
|
49
|
+
case 'prod':
|
|
50
|
+
return 'production';
|
|
51
|
+
case 'development':
|
|
52
|
+
case 'dev':
|
|
53
|
+
return 'development';
|
|
54
|
+
case 'test':
|
|
55
|
+
return 'test';
|
|
56
|
+
default:
|
|
57
|
+
console.warn(`Unknown OMRS_ENV value "${raw}", defaulting to "production".`);
|
|
58
|
+
return 'production';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// No explicit OMRS_ENV — derive from NODE_ENV or build mode.
|
|
63
|
+
// Only "development" is treated as development; everything else is production.
|
|
64
|
+
const fallback = process.env.NODE_ENV || buildMode || '';
|
|
65
|
+
return fallback === 'development' ? 'development' : 'production';
|
|
66
|
+
}
|
|
34
67
|
const openmrsOffline = process.env.OMRS_OFFLINE === 'enable';
|
|
35
68
|
const openmrsDefaultLocale = process.env.OMRS_ESM_DEFAULT_LOCALE || 'en';
|
|
36
69
|
const openmrsImportmapDef = process.env.OMRS_ESM_IMPORTMAP;
|
|
@@ -107,6 +140,7 @@ module.exports = (env, argv = []) => {
|
|
|
107
140
|
const mode = argv.mode || process.env.NODE_ENV || production;
|
|
108
141
|
const outDir = mode === production ? 'dist' : 'lib';
|
|
109
142
|
const isProd = mode === 'production';
|
|
143
|
+
const openmrsEnvironment = resolveEnvironment(mode);
|
|
110
144
|
const appPatterns = [];
|
|
111
145
|
|
|
112
146
|
const coreImportmap = {
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'import-map-overrides';
|
|
2
1
|
import type { SpaConfig } from '@openmrs/esm-framework/src/internal';
|
|
3
2
|
|
|
4
3
|
function _createSpaBase(baseUrl: string) {
|
|
@@ -27,27 +26,53 @@ function setupPaths(config: SpaConfig) {
|
|
|
27
26
|
);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
window
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
// Object.defineProperty used to make these read-only
|
|
30
|
+
Object.defineProperty(window, 'openmrsBase', {
|
|
31
|
+
value: config.apiUrl,
|
|
32
|
+
writable: false,
|
|
33
|
+
configurable: false,
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(window, 'spaBase', {
|
|
36
|
+
value: config.spaPath,
|
|
37
|
+
writable: false,
|
|
38
|
+
configurable: false,
|
|
39
|
+
});
|
|
40
|
+
Object.defineProperty(window, 'spaEnv', {
|
|
41
|
+
value: config.env || 'production',
|
|
42
|
+
writable: false,
|
|
43
|
+
configurable: false,
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(window, 'spaVersion', {
|
|
46
|
+
value: process.env.BUILD_VERSION ?? 'local',
|
|
47
|
+
writable: false,
|
|
48
|
+
configurable: false,
|
|
49
|
+
});
|
|
50
|
+
|
|
34
51
|
const spaBaseWithSlash = window.spaBase.endsWith('/') ? window.spaBase : window.spaBase + '/';
|
|
35
|
-
window
|
|
52
|
+
Object.defineProperty(window, 'getOpenmrsSpaBase', {
|
|
53
|
+
value: _createSpaBase(spaBaseWithSlash),
|
|
54
|
+
writable: false,
|
|
55
|
+
configurable: false,
|
|
56
|
+
});
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
export function setupUtils() {
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
Object.defineProperty(window, 'copyText', {
|
|
61
|
+
value: (source: HTMLElement) => {
|
|
62
|
+
const sel = window.getSelection();
|
|
41
63
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
if (sel) {
|
|
65
|
+
const r = document.createRange();
|
|
66
|
+
r.selectNode(source);
|
|
67
|
+
sel.removeAllRanges();
|
|
68
|
+
sel.addRange(r);
|
|
69
|
+
document.execCommand('copy');
|
|
70
|
+
sel.removeAllRanges();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
writable: false,
|
|
74
|
+
configurable: false,
|
|
75
|
+
});
|
|
51
76
|
}
|
|
52
77
|
|
|
53
78
|
function wireSpaPaths() {
|
|
@@ -58,15 +83,21 @@ function wireSpaPaths() {
|
|
|
58
83
|
__webpack_public_path__ = baseHref;
|
|
59
84
|
}
|
|
60
85
|
|
|
86
|
+
let initPromise: Promise<void> | null = null;
|
|
87
|
+
|
|
61
88
|
/**
|
|
62
89
|
* Initializes the OpenMRS Frontend App Shell.
|
|
63
90
|
* @param config The global configuration to apply.
|
|
64
91
|
*/
|
|
65
92
|
function initializeSpa(config: SpaConfig) {
|
|
93
|
+
if (initPromise) {
|
|
94
|
+
return initPromise;
|
|
95
|
+
}
|
|
96
|
+
|
|
66
97
|
setupUtils();
|
|
67
98
|
setupPaths(config);
|
|
68
99
|
wireSpaPaths();
|
|
69
|
-
|
|
100
|
+
initPromise = Promise.resolve(__webpack_init_sharing__('default')).then(async () => {
|
|
70
101
|
const shareScope = __webpack_share_scopes__.default;
|
|
71
102
|
// MF will deduplicate these as they're aliased at build time, but at runtime
|
|
72
103
|
// apps try to load `@openmrs/esm-framework`, so here we provide a runtime
|
|
@@ -76,11 +107,20 @@ function initializeSpa(config: SpaConfig) {
|
|
|
76
107
|
}
|
|
77
108
|
|
|
78
109
|
const { configUrls = [], offline = false } = config;
|
|
79
|
-
window
|
|
110
|
+
Object.defineProperty(window, 'offlineEnabled', {
|
|
111
|
+
value: offline,
|
|
112
|
+
writable: false,
|
|
113
|
+
configurable: false,
|
|
114
|
+
});
|
|
80
115
|
|
|
81
116
|
const { run } = await import(/* webpackPreload: true */ './run');
|
|
82
117
|
return run(configUrls);
|
|
83
118
|
});
|
|
119
|
+
return initPromise;
|
|
84
120
|
}
|
|
85
121
|
|
|
86
|
-
window
|
|
122
|
+
Object.defineProperty(window, 'initializeSpa', {
|
|
123
|
+
value: initializeSpa,
|
|
124
|
+
writable: false,
|
|
125
|
+
configurable: false,
|
|
126
|
+
});
|
package/src/locale.ts
CHANGED
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
registerTranslationNamespace('core');
|
|
12
12
|
|
|
13
13
|
export function setupI18n() {
|
|
14
|
-
const i18n =
|
|
14
|
+
const i18n = i18next.default || i18next;
|
|
15
|
+
Object.defineProperty(window, 'i18next', {
|
|
16
|
+
value: i18n,
|
|
17
|
+
writable: false,
|
|
18
|
+
configurable: false,
|
|
19
|
+
});
|
|
15
20
|
|
|
16
21
|
const languageChangeObserver = new MutationObserver(() => {
|
|
17
22
|
i18n.changeLanguage().catch((e) => console.error('i18next failed to re-detect language', e));
|
package/src/run.ts
CHANGED
|
@@ -2,19 +2,17 @@ import { start, triggerAppChange } from 'single-spa';
|
|
|
2
2
|
import { type CalendarIdentifier } from '@internationalized/date';
|
|
3
3
|
import {
|
|
4
4
|
activateOfflineCapability,
|
|
5
|
-
canAccessStorage,
|
|
6
5
|
dispatchConnectivityChanged,
|
|
7
6
|
dispatchPrecacheStaticDependencies,
|
|
8
7
|
type ExtensionDefinition,
|
|
9
8
|
finishRegisteringAllApps,
|
|
10
9
|
fireOpenmrsEvent,
|
|
11
10
|
getConfig,
|
|
11
|
+
getCurrentPageMap,
|
|
12
|
+
getCurrentRouteMap,
|
|
12
13
|
getCurrentUser,
|
|
13
14
|
integrateBreakpoints,
|
|
14
15
|
interpolateUrl,
|
|
15
|
-
isOpenmrsAppRoutes,
|
|
16
|
-
isOpenmrsRoutes,
|
|
17
|
-
localStorageRoutesPrefix,
|
|
18
16
|
messageOmrsServiceWorker,
|
|
19
17
|
openmrsFetch,
|
|
20
18
|
provide,
|
|
@@ -30,7 +28,9 @@ import {
|
|
|
30
28
|
restBaseUrl,
|
|
31
29
|
setupApiModule,
|
|
32
30
|
setupHistory,
|
|
31
|
+
setupImportMapOverrides,
|
|
33
32
|
setupModals,
|
|
33
|
+
setupRouteMapOverrides,
|
|
34
34
|
showActionableNotification,
|
|
35
35
|
showNotification,
|
|
36
36
|
showSnackbar,
|
|
@@ -43,8 +43,6 @@ import {
|
|
|
43
43
|
subscribeToastShown,
|
|
44
44
|
tryRegisterExtension,
|
|
45
45
|
type Config,
|
|
46
|
-
type OpenmrsAppRoutes,
|
|
47
|
-
type OpenmrsRoutes,
|
|
48
46
|
type StyleguideConfigObject,
|
|
49
47
|
} from '@openmrs/esm-framework/src/internal';
|
|
50
48
|
import { setupI18n } from './locale';
|
|
@@ -65,106 +63,21 @@ const REGISTRATION_PROMISES = Symbol('openmrs_registration_promises');
|
|
|
65
63
|
* as the registry of all apps in the application.
|
|
66
64
|
*/
|
|
67
65
|
async function setupApps() {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const promises: Array<Promise<OpenmrsRoutes>> = [];
|
|
71
|
-
for (let i = 0; i < scriptTags.length; i++) {
|
|
72
|
-
promises.push(
|
|
73
|
-
(async (scriptTag) => {
|
|
74
|
-
let routes: OpenmrsRoutes | undefined = undefined;
|
|
75
|
-
try {
|
|
76
|
-
if (scriptTag.textContent) {
|
|
77
|
-
routes = JSON.parse(scriptTag.textContent) as OpenmrsRoutes;
|
|
78
|
-
} else if (scriptTag.src) {
|
|
79
|
-
routes = (await openmrsFetch<OpenmrsRoutes>(scriptTag.src)).data;
|
|
80
|
-
}
|
|
81
|
-
} catch (e) {
|
|
82
|
-
console.error(`Caught error while loading routes from ${scriptTag.src ?? 'JSON script tag content'}`, e);
|
|
83
|
-
|
|
84
|
-
return {};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return Promise.resolve(routes ?? {});
|
|
88
|
-
})(scriptTags.item(i)),
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (canAccessStorage()) {
|
|
93
|
-
// load routes overrides from localStorage if any
|
|
94
|
-
const localStorage = window.localStorage;
|
|
95
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
96
|
-
const key = localStorage.key(i);
|
|
97
|
-
if (key?.startsWith(localStorageRoutesPrefix)) {
|
|
98
|
-
const localOverride = localStorage.getItem(key);
|
|
99
|
-
if (localOverride) {
|
|
100
|
-
try {
|
|
101
|
-
const maybeOpenmrsRoutes = JSON.parse(localOverride);
|
|
102
|
-
if (isOpenmrsAppRoutes(maybeOpenmrsRoutes)) {
|
|
103
|
-
promises.push(
|
|
104
|
-
Promise.resolve<OpenmrsRoutes>({
|
|
105
|
-
[key.slice(localStorageRoutesPrefix.length)]: maybeOpenmrsRoutes,
|
|
106
|
-
}),
|
|
107
|
-
);
|
|
108
|
-
} else if (typeof maybeOpenmrsRoutes === 'string' && maybeOpenmrsRoutes.startsWith('http')) {
|
|
109
|
-
promises.push(
|
|
110
|
-
openmrsFetch<OpenmrsAppRoutes>(maybeOpenmrsRoutes)
|
|
111
|
-
.then((response) => {
|
|
112
|
-
if (isOpenmrsAppRoutes(response.data)) {
|
|
113
|
-
return Promise.resolve({
|
|
114
|
-
[key.slice(localStorageRoutesPrefix.length)]: response.data,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return Promise.reject(
|
|
119
|
-
`${maybeOpenmrsRoutes} did not resolve to a valid OpenmrsAppRoutes JSON object`,
|
|
120
|
-
);
|
|
121
|
-
})
|
|
122
|
-
.catch((reason) => {
|
|
123
|
-
console.warn(
|
|
124
|
-
`Failed to fetch route overrides for ${key.slice(localStorageRoutesPrefix.length)}`,
|
|
125
|
-
reason,
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
// still fail the promise
|
|
129
|
-
throw reason;
|
|
130
|
-
}),
|
|
131
|
-
);
|
|
132
|
-
} else {
|
|
133
|
-
console.warn(
|
|
134
|
-
`Route override for ${key.slice(
|
|
135
|
-
localStorageRoutesPrefix.length,
|
|
136
|
-
)} could not be handled as it was neither a JSON object nor a URL string`,
|
|
137
|
-
localOverride,
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
} catch (e) {
|
|
141
|
-
console.error(`Error parsing local route override for ${key}`, e);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const routes: OpenmrsRoutes = (await Promise.allSettled(promises))
|
|
149
|
-
.filter((p) => p.status === 'fulfilled')
|
|
150
|
-
.map((p) => (p as PromiseFulfilledResult<OpenmrsRoutes>).value)
|
|
151
|
-
.filter(isOpenmrsRoutes)
|
|
152
|
-
.reduce(
|
|
153
|
-
(accumulatedRoutes, routes) => ({
|
|
154
|
-
...accumulatedRoutes,
|
|
155
|
-
...routes,
|
|
156
|
-
}),
|
|
157
|
-
{},
|
|
158
|
-
);
|
|
66
|
+
await setupRouteMapOverrides();
|
|
67
|
+
const routes = await getCurrentRouteMap();
|
|
159
68
|
|
|
160
69
|
const modules: typeof window.installedModules = [];
|
|
161
|
-
const registrationPromises = Object.entries(routes).map(async ([module,
|
|
162
|
-
modules.push([module,
|
|
163
|
-
registerApp(module,
|
|
70
|
+
const registrationPromises = Object.entries(routes).map(async ([module, appRoutes]) => {
|
|
71
|
+
modules.push([module, appRoutes]);
|
|
72
|
+
registerApp(module, appRoutes);
|
|
164
73
|
});
|
|
165
74
|
|
|
166
75
|
window[REGISTRATION_PROMISES] = Promise.all(registrationPromises);
|
|
167
|
-
window
|
|
76
|
+
Object.defineProperty(window, 'installedModules', {
|
|
77
|
+
value: modules,
|
|
78
|
+
writable: false,
|
|
79
|
+
configurable: false,
|
|
80
|
+
});
|
|
168
81
|
}
|
|
169
82
|
|
|
170
83
|
/**
|
|
@@ -385,7 +298,7 @@ async function precacheGlobalStaticDependencies() {
|
|
|
385
298
|
}
|
|
386
299
|
|
|
387
300
|
async function precacheImportMap() {
|
|
388
|
-
const importMap = await
|
|
301
|
+
const importMap = await getCurrentPageMap();
|
|
389
302
|
await messageOmrsServiceWorker({
|
|
390
303
|
type: 'onImportMapChanged',
|
|
391
304
|
importMap,
|
|
@@ -409,6 +322,8 @@ function setupOfflineCssClasses() {
|
|
|
409
322
|
}
|
|
410
323
|
|
|
411
324
|
export function run(configUrls: Array<string>) {
|
|
325
|
+
setupImportMapOverrides();
|
|
326
|
+
|
|
412
327
|
const offlineEnabled = window.offlineEnabled;
|
|
413
328
|
const closeLoading = showLoadingSpinner();
|
|
414
329
|
const provideConfigs = createConfigLoader(configUrls);
|
|
@@ -441,7 +356,7 @@ export function run(configUrls: Array<string>) {
|
|
|
441
356
|
|
|
442
357
|
return polyfillReady
|
|
443
358
|
.then(setupApps)
|
|
444
|
-
.then(() => finishRegisteringAllApps())
|
|
359
|
+
.then(() => Promise.resolve(finishRegisteringAllApps()))
|
|
445
360
|
.then(offlineEnabled ? setupOfflineCssClasses : undefined)
|
|
446
361
|
.then(offlineEnabled ? registerOfflineHandlers : undefined)
|
|
447
362
|
.then(provideConfigs)
|
|
@@ -47,6 +47,21 @@ async function registerDynamicRoute({ pattern, url, strategy }: RegisterDynamicR
|
|
|
47
47
|
* @param event The event data containing the message dispatched to the Service Worker.
|
|
48
48
|
*/
|
|
49
49
|
export async function handleMessage(event: ExtendableMessageEvent) {
|
|
50
|
+
// Defense-in-depth: only process messages from same-origin Client sources.
|
|
51
|
+
// Reject messages with no source or from cross-origin sources.
|
|
52
|
+
if (!event.source || !('url' in event.source)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let sourceOrigin: string;
|
|
56
|
+
try {
|
|
57
|
+
sourceOrigin = new URL(event.source.url).origin;
|
|
58
|
+
} catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (sourceOrigin !== self.location.origin) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
const handler = messageHandlers[event.data?.type?.toString() ?? ''];
|
|
51
66
|
|
|
52
67
|
if (!handler) {
|