@openmrs/esm-routes 5.3.3-pre.1268

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.
@@ -0,0 +1,11 @@
1
+ @openmrs/esm-routes:build: cache hit, replaying output 707c5d2b5ef0dfc3
2
+ @openmrs/esm-routes:build: Browserslist: caniuse-lite is outdated. Please run:
3
+ @openmrs/esm-routes:build:  npx update-browserslist-db@latest
4
+ @openmrs/esm-routes:build:  Why you should do it regularly: https://github.com/browserslist/update-db#readme
5
+ @openmrs/esm-routes:build: asset openmrs-esm-utils.js 3.06 KiB [emitted] [minimized] (name: main) 1 related asset
6
+ @openmrs/esm-routes:build: runtime modules 670 bytes 3 modules
7
+ @openmrs/esm-routes:build: orphan modules 6.77 KiB [orphan] 2 modules
8
+ @openmrs/esm-routes:build: built modules 6.87 KiB [built]
9
+ @openmrs/esm-routes:build:  ./src/index.ts + 2 modules 6.82 KiB [built] [code generated]
10
+ @openmrs/esm-routes:build:  external "@openmrs/esm-utils" 42 bytes [built] [code generated]
11
+ @openmrs/esm-routes:build: webpack 5.88.0 compiled successfully in 7491 ms
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # openmrs-esm-routes
2
+
3
+ openmrs-esm-routes provides helper functions for working with the O3 routes.json files.
@@ -0,0 +1,2 @@
1
+ System.register(["@openmrs/esm-utils"],(function(e,r){var t={};return{setters:[function(e){t.canAccessStorage=e.canAccessStorage}],execute:function(){e((()=>{"use strict";var e={618:e=>{e.exports=t}},r={};function o(t){var n=r[t];if(void 0!==n)return n.exports;var a=r[t]={exports:{}};return e[t](a,a.exports,o),a.exports}o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var n={};return(()=>{o.r(n),o.d(n,{addRoutesOverride:()=>a,isOpenmrsAppRoutes:()=>u,isOpenmrsRoutes:()=>l,localStorageRoutesPrefix:()=>e,removeRoutesOverride:()=>i,resetAllRoutesOverrides:()=>s});var e="openmrs-routes:";function r(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,o=new Array(r);t<r;t++)o[t]=e[t];return o}var t=(0,o(618).canAccessStorage)();function a(e,r){if(t){if("string"==typeof r){if(r.startsWith("http"))return c(e,r);try{var o=JSON.parse(r);if(u(o))return c(e,o);console.error("The supplied routes for ".concat(e," is not a valid OpenmrsAppRoutes object"),r)}catch(e){}}else{if(n=r,null!=(a=URL)&&"undefined"!=typeof Symbol&&a[Symbol.hasInstance]?a[Symbol.hasInstance](n):n instanceof a)return c(e,r.toString());if(u(r))return c(e,r)}var n,a;console.error("Override for ".concat(e," is not in a valid format. Expected either a Javascript Object, a JSON string of a Javascript object, or a URL"),r)}}function i(r){if(t){var o=e+r;localStorage.removeItem(o)}}function s(){if(t)for(var r=window.localStorage,o=0;o<r.length;o++){var n=r.key(o);(null==n?void 0:n.startsWith(e))&&r.removeItem(n)}}function c(r,t){var o=e+r;localStorage.setItem(o,JSON.stringify(t))}function u(e){if(e&&"object"==typeof e){var r=Object.prototype.hasOwnProperty,t=e;return!!(!r.call(e,"pages")||Boolean(t.pages)&&Array.isArray(t.pages))&&!!(!r.call(e,"extensions")||Boolean(t.extensions)&&Array.isArray(t.extensions))}return!1}function l(e){if(e&&"object"==typeof e){var t=e;return Object.entries(t).every((function(e){var t,o,n=(o=2,function(e){if(Array.isArray(e))return e}(t=e)||function(e,r){var t=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var o,n,a=[],i=!0,s=!1;try{for(t=t.call(e);!(i=(o=t.next()).done)&&(a.push(o.value),!r||a.length!==r);i=!0);}catch(e){s=!0,n=e}finally{try{i||null==t.return||t.return()}finally{if(s)throw n}}return a}}(t,o)||function(e,t){if(e){if("string"==typeof e)return r(e,t);var o=Object.prototype.toString.call(e).slice(8,-1);return"Object"===o&&e.constructor&&(o=e.constructor.name),"Map"===o||"Set"===o?Array.from(o):"Arguments"===o||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?r(e,t):void 0}}(t,o)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),a=n[0],i=n[1];return"string"==typeof a&&u(i)}))}return!1}})(),n})())}}}));
2
+ //# sourceMappingURL=openmrs-esm-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openmrs-esm-utils.js","mappings":"0LAAAA,EAAOC,QAAUC,C,GCCbC,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaL,QAGrB,IAAID,EAASG,EAAyBE,GAAY,CAGjDJ,QAAS,CAAC,GAOX,OAHAO,EAAoBH,GAAUL,EAAQA,EAAOC,QAASG,GAG/CJ,EAAOC,OACf,CCrBAG,EAAoBK,EAAI,CAACR,EAASS,KACjC,IAAI,IAAIC,KAAOD,EACXN,EAAoBQ,EAAEF,EAAYC,KAASP,EAAoBQ,EAAEX,EAASU,IAC5EE,OAAOC,eAAeb,EAASU,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDP,EAAoBQ,EAAI,CAACK,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFd,EAAoBkB,EAAKrB,IACH,oBAAXsB,QAA0BA,OAAOC,aAC1CX,OAAOC,eAAeb,EAASsB,OAAOC,YAAa,CAAEC,MAAO,WAE7DZ,OAAOC,eAAeb,EAAS,aAAc,CAAEwB,OAAO,GAAO,E,oMCLvD,IAAMC,EAA2B,kBCAD,iB,yFAKvC,IAAMC,GAAYC,E,OAAAA,oBAYX,SAASC,EAAkBC,EAAoBC,GACpD,GAAKJ,EAAL,CAIA,GAAsB,iBAAXI,EAAqB,CAC9B,GAAIA,EAAOC,WAAW,QACpB,OAAOC,EAAyBH,EAAYC,GAE5C,IACE,IAAMG,EAAcC,KAAKC,MAAML,GAC/B,GAAIM,EAAmBH,GACrB,OAAOD,EAAyBH,EAAYI,GAE5CI,QAAQC,MAAM,2BAAsC,OAAXT,EAAW,2CAA0CC,EAElG,CAAE,SAAO,CAEb,KAAO,I,EAAIA,E,SAAkBS,M,0FAC3B,OAAOP,EAAyBH,EAAYC,EAAOU,YAC9C,GAAIJ,EAAmBN,GAC5B,OAAOE,EAAyBH,EAAYC,EAC9C,C,QAEAO,QAAQC,MACN,gBAA2B,OAAXT,EAAW,kHAC3BC,EAvBF,CAyBF,CASO,SAASW,EAAqBZ,GACnC,GAAKH,EAAL,CAIA,IAAMhB,EAAMe,EAA2BI,EACvCa,aAAaC,WAAWjC,EAHxB,CAIF,CAQO,SAASkC,IACd,GAAKlB,EAKL,IADA,IAAMgB,EAAeG,OAAOH,aACnBI,EAAI,EAAGA,EAAIJ,EAAaK,OAAQD,IAAK,CAC5C,IAAMpC,EAAMgC,EAAahC,IAAIoC,IACzBpC,aAAAA,EAAAA,EAAKqB,WAAWN,KAClBiB,EAAaC,WAAWjC,EAE5B,CACF,CAEA,SAASsB,EAAyBH,EAAoBC,GACpD,IAAMpB,EAAMe,EAA2BI,EACvCa,aAAaM,QAAQtC,EAAKwB,KAAKe,UAAUnB,GAC3C,CAUO,SAASM,EAAmBN,GACjC,GAAIA,GAA4B,iBAAXA,EAAqB,CACxC,IAAMX,EAAiBP,OAAOM,UAAUC,eAGlCc,EAAcH,EAEpB,UAAIX,EAAeC,KAAKU,EAAQ,UACzBoB,QAAQjB,EAAYkB,QAAWC,MAAMC,QAAQpB,EAAYkB,aAK5DhC,EAAeC,KAAKU,EAAQ,eACzBoB,QAAQjB,EAAYqB,aAAgBF,MAAMC,QAAQpB,EAAYqB,YAQvE,CAEA,OAAO,CACT,CAUO,SAASC,EAAgBzB,GAC9B,GAAIA,GAA4B,iBAAXA,EAAqB,CACxC,IAAMG,EAAcH,EAEpB,OAAOlB,OAAO4C,QAAQvB,GAAawB,OAAM,Y,g1BAAE/C,EAAAA,EAAAA,GAAKc,EAAAA,EAAAA,G,MAA0B,iBAARd,GAAoB0B,EAAmBZ,E,GAC3G,CAEA,OAAO,CACT,C","sources":["webpack://@openmrs/esm-routes/external system \"@openmrs/esm-utils\"","webpack://@openmrs/esm-routes/webpack/bootstrap","webpack://@openmrs/esm-routes/webpack/runtime/define property getters","webpack://@openmrs/esm-routes/webpack/runtime/hasOwnProperty shorthand","webpack://@openmrs/esm-routes/webpack/runtime/make namespace object","webpack://@openmrs/esm-routes/./src/constants.ts","webpack://@openmrs/esm-routes/./src/routes.ts"],"sourcesContent":["module.exports = __WEBPACK_EXTERNAL_MODULE__618__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export const localStorageRoutesPrefix = 'openmrs-routes:';\n","/** @module @category Routes Utilities */\nimport type { OpenmrsAppRoutes, OpenmrsRoutes } from '@openmrs/esm-globals';\nimport { canAccessStorage } from '@openmrs/esm-utils';\nimport { localStorageRoutesPrefix } from './constants';\n\nconst isEnabled = canAccessStorage();\n\n/**\n * Used to add a route override to local storage. These are read as the routes registry\n * is assembled, so the app must be reloaded for new overrides to take effect.\n *\n * @internal\n * @param moduleName The name of the module the routes are for\n * @param routes Either an {@link OpenmrsAppRoutes} object, a string that represents a JSON\n * version of an {@link OpenmrsAppRoutes} object or a string or URL that resolves to a\n * JSON document that represents an {@link OpenmrsAppRoutes} object\n */\nexport function addRoutesOverride(moduleName: string, routes: OpenmrsAppRoutes | string | URL) {\n if (!isEnabled) {\n return;\n }\n\n if (typeof routes === 'string') {\n if (routes.startsWith('http')) {\n return addRouteOverrideInternal(moduleName, routes);\n } else {\n try {\n const maybeRoutes = JSON.parse(routes);\n if (isOpenmrsAppRoutes(maybeRoutes)) {\n return addRouteOverrideInternal(moduleName, maybeRoutes);\n } else {\n console.error(`The supplied routes for ${moduleName} is not a valid OpenmrsAppRoutes object`, routes);\n }\n } catch {}\n }\n } else if (routes instanceof URL) {\n return addRouteOverrideInternal(moduleName, routes.toString());\n } else if (isOpenmrsAppRoutes(routes)) {\n return addRouteOverrideInternal(moduleName, routes);\n }\n\n console.error(\n `Override for ${moduleName} is not in a valid format. Expected either a Javascript Object, a JSON string of a Javascript object, or a URL`,\n routes,\n );\n}\n\n/**\n * Used to remove an existing routes override from local storage. These are read as the routes registry\n * is assembled, so the app must be reloaded for removed override to be removed.\n *\n * @internal\n * @param moduleName The module to remove the overrides for\n */\nexport function removeRoutesOverride(moduleName: string) {\n if (!isEnabled) {\n return;\n }\n\n const key = localStorageRoutesPrefix + moduleName;\n localStorage.removeItem(key);\n}\n\n/**\n * Used to remove all existing routes overrides from local storage. These are read as the routes registry\n * is assembled, so the app must be reloaded for the removed overrides to appear to be removed.\n *\n * @internal\n */\nexport function resetAllRoutesOverrides() {\n if (!isEnabled) {\n return;\n }\n\n const localStorage = window.localStorage;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(localStorageRoutesPrefix)) {\n localStorage.removeItem(key);\n }\n }\n}\n\nfunction addRouteOverrideInternal(moduleName: string, routes: OpenmrsAppRoutes | string) {\n const key = localStorageRoutesPrefix + moduleName;\n localStorage.setItem(key, JSON.stringify(routes));\n}\n\n/**\n * Simple type-predicate to ensure that the value can be treated as an OpenmrsAppRoutes\n * object.\n *\n * @internal\n * @param routes the object to check to see if it is an OpenmrsAppRoutes object\n * @returns true if the routes value is an OpenmrsAppRoutes\n */\nexport function isOpenmrsAppRoutes(routes: OpenmrsAppRoutes | unknown): routes is OpenmrsAppRoutes {\n if (routes && typeof routes === 'object') {\n const hasOwnProperty = Object.prototype.hasOwnProperty;\n // we cast maybeRoutes as OpenmrsAppRoutes mainly so we can refer to the properties it should\n // have without repeated casts\n const maybeRoutes = routes as OpenmrsAppRoutes;\n\n if (hasOwnProperty.call(routes, 'pages')) {\n if (!Boolean(maybeRoutes.pages) || !Array.isArray(maybeRoutes.pages)) {\n return false;\n }\n }\n\n if (hasOwnProperty.call(routes, 'extensions')) {\n if (!Boolean(maybeRoutes.extensions) || !Array.isArray(maybeRoutes.extensions)) {\n return false;\n }\n }\n\n // Notice that we're essentially testing for things that cannot be treated as an OpenmrsAppRoutes\n // object. This is because a completely empty object is a valid OpenmrsAppRoutes object.\n return true;\n }\n\n return false;\n}\n\n/**\n * Simple type-predicate to ensure that the value can be treated as an OpenmrsRoutes\n * object.\n *\n * @internal\n * @param routes the object to check to see if it is an OpenmrsRoutes object\n * @returns true if the routes value is an OpenmrsRoutes\n */\nexport function isOpenmrsRoutes(routes: OpenmrsRoutes | unknown): routes is OpenmrsRoutes {\n if (routes && typeof routes === 'object') {\n const maybeRoutes = routes as OpenmrsRoutes;\n\n return Object.entries(maybeRoutes).every(([key, value]) => typeof key === 'string' && isOpenmrsAppRoutes(value));\n }\n\n return false;\n}\n"],"names":["module","exports","__WEBPACK_EXTERNAL_MODULE__618__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","d","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","value","localStorageRoutesPrefix","isEnabled","canAccessStorage","addRoutesOverride","moduleName","routes","startsWith","addRouteOverrideInternal","maybeRoutes","JSON","parse","isOpenmrsAppRoutes","console","error","URL","toString","removeRoutesOverride","localStorage","removeItem","resetAllRoutesOverrides","window","i","length","setItem","stringify","Boolean","pages","Array","isArray","extensions","isOpenmrsRoutes","entries","every"],"sourceRoot":""}
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ transform: {
3
+ "^.+\\.(m?j|t)sx?$": ["@swc/jest"],
4
+ },
5
+ testEnvironment: "jsdom",
6
+ testEnvironmentOptions: {
7
+ url: "http://localhost/",
8
+ },
9
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@openmrs/esm-routes",
3
+ "version": "5.3.3-pre.1268",
4
+ "license": "MPL-2.0",
5
+ "description": "Utilities for working with the routes registry",
6
+ "browser": "dist/openmrs-esm-routes.js",
7
+ "main": "src/index.ts",
8
+ "source": true,
9
+ "sideEffects": false,
10
+ "scripts": {
11
+ "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color",
12
+ "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
13
+ "build": "webpack --mode=production",
14
+ "build:development": "webpack --mode development",
15
+ "analyze": "webpack --mode=production --env analyze=true",
16
+ "typescript": "tsc",
17
+ "lint": "eslint src --ext ts,tsx"
18
+ },
19
+ "keywords": [
20
+ "openmrs",
21
+ "microfrontends"
22
+ ],
23
+ "directories": {
24
+ "lib": "dist",
25
+ "src": "src"
26
+ },
27
+ "browserslist": [
28
+ "extends browserslist-config-openmrs"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/openmrs/openmrs-esm-core.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/openmrs/openmrs-esm-core/issues"
36
+ },
37
+ "homepage": "https://github.com/openmrs/openmrs-esm-core#readme",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "peerDependencies": {
42
+ "@openmrs/esm-globals": "5.x",
43
+ "@openmrs/esm-utils": "5.x"
44
+ },
45
+ "devDependencies": {
46
+ "@openmrs/esm-globals": "5.3.3-pre.1268",
47
+ "@openmrs/esm-utils": "5.3.3-pre.1268",
48
+ "jest-fetch-mock": "^3.0.3"
49
+ },
50
+ "stableVersion": "5.2.0"
51
+ }
@@ -0,0 +1 @@
1
+ export const localStorageRoutesPrefix = 'openmrs-routes:';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './constants';
2
+ export * from './routes';
@@ -0,0 +1,137 @@
1
+ import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
2
+ enableFetchMocks();
3
+
4
+ import { addRoutesOverride, isOpenmrsAppRoutes } from './routes';
5
+
6
+ describe('Openmrs Routes Utilities', () => {
7
+ describe('addRoutesOverride', () => {
8
+ beforeEach(() => localStorage.clear());
9
+
10
+ it('should add routes when provided as an object', () => {
11
+ addRoutesOverride('@openmrs/my-module', {
12
+ backendDependencies: {
13
+ fhir2: '^2.0.0',
14
+ 'webservices.rest': '^1.4.0',
15
+ },
16
+ version: '1.2.0-pre.12345+build.8',
17
+ pages: [
18
+ {
19
+ component: 'root',
20
+ route: 'myPage',
21
+ },
22
+ ],
23
+ extensions: [
24
+ {
25
+ name: 'custom extension',
26
+ component: 'customExtension',
27
+ },
28
+ ],
29
+ });
30
+
31
+ expect(localStorage.getItem('openmrs-routes:@openmrs/my-module')).toBe(
32
+ '{"backendDependencies":{"fhir2":"^2.0.0","webservices.rest":"^1.4.0"},"version":"1.2.0-pre.12345+build.8","pages":[{"component":"root","route":"myPage"}],"extensions":[{"name":"custom extension","component":"customExtension"}]}',
33
+ );
34
+ });
35
+
36
+ it('should add routes when provided as a JSON string', () => {
37
+ addRoutesOverride(
38
+ '@openmrs/my-module',
39
+ JSON.stringify({
40
+ backendDependencies: {
41
+ fhir2: '^2.0.0',
42
+ 'webservices.rest': '^1.4.0',
43
+ },
44
+ version: '1.2.0-pre.12345+build.8',
45
+ pages: [
46
+ {
47
+ component: 'root',
48
+ route: 'myPage',
49
+ },
50
+ ],
51
+ extensions: [
52
+ {
53
+ name: 'custom extension',
54
+ component: 'customExtension',
55
+ },
56
+ ],
57
+ }),
58
+ );
59
+
60
+ expect(localStorage.getItem('openmrs-routes:@openmrs/my-module')).toBe(
61
+ '{"backendDependencies":{"fhir2":"^2.0.0","webservices.rest":"^1.4.0"},"version":"1.2.0-pre.12345+build.8","pages":[{"component":"root","route":"myPage"}],"extensions":[{"name":"custom extension","component":"customExtension"}]}',
62
+ );
63
+ });
64
+
65
+ it('should add routes when loaded via a string HTTP endpoint', () => {
66
+ addRoutesOverride('@openmrs/my-module', 'http://localhost/my-route-override.json');
67
+
68
+ expect(localStorage.getItem('openmrs-routes:@openmrs/my-module')).toBe(
69
+ '"http://localhost/my-route-override.json"',
70
+ );
71
+ });
72
+
73
+ it('should add routes when loaded via a URL HTTP endpoint', () => {
74
+ addRoutesOverride('@openmrs/my-module', new URL('http://localhost/my-route-override.json'));
75
+
76
+ expect(localStorage.getItem('openmrs-routes:@openmrs/my-module')).toBe(
77
+ '"http://localhost/my-route-override.json"',
78
+ );
79
+ });
80
+ });
81
+
82
+ describe('isOpenmrsAppRoutes', () => {
83
+ it('should return true for a valid routes object', () => {
84
+ expect(
85
+ isOpenmrsAppRoutes({
86
+ backendDependencies: {
87
+ fhir2: '^2.0.0',
88
+ 'webservices.rest': '^1.4.0',
89
+ },
90
+ version: '1.2.0-pre.12345+build.8',
91
+ pages: [
92
+ {
93
+ component: 'root',
94
+ route: 'myPage',
95
+ },
96
+ ],
97
+ extensions: [
98
+ {
99
+ name: 'custom extension',
100
+ component: 'customExtension',
101
+ },
102
+ ],
103
+ }),
104
+ ).toBe(true);
105
+ });
106
+
107
+ it('should accept an object with only pages', () => {
108
+ expect(
109
+ isOpenmrsAppRoutes({
110
+ pages: [
111
+ {
112
+ component: 'root',
113
+ route: 'myPage',
114
+ },
115
+ ],
116
+ }),
117
+ ).toBe(true);
118
+ });
119
+
120
+ it('should accept an object with only extensions', () => {
121
+ expect(
122
+ isOpenmrsAppRoutes({
123
+ extensions: [
124
+ {
125
+ name: 'custom extension',
126
+ component: 'customExtension',
127
+ },
128
+ ],
129
+ }),
130
+ ).toBe(true);
131
+ });
132
+
133
+ it('should report an empty object as valid', () => {
134
+ expect(isOpenmrsAppRoutes({})).toBe(true);
135
+ });
136
+ });
137
+ });
package/src/routes.ts ADDED
@@ -0,0 +1,140 @@
1
+ /** @module @category Routes Utilities */
2
+ import type { OpenmrsAppRoutes, OpenmrsRoutes } from '@openmrs/esm-globals';
3
+ import { canAccessStorage } from '@openmrs/esm-utils';
4
+ import { localStorageRoutesPrefix } from './constants';
5
+
6
+ const isEnabled = canAccessStorage();
7
+
8
+ /**
9
+ * Used to add a route override to local storage. These are read as the routes registry
10
+ * is assembled, so the app must be reloaded for new overrides to take effect.
11
+ *
12
+ * @internal
13
+ * @param moduleName The name of the module the routes are for
14
+ * @param routes Either an {@link OpenmrsAppRoutes} object, a string that represents a JSON
15
+ * version of an {@link OpenmrsAppRoutes} object or a string or URL that resolves to a
16
+ * JSON document that represents an {@link OpenmrsAppRoutes} object
17
+ */
18
+ export function addRoutesOverride(moduleName: string, routes: OpenmrsAppRoutes | string | URL) {
19
+ if (!isEnabled) {
20
+ return;
21
+ }
22
+
23
+ if (typeof routes === 'string') {
24
+ if (routes.startsWith('http')) {
25
+ return addRouteOverrideInternal(moduleName, routes);
26
+ } else {
27
+ try {
28
+ const maybeRoutes = JSON.parse(routes);
29
+ if (isOpenmrsAppRoutes(maybeRoutes)) {
30
+ return addRouteOverrideInternal(moduleName, maybeRoutes);
31
+ } else {
32
+ console.error(`The supplied routes for ${moduleName} is not a valid OpenmrsAppRoutes object`, routes);
33
+ }
34
+ } catch {}
35
+ }
36
+ } else if (routes instanceof URL) {
37
+ return addRouteOverrideInternal(moduleName, routes.toString());
38
+ } else if (isOpenmrsAppRoutes(routes)) {
39
+ return addRouteOverrideInternal(moduleName, routes);
40
+ }
41
+
42
+ console.error(
43
+ `Override for ${moduleName} is not in a valid format. Expected either a Javascript Object, a JSON string of a Javascript object, or a URL`,
44
+ routes,
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Used to remove an existing routes override from local storage. These are read as the routes registry
50
+ * is assembled, so the app must be reloaded for removed override to be removed.
51
+ *
52
+ * @internal
53
+ * @param moduleName The module to remove the overrides for
54
+ */
55
+ export function removeRoutesOverride(moduleName: string) {
56
+ if (!isEnabled) {
57
+ return;
58
+ }
59
+
60
+ const key = localStorageRoutesPrefix + moduleName;
61
+ localStorage.removeItem(key);
62
+ }
63
+
64
+ /**
65
+ * Used to remove all existing routes overrides from local storage. These are read as the routes registry
66
+ * is assembled, so the app must be reloaded for the removed overrides to appear to be removed.
67
+ *
68
+ * @internal
69
+ */
70
+ export function resetAllRoutesOverrides() {
71
+ if (!isEnabled) {
72
+ return;
73
+ }
74
+
75
+ const localStorage = window.localStorage;
76
+ for (let i = 0; i < localStorage.length; i++) {
77
+ const key = localStorage.key(i);
78
+ if (key?.startsWith(localStorageRoutesPrefix)) {
79
+ localStorage.removeItem(key);
80
+ }
81
+ }
82
+ }
83
+
84
+ function addRouteOverrideInternal(moduleName: string, routes: OpenmrsAppRoutes | string) {
85
+ const key = localStorageRoutesPrefix + moduleName;
86
+ localStorage.setItem(key, JSON.stringify(routes));
87
+ }
88
+
89
+ /**
90
+ * Simple type-predicate to ensure that the value can be treated as an OpenmrsAppRoutes
91
+ * object.
92
+ *
93
+ * @internal
94
+ * @param routes the object to check to see if it is an OpenmrsAppRoutes object
95
+ * @returns true if the routes value is an OpenmrsAppRoutes
96
+ */
97
+ export function isOpenmrsAppRoutes(routes: OpenmrsAppRoutes | unknown): routes is OpenmrsAppRoutes {
98
+ if (routes && typeof routes === 'object') {
99
+ const hasOwnProperty = Object.prototype.hasOwnProperty;
100
+ // we cast maybeRoutes as OpenmrsAppRoutes mainly so we can refer to the properties it should
101
+ // have without repeated casts
102
+ const maybeRoutes = routes as OpenmrsAppRoutes;
103
+
104
+ if (hasOwnProperty.call(routes, 'pages')) {
105
+ if (!Boolean(maybeRoutes.pages) || !Array.isArray(maybeRoutes.pages)) {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ if (hasOwnProperty.call(routes, 'extensions')) {
111
+ if (!Boolean(maybeRoutes.extensions) || !Array.isArray(maybeRoutes.extensions)) {
112
+ return false;
113
+ }
114
+ }
115
+
116
+ // Notice that we're essentially testing for things that cannot be treated as an OpenmrsAppRoutes
117
+ // object. This is because a completely empty object is a valid OpenmrsAppRoutes object.
118
+ return true;
119
+ }
120
+
121
+ return false;
122
+ }
123
+
124
+ /**
125
+ * Simple type-predicate to ensure that the value can be treated as an OpenmrsRoutes
126
+ * object.
127
+ *
128
+ * @internal
129
+ * @param routes the object to check to see if it is an OpenmrsRoutes object
130
+ * @returns true if the routes value is an OpenmrsRoutes
131
+ */
132
+ export function isOpenmrsRoutes(routes: OpenmrsRoutes | unknown): routes is OpenmrsRoutes {
133
+ if (routes && typeof routes === 'object') {
134
+ const maybeRoutes = routes as OpenmrsRoutes;
135
+
136
+ return Object.entries(maybeRoutes).every(([key, value]) => typeof key === 'string' && isOpenmrsAppRoutes(value));
137
+ }
138
+
139
+ return false;
140
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "esModuleInterop": true,
4
+ "module": "esnext",
5
+ "target": "es2015",
6
+ "allowSyntheticDefaultImports": true,
7
+ "jsx": "react",
8
+ "strictNullChecks": true,
9
+ "moduleResolution": "node",
10
+ "declaration": true,
11
+ "declarationDir": "dist",
12
+ "emitDeclarationOnly": true,
13
+ "lib": [
14
+ "dom",
15
+ "es5",
16
+ "scripthost",
17
+ "es2015",
18
+ "es2015.promise",
19
+ "es2016.array.include",
20
+ "es2018",
21
+ "esnext"
22
+ ]
23
+ },
24
+ "include": ["src/**/*"]
25
+ }
@@ -0,0 +1,42 @@
1
+ const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
2
+ const { resolve } = require("path");
3
+ const { CleanWebpackPlugin } = require("clean-webpack-plugin");
4
+ const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
5
+
6
+ const { peerDependencies } = require("./package.json");
7
+
8
+ module.exports = (env) => ({
9
+ entry: [resolve(__dirname, "src/index.ts")],
10
+ output: {
11
+ filename: "openmrs-esm-utils.js",
12
+ path: resolve(__dirname, "dist"),
13
+ library: { type: "system" },
14
+ },
15
+ devtool: "source-map",
16
+ module: {
17
+ rules: [
18
+ {
19
+ test: /\.m?(js|ts|tsx)$/,
20
+ exclude: /node_modules/,
21
+ use: "swc-loader",
22
+ },
23
+ ],
24
+ },
25
+ externals: Object.keys(peerDependencies || {}),
26
+ resolve: {
27
+ extensions: [".ts", ".js", ".tsx", ".jsx"],
28
+ },
29
+ plugins: [
30
+ new CleanWebpackPlugin(),
31
+ new ForkTsCheckerWebpackPlugin(),
32
+ new BundleAnalyzerPlugin({
33
+ analyzerMode: env && env.analyze ? "static" : "disabled",
34
+ }),
35
+ ],
36
+ devServer: {
37
+ disableHostCheck: true,
38
+ headers: {
39
+ "Access-Control-Allow-Origin": "*",
40
+ },
41
+ },
42
+ });