Developing frontend modules
After setting up your local environment, you'll typically want to work on a frontend module. Broadly speaking, the approach to follow to get a frontend module running locally involves the following steps:
Installing dependencies
We use yarn
as our package manager. To install dependencies, you'll typically cd
into the repo of the frontend module you want to work on and run yarn
. For example, if you want to work on the patient-chart
frontend module, you'd run:
cd openmrs-esm-patient-chart
yarn
Running frontend modules locally
Typically, you’ll want to run a specific frontend module within a monorepo. For example, you might want to run only the conditions
frontend module. To do that, run yarn start --sources
and pass in the name of your package of interest as a string:
yarn start --sources 'packages/esm-patient-conditions-app'
Note that the argument following packages in the command has to be the frontend module’s full path. It is also possible to pass multiple arguments to yarn start --sources
. Doing so allows you to run multiple frontend modules simultaneously. Imagine a situation where you need to make a change to both the vitals and biometrics widgets in the patient chart. To get them to run simultaneously, you’d run:
yarn start --sources 'packages/esm-patient-biometrics-app' 'packages/esm-patient-vitals-app'
Running this command fires up a dev server in your default browser. By default, this server will run on port 8080. If you wish to run the server on a different you can specify that by passing a --port
argument. For example, if you run the command above, you'd have biometrics and vitals getting served from http://localhost:8080/openmrs/spa (opens in a new tab). If you then want to run, say the patient search frontend module from the patient management monorepo, you could specify a different port number. You'd then run it like so:
yarn start --sources 'packages/esm-patient-chart-app' --port=7000
And that should fire up a local dev server at http://localhost:7000/openmrs/spa
.
When you fire up a dev server in this way, several things happen under the hood. If you look at the scripts
section of a manifest file (package.json
) in a frontend module, you should see a start
script that looks something like:
“start”: "openmrs develop"
What this means is that running yarn start
invokes the develop
command of the openmrs
tooling. This command starts a new frontend development session using the OpenMRS app shell. The tooling sets up the import map, adds entries for core applications from the app shell and overrides the entries for the modules running locally. Ultimately, the corollary is that you can make changes to frontend modules in your local environment and have those changes propagate to the dev server in your browser. The tooling allows you to do various other cool things, such as:
- Specifying an alternative backend server (other than the community dev3 server) and proxying requests to it.
- Specifying an alternative target server path (defaults to
/openmrs/spa
). - Specifying an alternative API URL (defaults to
openmrs
). - Specifying a custom import map.
Running the app shell locally
Most of the time, this should be all a developer needs to do to get a local dev server running. One notable departure, however, is running the app shell
. You’ll need to run the shell directly if you want to:
-
Make changes to the
styleguide
frontend module and test them out locally. -
Test out changes to the following applications:
devtools
- used for setting up import map overrides.implementer tools
- used for configuring extensions and UI components on the fly.login
- the reference application login page.offline tools
- the offline tools dashboard.primary navigation
- the navigation menu.
To run the app shell, you’ll need to cd
into the esm-core
monorepo and run:
yarn run:shell
This command will fire up a dev server at http://localhost:8080/openmrs/spa
. You can then navigate to the app shell at http://localhost:8080/openmrs/spa/shell
. From there, you can navigate to the applications you want to test out.
Because these applications reside in the apps
directory of the esm-core
monorepo, running them within the context of the app shell makes it a lot easier to test out changes. Something to note when using this approach, however, is that for your changes to propagate to your local server, you might need to rebuild your frontend module. To do this, run:
yarn turbo build
This command should run the build
script in all of the constituent frontend modules whose code you’ve changed. Once the build is complete, you should see your changes reflected in the dev server in your browser.
How do I know where to make changes?
There is no simple correlation between frontend modules and pages and content. The first step to figuring out where to make changes is to identify the frontend module you want to work on. For example, if you want to make changes to the patient chart, you’d work on the patient-chart
frontend module. If you want to make changes to the patient search, you’d work on the patient-search
frontend module. If you want to make changes to the login page, you’d work on the login
frontend module. And so on and so forth. There are a few ways to figure out which frontend module you need to work on:
- You can inspect an element in the browser and look at it's class name. You can then search through your IDE for that class name. This should point you to the specific bit of code in the frontend module you need to work on.
- You could search for specific strings in the codebase. For example, if you see the string
Vitals history
in the UI, you could search for that string in your IDE. This should point you to the frontend module that renders that string (the vitals frontend module, in this case). - You could also use the UI editor in the implementer tools to see which extensions and extension slots get rendered in the UI. You can then search for those extensions and extension slots in your IDE and make changes to them. This is a bit more involved, but it’s a good way to get a sense of the structure of the UI and frontend modules it contains.
- Go through the list of key repositories and their descriptions. There aren’t very many and they're typically domain-specific, so it’s likely you can narrow down what you’re trying to work on just by looking at the list.
There are no hard and fast rules for figuring out where to make changes. The best approach is to try out different things and see what works for you. The more you work on the codebase, the more familiar you’ll become with it and the easier it’ll be to figure out where to make changes. If you have done all of the things above and still haven’t found the code you’re looking for, feel free to ask in the #openmrs-helpme channel (opens in a new tab) on Slack.
How can I develop against a restricted environment?
In general you can develop against another environment using the --backend
flag. If the other environment is guarded, e.g., by an IP or network restriction then this is something you need to take care of on your local machine. In case the guarded environment is restricted via some SSO mechanism using a cookie you could use the --add-cookie
flag to achieve this. As an example, look at the access for a development server from the ICRC:
npx openmrs start --backend "https://emr-v2.test.icrc.org/" --add-cookie "MRHSession=1234..."
The cookie must be obtained by you and strongly depends on the backend used.
Should I write tests?
Yes, absolutely! We have a test suite for the frontend modules in the monorepo. We use Jest (opens in a new tab) for testing. We also use React Testing Library (opens in a new tab) for testing React components. We have a testing guide that you can refer to for more information on how to write tests. We're adding support for Playwright e2e tests (opens in a new tab) as well across our repositories. Consider adding e2e tests for your changes as well. Going through the testing guide as well as the tests in the codebase should give you a good sense of how to write tests.
Pushing your changes to GitHub
Once you’re done working on a feature or fix, you’ll want to push your changes to GitHub and file a PR. To do this, package your changes in a commit. We have guidelines for writing commit messages in our contributing guide (opens in a new tab).
Creating a commit should trigger some pre-commit hooks. Typically, part of the work these hooks do involves running yarn run extract-translations
. This task fires up turborepo
and runs the extract-translations
task in each package in the monorepo. This task extracts translation keys and strings and updates the default locale’s translations file, en.json
in the translations
directory of your frontend module. This is useful for internationalisation. After the pre-commit hooks run, you then want to push your changes to GitHub. Doing so will trigger a pre-push
hook that will typically run the following tasks in parallel using turborepo
:
typescript
- checks your application’s types.lint
- runs a lint check using Prettier.test
- runs tests using Jest and React Testing Library.
If any of these tasks fail, the push should fail. The terminal should display the error message(s) that caused the push to fail. You’ll need to fix the error(s) and push again. Once the push succeeds, you can go to the GitHub repo related to your package and file a pull request.
Maintaining dependencies
Maintaining up to date dependencies is important to leverage the latest features, performance improvements and security patches. Yarn 3 provides a handy interactive tool to help you keep your dependencies up to date. To use it, run the following command:
yarn upgrade-interactive
This command lists your projects dependencies that are eligible for updates. You can navigate the list using your keyboard's arrow keys. In general, you want to avoid making major version updates because they can introduce breaking changes. You also want to keep OpenMRS-related dependencies (such as openmrs
and @openmrs/esm-framework
) pinned to next
. Everything else should generally be OK to update to the latest version. Once you've selected the dependencies you want to update, you can press Enter
to update them. Make sure to test your changes to ensure that nothing has broken. Ensure the app runs OK, tests pass and the app can get built correctly. If everything looks good, you can commit your changes and push them to GitHub.
Troubleshooting
I'm getting a bunch of errors related to missing dependencies
If you're working off of a fork, it's possible that your fork might miss the latest changes from main
. To fix this, you can run the following command to update your fork:
git pull upstream main --rebase
I'm getting a Error: ENOSPC: System limit for number of file watchers reached
error
If you’re running Linux, you may see the following error the first time you run a a dev server: Error: ENOSPC: System limit for number of file watchers reached
. If that happens, you need to increase the system limit for the number of file watchers. See this StackOverflow answer (opens in a new tab).
I’m not seeing the latest @openmrs/esm-framework. How do I update the dependency?
If you notice your app suddenly stop working, it might be because a core library has changed in the app shell on the server you are developing against. The main dependencies shared by all OpenMRS frontend modules are openmrs
and @openmrs/esm-framework
. They can be updated like this:
# Upgrade core libraries
yarn up openmrs @openmrs/esm-framework
# Reset version specifiers to `next`. Don't commit actual version numbers.
git checkout package.json
# Run `yarn` to recreate the lockfile
yarn
I'm getting a ERR_OSSL_EVP_UNSUPPORTED
error when running yarn run build
If you're running or building the form-entry
package (an Angular wrapper around the AMPATH Form Engine) using Node v18, you may see the following error:
Error: error:0308010C:digital envelope routines::unsupported
at new Hash (node:internal/crypto/hash:71:19)
at Object.createHash (node:crypto:133:10)
at BulkUpdateDecorator.hashFactory (/Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/util/createHash.js:145:18)
at BulkUpdateDecorator.update (/Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/util/createHash.js:46:50)
at RawSource.updateHash (/Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/webpack-sources/lib/RawSource.js:77:8)
at NormalModule._initBuildHash (/Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/NormalModule.js:880:17)
at handleParseResult (/Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/NormalModule.js:946:10)
at /Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/NormalModule.js:1040:4
at processResult (/Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/NormalModule.js:755:11)
at /Users/denniskigen/code/openmrs/openmrs-esm-patient-chart/node_modules/@angular-devkit/build-angular/node_modules/webpack/lib/NormalModule.js:819:5 {
opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'
}
To get around this, run:
export NODE_OPTIONS=--openssl-legacy-provider
I'm getting errors after upgrading @carbon/react
You might see the following errors in your terminal when running a local dev server after upgrading @carbon/react
:
ERROR in src/components/search-history/search-history.component.tsx:8:3
TS2305: Module '"@carbon/react"' has no exported member 'ModalHeader'.
ERROR in src/components/search-history/search-history.component.tsx:9:3
TS2305: Module '"@carbon/react"' has no exported member 'Pagination'.
To fix these, you need to update Carbon's ambient type declarations. To do so, add the following entries to your declarations.d.ts
file:
declare module "@carbon/react";
declare type SideNavProps = {};
I'm getting merge conflicts in my yarn.lock
file
To resolve merge conflicts in your yarn.lock
file, run the following command:
git checkout HEAD yarn.lock && yarn
The verify
job is failing on GitHub Actions for my PR
You likely have a lint or typescript error in your code. ESLint is setup to fail when it flags warnings. Run yarn turbo lint
locally to the specific lint error in your IDE. For TypeScript errors, you should see the error in the CI log. If not, run yarn turbo typescript
locally.
I'm getting 502
errors from attempting to fetch the session when running a local dev server
If you're running a local dev server and you're getting 502
errors from attempting to fetch the session, that's likely because the dev3 server (which your local dev server proxies to) is down. Check that you can log in to dev3 (opens in a new tab) first.
If you cannot log in, then you'll need to wait for the server to come back up.