End user web applications built on top of the Recidiviz data platform.
This repo uses the Nx integrated monorepo pattern to support a number of separate modules and applications that can be used together or separately. Nx provides a lot of tooling that we will not discuss in detail here but that we may use routinely for development or CI; you are encouraged to spend some time with the Nx docs and get familiar with its core features. We generally aspire to stick to (or migrate towards) the default ways of doing things in Nx; e.g., using standard plugins for common tasks, adopting the typical folder structure, etc.
Code in this repo is organized into projects, which are the main unit of code organization in Nx. In practice a project in Nx is just a folder with a project.json config file in it, and import statements can freely cross project boundaries (except as restricted by linting rules). Refer to the README for a given project for more detailed information about how to use it and develop on it.
Application projects (a project in Nx ) (found in apps/**) are the primary entry points for the applications contained in this codebase, but projects may be added to other subfolders as well as the codebase evolves.
-
Grab the source:
git clone https://github.com/Recidiviz/pulse-dashboards.git -
Ensure you are using the correct version of Node (if you don't use NVM, just check the .nvmrc file and ensure you are using that version).
nvm use -
Install Yarn package manager:
brew install yarnFor alternative Yarn installation options, see Yarn Installation.
If you have classic Yarn (currently v1.22.22) and are struggling to get brew to upgrade, you may need to install corepack and yarn as described here).
-
Install dependencies:
yarn install -
Install Firebase Tools (version >=10 required) and ensure you are logged in:
brew install firebase-cliThen:
firebase login -
Additional recommendations:
- Install Nx globally (convenient for running package scripts):
npm install -g nx@latest - Install a linting package for your preferred code editor that hooks into ESLint, such as the ESLint extension for VS Code.
- Install a formatting package for your preferred code editor that hooks into Prettier, such as the Prettier - Code Formatter extension for VS Code.
- Install the Nx Console for your code editor if you prefer a GUI for exploring and using Nx.
- To make
git blamemore informative, tell it to ignore reformatting commits by runninggit config blame.ignorerevsfile .git-blame-ignore-revs.
- Install Nx globally (convenient for running package scripts):
If you install the Vitest VS Code extension, you can run tests from the editor. It should work out of the box with our current setup!
NOTE: If your test need certain environment variables to be set, you can add them to the vitest.config.ts file in the project you are testing. See apps/sentencing/vite.config.mts for an example.
It can be helpful to run Nx tasks in CI, e.g. to validate that files in a certain directory follow a given format. nx affected is a useful command to run tasks on projects with changes (docs).
In order to do this successfully, set up your target in the right project.json and add a job to the build.yml file that has the following steps in order:
- Checkout the repo
- uses: actions/checkout@v4
with:
# By default, the 'pull_request' action checks out the merge commit, which doesn't
# necessarily have the latest changes for the branch. This will run the action on the
# latest commit in the branch.
# It is possible this workflow is triggered manually, so use the GITHUB_SHA in that case
ref: ${{ github.event.pull_request.head.sha || github.sha }}
# 'nx affected' requires the full git history to determine affected projects
fetch-depth: 0
- Set up Node and Yarn
- name: Enable corepack for yarn
run: corepack enable
- name: Use Node
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "yarn"
- name: "Setup Yarn"
run: |
yarn install --immutable
- Set the base and head SHAs for
nx affected
- name: Set shas for Nx
uses: nrwl/nx-set-shas@v4
- Run the
nx affectedcommand with the desired target
- name: <Readable name of job>
run: yarn nx affected -t <target-name>
We use ESLint to lint our codebase. There are many ESLint plugins available to lint files other than .js files and can often be found with the prefix eslint-plugin-<file_type>, e.g. eslint-plugin-yml (docs). It might be helpful to add linting to both pre-commit and CI checks to help
with the development flow.
-
Install the plugin with the command below. The
-Dflag is used to install this in the dev dependencies section of the top-levelpackage.jsonyarn add -D <plugin-name> -
Add the plugin name without the
eslint-pluginprefix, e.g.yml, to the list ofpluginsin the top-level.eslintrc.json -
To configure the rules from the plugin, follow the package's usage instructions. This often means adding a new object to the
overrideslist in the.eslintrc.jsonfile.{ "files": ["*.yaml", "*.yml"], "extends": ["plugin:yml/standard"] }- If you want to lint this type of file across the entire codebase, add this to the top-level
.eslintrc.json. However, if you want to lint files from a certain project, you can add this override to the.eslintrc.jsonfor the given project. See/libs/atmos/.eslintrc.jsonto see the ESLint config for linting YAML only in this project
- If you want to lint this type of file across the entire codebase, add this to the top-level
We use Husky to run our pre-commit checks, which are configured at .husky/_/pre-commit. The pre-commit check we have is lint-staged and is configured at lint-staged.config.js.
- Make sure the project has a
lint-filestarget whose command is"eslint --max-warnings 0". If you used the library generator below, this will automatically be included in theproject.json. - Add the new file extension to the module exports with the
lintCommandas the value.- The affected/staged files are passed in automatically when linting
- Add a
linttarget to the relevantproject.json. Unlike thelint-filestarget above, this is not automatically included in theproject.jsonbecause projects rely on the default inferred task atnx.json.- In most cases, ESLint should pick up file extensions with rules in
overrides, so the defaulteslint .will work. However, if you want to specify a specific directory to look in, you can use a glob pattern to specify the path (docs)
- In most cases, ESLint should pick up file extensions with rules in
- If you are making changes for a new project, ensure that the CI check includes the project once you've added the
project.jsonto the PR.
One of the core concepts of Nx is the idea of organizing your code into libraries, and putting most of your code into libraries rather than apps. While it may be a scary and overloaded word, in Nx parlance a "library" is basically just a folder with an Nx config file in it (usually project.json). This lets Nx include that directory in its "project graph", which it uses to model the dependencies between different projects in the repo.
Briefly stated, there are a few benefits to doing this:
- Allows us to only run tasks like
lintandtestwhen there are changes affecting that project, vianx affectedin CI or even just during development vianx test [your-project] - Allows us to have more fine-grained control over which features can depend on which other features, via the Nx enforce-module-boundaries linting package.
TL;DR: run nx generate ~repo:lib [my-library] and follow the prompts. Update the generated files as necessary.
Long version: Nx has various plugins that automate the boilerplate and nuances of creating and configuring libraries, such as @nx/js and @nx/react. You can use those directly when needed, but in most cases you will want to reach for our local plugin first, which extends those plugins by setting our preferred options and extending the default plugin outputs with additional configuration and features that are tailored to our applications. These are meant to be sensible defaults, you can always override or extend them as needed to suit your use case. See /plugins/repo to learn more about its implementation.
The below instructions apply to creating an app that doesn't have a UI as the client, e.g. JII texting.
-
Generate the app
nx g @nx/node:app apps/{server-name} --e2eTestRunner=none --unitTestRunner=none- This creates a new directory named {server_name} in apps without adding a directory for e2e tests. The second argument ensures nx doesn't set up our testing framework with jest automatically, since we use vitest.
-
Inspect any changes to the
yarn.lockandpackage.jsonto look for any unnecessary changes made by the nx generator. You might also want to check if the Fastify version was updated by searching forfastify@inyarn.lock.