diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 9f2186dc8..d7349d118 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -32,7 +32,7 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Install dependencies - run: yarn setup + run: yarn install --immutable - name: Install Playwright Browsers run: yarn playwright install chromium --with-deps diff --git a/packages/apps/esm-implementer-tools-app/package.json b/packages/apps/esm-implementer-tools-app/package.json index 6aff133c4..d1fc05091 100644 --- a/packages/apps/esm-implementer-tools-app/package.json +++ b/packages/apps/esm-implementer-tools-app/package.json @@ -46,7 +46,8 @@ "react": "18.x", "react-dom": "18.x", "react-i18next": "11.x", - "rxjs": "6.x" + "rxjs": "6.x", + "swr": "2.x" }, "devDependencies": { "@openmrs/esm-framework": "^5.1.0", @@ -57,7 +58,7 @@ "react-dom": "^18.1.0", "react-i18next": "^11.16.9", "rxjs": "^6.5.3", - "swr": "^2.0.1", + "swr": "^2.2.2", "webpack": "^5.88.0" } } diff --git a/packages/apps/esm-implementer-tools-app/src/global-implementer-tools.component.tsx b/packages/apps/esm-implementer-tools-app/src/global-implementer-tools.component.tsx index 77debcb2a..71766d76f 100644 --- a/packages/apps/esm-implementer-tools-app/src/global-implementer-tools.component.tsx +++ b/packages/apps/esm-implementer-tools-app/src/global-implementer-tools.component.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { useTranslation } from "react-i18next"; import { ChevronUp, ChevronDown } from "@carbon/react/icons"; import { UserHasAccess, useStore } from "@openmrs/esm-framework"; import { implementerToolsStore, togglePopup } from "./store"; diff --git a/packages/apps/esm-login-app/package.json b/packages/apps/esm-login-app/package.json index 781231004..d7fb7c9c2 100644 --- a/packages/apps/esm-login-app/package.json +++ b/packages/apps/esm-login-app/package.json @@ -47,7 +47,8 @@ "react-dom": "18.x", "react-i18next": "11.x", "react-router-dom": "6.x", - "rxjs": "6.x" + "rxjs": "6.x", + "swr": "2.x" }, "devDependencies": { "@openmrs/esm-framework": "^5.1.0", @@ -58,7 +59,7 @@ "react-i18next": "^11.16.9", "react-router-dom": "^6.3.0", "rxjs": "^6.5.3", - "swr": "^2.0.1", + "swr": "^2.2.2", "webpack": "^5.88.0" } } diff --git a/packages/apps/esm-login-app/src/login.resource.ts b/packages/apps/esm-login-app/src/login.resource.ts index a6cc5dbeb..49a6d23f7 100644 --- a/packages/apps/esm-login-app/src/login.resource.ts +++ b/packages/apps/esm-login-app/src/login.resource.ts @@ -47,12 +47,20 @@ export function useLoginLocations( searchQuery: string = "" ): LoginLocationData { const { t } = useTranslation(); - function constructUrl(page, prevPageData: FetchResponse) { - if ( - prevPageData && - !prevPageData?.data?.link?.some((link) => link.relation === "next") - ) { - return null; + function constructUrl( + page: number, + prevPageData: FetchResponse + ) { + if (prevPageData) { + const nextLink = prevPageData.data?.link?.find( + (link) => link.relation === "next" + ); + + if (!nextLink) { + return null; + } + + return nextLink.url; } let url = `${fhirBaseUrl}/Location?`; diff --git a/packages/apps/esm-offline-tools-app/package.json b/packages/apps/esm-offline-tools-app/package.json index a72135465..1b4a4b1ac 100644 --- a/packages/apps/esm-offline-tools-app/package.json +++ b/packages/apps/esm-offline-tools-app/package.json @@ -40,7 +40,7 @@ "dependencies": { "@carbon/react": "^1.37.0", "lodash-es": "^4.17.21", - "swr": "^2.0.1" + "swr": "^2.2.2" }, "peerDependencies": { "@openmrs/esm-framework": "*", @@ -48,7 +48,8 @@ "react-dom": "18.x", "react-i18next": "11.x", "react-router-dom": "6.x", - "rxjs": "6.x" + "rxjs": "6.x", + "swr": "2.x" }, "devDependencies": { "@openmrs/esm-framework": "^5.1.0", diff --git a/packages/apps/esm-offline-tools-app/src/hooks/offline-patient-data-hooks.ts b/packages/apps/esm-offline-tools-app/src/hooks/offline-patient-data-hooks.ts index e899bcded..974608721 100644 --- a/packages/apps/esm-offline-tools-app/src/hooks/offline-patient-data-hooks.ts +++ b/packages/apps/esm-offline-tools-app/src/hooks/offline-patient-data-hooks.ts @@ -121,8 +121,11 @@ function useMergedSwr( const areAllLoaded = swrResponses.every((res) => !!res.data); const data = areAllLoaded ? merge() : null; const error = swrResponses.find((res) => res.error); - const mutate = () => - Promise.all(swrResponses.map((res) => res.mutate())).then(merge); + const mutate: () => Promise = () => + Promise.all(swrResponses.map((res) => res.mutate())).then(() => { + merge(); + return undefined; + }); const isValidating = swrResponses.some((res) => res.isValidating); const isLoading = swrResponses.some((res) => res.isLoading); @@ -133,10 +136,5 @@ function useMergedSwr( isValidating, isLoading, }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - merge, - // eslint-disable-next-line react-hooks/exhaustive-deps - ...swrResponses.flatMap((res) => [res.data, res.error, res.isValidating]), - ]); + }, [merge, swrResponses]); } diff --git a/packages/apps/esm-primary-navigation-app/src/components/navbar/navbar.component.tsx b/packages/apps/esm-primary-navigation-app/src/components/navbar/navbar.component.tsx index 95f5e45d3..00603d17b 100644 --- a/packages/apps/esm-primary-navigation-app/src/components/navbar/navbar.component.tsx +++ b/packages/apps/esm-primary-navigation-app/src/components/navbar/navbar.component.tsx @@ -190,7 +190,7 @@ const Navbar: React.FC = () => { ) : ( { return ( = () => { return ( - -
- -
+ + + + + + + + } + /> + ); }; diff --git a/packages/framework/esm-framework/docs/API.md b/packages/framework/esm-framework/docs/API.md index 40e1bb4a1..8429950cb 100644 --- a/packages/framework/esm-framework/docs/API.md +++ b/packages/framework/esm-framework/docs/API.md @@ -3176,11 +3176,17 @@ ___ ### getDynamicOfflineDataEntries -▸ **getDynamicOfflineDataEntries**(`type?`): `Promise`<[`DynamicOfflineData`](interfaces/DynamicOfflineData.md)[]\> +▸ **getDynamicOfflineDataEntries**<`T`\>(`type?`): `Promise`<`T`[]\> Returns all [DynamicOfflineData](interfaces/DynamicOfflineData.md) entries which registered for the currently logged in user. Optionally returns only entries of a given type. +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends [`DynamicOfflineData`](interfaces/DynamicOfflineData.md) | + #### Parameters | Name | Type | Description | @@ -3189,7 +3195,7 @@ Optionally returns only entries of a given type. #### Returns -`Promise`<[`DynamicOfflineData`](interfaces/DynamicOfflineData.md)[]\> +`Promise`<`T`[]\> #### Defined in @@ -3199,11 +3205,17 @@ ___ ### getDynamicOfflineDataEntriesFor -▸ **getDynamicOfflineDataEntriesFor**(`userId`, `type?`): `Promise`<[`DynamicOfflineData`](interfaces/DynamicOfflineData.md)[]\> +▸ **getDynamicOfflineDataEntriesFor**<`T`\>(`userId`, `type?`): `Promise`<`T`[]\> Returns all [DynamicOfflineData](interfaces/DynamicOfflineData.md) entries which registered for the given user. Optionally returns only entries of a given type. +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends [`DynamicOfflineData`](interfaces/DynamicOfflineData.md) | + #### Parameters | Name | Type | Description | @@ -3213,7 +3225,7 @@ Optionally returns only entries of a given type. #### Returns -`Promise`<[`DynamicOfflineData`](interfaces/DynamicOfflineData.md)[]\> +`Promise`<`T`[]\> #### Defined in @@ -3432,7 +3444,7 @@ should be made available offline for the currently logged in user. #### Defined in -[packages/framework/esm-offline/src/dynamic-offline-data.ts:162](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L162) +[packages/framework/esm-offline/src/dynamic-offline-data.ts:161](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L161) ___ @@ -3457,7 +3469,7 @@ should be made available offline for the user with the given ID. #### Defined in -[packages/framework/esm-offline/src/dynamic-offline-data.ts:177](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L177) +[packages/framework/esm-offline/src/dynamic-offline-data.ts:176](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L176) ___ @@ -3534,7 +3546,7 @@ no longer needs to be available offline for the currently logged in user. #### Defined in -[packages/framework/esm-offline/src/dynamic-offline-data.ts:213](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L213) +[packages/framework/esm-offline/src/dynamic-offline-data.ts:212](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L212) ___ @@ -3559,7 +3571,7 @@ no longer needs to be available offline for the user with the given ID. #### Defined in -[packages/framework/esm-offline/src/dynamic-offline-data.ts:228](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L228) +[packages/framework/esm-offline/src/dynamic-offline-data.ts:227](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L227) ___ @@ -3714,7 +3726,7 @@ Synchronizes all offline data entries of the given [type](interfaces/FetchRespon #### Defined in -[packages/framework/esm-offline/src/dynamic-offline-data.ts:262](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L262) +[packages/framework/esm-offline/src/dynamic-offline-data.ts:261](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L261) ___ @@ -3738,7 +3750,7 @@ Synchronizes a single offline data entry of the given [type](interfaces/FetchRes #### Defined in -[packages/framework/esm-offline/src/dynamic-offline-data.ts:280](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L280) +[packages/framework/esm-offline/src/dynamic-offline-data.ts:279](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-offline/src/dynamic-offline-data.ts#L279) ___ diff --git a/packages/framework/esm-framework/package.json b/packages/framework/esm-framework/package.json index fd0dea7b8..ae7187e97 100644 --- a/packages/framework/esm-framework/package.json +++ b/packages/framework/esm-framework/package.json @@ -59,7 +59,8 @@ "react-dom": "18.x", "react-i18next": "11.x", "rxjs": "6.x", - "single-spa": "5.x" + "single-spa": "5.x", + "swr": "2.x" }, "devDependencies": { "jest": "28.1.0", diff --git a/packages/framework/esm-offline/src/dynamic-offline-data.ts b/packages/framework/esm-offline/src/dynamic-offline-data.ts index 0d06728f5..e4cb87ab6 100644 --- a/packages/framework/esm-offline/src/dynamic-offline-data.ts +++ b/packages/framework/esm-offline/src/dynamic-offline-data.ts @@ -128,11 +128,11 @@ export function setupDynamicOfflineDataHandler( * Optionally returns only entries of a given type. * @param type The type of the entries to be returned. If `undefined`, returns all types. */ -export async function getDynamicOfflineDataEntries( - type?: string -): Promise> { +export async function getDynamicOfflineDataEntries< + T extends DynamicOfflineData +>(type?: string): Promise> { const userId = await getCurrentUserId(); - return await getDynamicOfflineDataEntriesFor(userId, type); + return await getDynamicOfflineDataEntriesFor(userId, type); } /** @@ -141,16 +141,15 @@ export async function getDynamicOfflineDataEntries( * @param userId The ID of the user whose entries are to be retrieved. * @param type The type of the entries to be returned. If `undefined`, returns all types. */ -export async function getDynamicOfflineDataEntriesFor( - userId: string, - type?: string -): Promise> { +export async function getDynamicOfflineDataEntriesFor< + T extends DynamicOfflineData +>(userId: string, type?: string): Promise> { const filter = type ? { type, users: userId } : { users: userId }; const db = new OfflineDb(); - return await db.dynamicOfflineData + return (await db.dynamicOfflineData .where(filter) .toArray() - .catch(Dexie.errnames.DatabaseClosed, () => []); + .catch(Dexie.errnames.DatabaseClosed, () => [])) as Array; } /** diff --git a/packages/framework/esm-react-utils/package.json b/packages/framework/esm-react-utils/package.json index 15a091f51..e2b45c2e6 100644 --- a/packages/framework/esm-react-utils/package.json +++ b/packages/framework/esm-react-utils/package.json @@ -40,8 +40,7 @@ }, "dependencies": { "lodash-es": "^4.17.21", - "single-spa-react": "~5.0.0", - "swr": "^2.0.1" + "single-spa-react": "~5.0.0" }, "peerDependencies": { "@openmrs/esm-api": "5.x", @@ -53,7 +52,8 @@ "i18next": "19.x", "react": "18.x", "react-dom": "18.x", - "react-i18next": "11.x" + "react-i18next": "11.x", + "swr": "2.x" }, "devDependencies": { "@openmrs/esm-api": "^5.1.0", @@ -68,6 +68,7 @@ "react-dom": "^18.1.0", "react-i18next": "^11.16.9", "rxjs": "^6.5.3", + "swr": "^2.2.2", "webpack": "^5.88.0" } } diff --git a/packages/shell/esm-app-shell/dependencies.json b/packages/shell/esm-app-shell/dependencies.json index 3fae7e7ab..c929b7d12 100644 --- a/packages/shell/esm-app-shell/dependencies.json +++ b/packages/shell/esm-app-shell/dependencies.json @@ -8,5 +8,6 @@ "@openmrs/esm-framework", "@openmrs/esm-framework/src/internal", "rxjs", - "single-spa" + "single-spa", + "swr" ] diff --git a/packages/shell/esm-app-shell/package.json b/packages/shell/esm-app-shell/package.json index 6f76b288e..f0ac2a336 100644 --- a/packages/shell/esm-app-shell/package.json +++ b/packages/shell/esm-app-shell/package.json @@ -50,8 +50,10 @@ "react-i18next": "^11.7.0", "react-router-dom": "^6.3.0", "rxjs": "^6.5.3", + "semver": "^7.3.4", "single-spa": "^5.9.2", "swc-loader": "^0.2.3", + "swr": "^2.2.2", "systemjs": "^6.8.3", "webpack": "^5.88.0", "webpack-pwa-manifest": "^4.3.0", diff --git a/packages/shell/esm-app-shell/webpack.config.js b/packages/shell/esm-app-shell/webpack.config.js index c97a8d3ba..83f45e2b6 100644 --- a/packages/shell/esm-app-shell/webpack.config.js +++ b/packages/shell/esm-app-shell/webpack.config.js @@ -8,8 +8,9 @@ const WebpackPwaManifest = require("webpack-pwa-manifest"); const { InjectManifest } = require("workbox-webpack-plugin"); const { DefinePlugin, container } = require("webpack"); const { basename, dirname, resolve } = require("path"); -const { removeTrailingSlash, getTimestamp } = require("./tools/helpers"); const { readdirSync, statSync, readFileSync } = require("fs"); +const semver = require("semver"); +const { removeTrailingSlash, getTimestamp } = require("./tools/helpers"); const { name, version, dependencies } = require("./package.json"); const sharedDependencies = require("./dependencies.json"); @@ -313,14 +314,42 @@ module.exports = (env, argv = {}) => { new ModuleFederationPlugin({ name, shared: sharedDependencies.reduce((obj, depName) => { - obj[depName] = { - requiredVersion: dependencies[depName] ?? false, - singleton: true, - eager: true, - import: depName, - shareKey: depName, - shareScope: "default", - }; + // This just attempts to align the requiredVersion with what we usually have in peerDependencies + let version = dependencies[depName]; + + if (version) { + if (version.startsWith("^")) { + version = `${semver.parse(version.slice(1)).major}.x`; + } else if (version.startsWith("~")) { + const semVer = semver.parse(version.slice(1)); + version = `${semVer.major}.${semVer.minor}.x`; + } else if (depName === "@openmrs/esm-framework") { + version = `${semver.parse(version).major}.x`; + } + } + + if (depName === "swr") { + // SWR is annoying with Module Federation + // See: https://github.com/webpack/webpack/issues/16125 and https://github.com/vercel/swr/issues/2356 + obj["swr/"] = { + requiredVersion: version, + singleton: true, + eager: true, + import: "swr/", + shareKey: "swr/", + shareScope: "default", + version: require("swr/package.json").version, + }; + } else { + obj[depName] = { + requiredVersion: version ?? false, + singleton: true, + eager: true, + import: depName, + shareKey: depName, + shareScope: "default", + }; + } return obj; }, {}), }), diff --git a/packages/tooling/webpack-config/src/index.ts b/packages/tooling/webpack-config/src/index.ts index 4bab296cf..1a50dedcb 100644 --- a/packages/tooling/webpack-config/src/index.ts +++ b/packages/tooling/webpack-config/src/index.ts @@ -267,13 +267,27 @@ export default ( ...Object.keys(peerDependencies), "@openmrs/esm-framework/src/internal", ].reduce((obj, depName) => { - obj[depName] = { - requiredVersion: peerDependencies[depName] ?? false, - singleton: true, - import: depName, - shareKey: depName, - shareScope: "default", - }; + if (depName === "swr") { + // SWR is annoying with Module Federation + // See: https://github.com/webpack/webpack/issues/16125 and https://github.com/vercel/swr/issues/2356 + obj["swr/"] = { + requiredVersion: version, + singleton: true, + import: "swr/", + shareKey: "swr/", + shareScope: "default", + version: require("swr/package.json").version, + }; + } else { + obj[depName] = { + requiredVersion: peerDependencies[depName] ?? false, + singleton: true, + import: depName, + shareKey: depName, + shareScope: "default", + }; + } + return obj; }, {}), }), diff --git a/yarn.lock b/yarn.lock index 2dca2d1a2..15cd33b0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3667,8 +3667,10 @@ __metadata: react-i18next: ^11.7.0 react-router-dom: ^6.3.0 rxjs: ^6.5.3 + semver: ^7.3.4 single-spa: ^5.9.2 swc-loader: ^0.2.3 + swr: ^2.2.2 systemjs: ^6.8.3 webpack: ^5.88.0 webpack-pwa-manifest: ^4.3.0 @@ -3860,6 +3862,7 @@ __metadata: react-i18next: 11.x rxjs: 6.x single-spa: 5.x + swr: 2.x languageName: unknown linkType: soft @@ -3887,7 +3890,7 @@ __metadata: react-dom: ^18.1.0 react-i18next: ^11.16.9 rxjs: ^6.5.3 - swr: ^2.0.1 + swr: ^2.2.2 webpack: ^5.88.0 peerDependencies: "@openmrs/esm-framework": "*" @@ -3895,6 +3898,7 @@ __metadata: react-dom: 18.x react-i18next: 11.x rxjs: 6.x + swr: 2.x languageName: unknown linkType: soft @@ -3912,7 +3916,7 @@ __metadata: react-i18next: ^11.16.9 react-router-dom: ^6.3.0 rxjs: ^6.5.3 - swr: ^2.0.1 + swr: ^2.2.2 webpack: ^5.88.0 peerDependencies: "@openmrs/esm-framework": "*" @@ -3921,6 +3925,7 @@ __metadata: react-i18next: 11.x react-router-dom: 6.x rxjs: 6.x + swr: 2.x languageName: unknown linkType: soft @@ -3939,7 +3944,7 @@ __metadata: react-i18next: ^11.16.9 react-router-dom: ^6.3.0 rxjs: ^6.5.3 - swr: ^2.0.1 + swr: ^2.2.2 peerDependencies: "@openmrs/esm-framework": "*" react: 18.x @@ -3947,6 +3952,7 @@ __metadata: react-i18next: 11.x react-router-dom: 6.x rxjs: 6.x + swr: 2.x languageName: unknown linkType: soft @@ -4017,7 +4023,7 @@ __metadata: react-i18next: ^11.16.9 rxjs: ^6.5.3 single-spa-react: ~5.0.0 - swr: ^2.0.1 + swr: ^2.2.2 webpack: ^5.88.0 peerDependencies: "@openmrs/esm-api": 5.x @@ -4030,6 +4036,7 @@ __metadata: react: 18.x react-dom: 18.x react-i18next: 11.x + swr: 2.x languageName: unknown linkType: soft @@ -7885,6 +7892,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -18982,14 +18996,15 @@ __metadata: languageName: node linkType: hard -"swr@npm:^2.0.1": - version: 2.0.1 - resolution: "swr@npm:2.0.1" +"swr@npm:^2.2.2": + version: 2.2.2 + resolution: "swr@npm:2.2.2" dependencies: + client-only: ^0.0.1 use-sync-external-store: ^1.2.0 peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 - checksum: 5466e46a80bccf722c212ea3f14668b4e7f047d958f3da7252fba3529e6b1773a1762aa4bbf43aec104cc50625dbd55380539eeef59fd5ad9ebca6ad0552f45d + checksum: afb3d4824f7631bc3a045483f4e4beb5ca9bfa82b64690b3d1133cdf0666ad8e4a9a879657fac5b34dbf19ea9a1503cf92c9bde972f3d40cefe434c73bd9627a languageName: node linkType: hard