@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.
- package/.turbo/turbo-build.log +11 -0
- package/README.md +3 -0
- package/dist/openmrs-esm-utils.js +2 -0
- package/dist/openmrs-esm-utils.js.map +1 -0
- package/jest.config.js +9 -0
- package/package.json +51 -0
- package/src/constants.ts +1 -0
- package/src/index.ts +2 -0
- package/src/routes.test.ts +137 -0
- package/src/routes.ts +140 -0
- package/tsconfig.json +25 -0
- package/webpack.config.js +42 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[32m@openmrs/esm-routes:build[0m: cache hit, replaying output [2m707c5d2b5ef0dfc3[0m
|
|
2
|
+
[32m@openmrs/esm-routes:build: [0mBrowserslist: caniuse-lite is outdated. Please run:
|
|
3
|
+
[32m@openmrs/esm-routes:build: [0m npx update-browserslist-db@latest
|
|
4
|
+
[32m@openmrs/esm-routes:build: [0m Why you should do it regularly: https://github.com/browserslist/update-db#readme
|
|
5
|
+
[32m@openmrs/esm-routes:build: [0masset [1m[32mopenmrs-esm-utils.js[39m[22m 3.06 KiB [1m[32m[emitted][39m[22m [1m[32m[minimized][39m[22m (name: main) 1 related asset
|
|
6
|
+
[32m@openmrs/esm-routes:build: [0mruntime modules 670 bytes 3 modules
|
|
7
|
+
[32m@openmrs/esm-routes:build: [0morphan modules 6.77 KiB [1m[33m[orphan][39m[22m 2 modules
|
|
8
|
+
[32m@openmrs/esm-routes:build: [0mbuilt modules 6.87 KiB [1m[33m[built][39m[22m
|
|
9
|
+
[32m@openmrs/esm-routes:build: [0m [1m./src/index.ts + 2 modules[39m[22m 6.82 KiB [1m[33m[built][39m[22m [1m[33m[code generated][39m[22m
|
|
10
|
+
[32m@openmrs/esm-routes:build: [0m [1mexternal "@openmrs/esm-utils"[39m[22m 42 bytes [1m[33m[built][39m[22m [1m[33m[code generated][39m[22m
|
|
11
|
+
[32m@openmrs/esm-routes:build: [0mwebpack 5.88.0 compiled [1m[32msuccessfully[39m[22m in 7491 ms
|
package/README.md
ADDED
|
@@ -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
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
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const localStorageRoutesPrefix = 'openmrs-routes:';
|
package/src/index.ts
ADDED
|
@@ -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
|
+
});
|