Skip to content

Maintainer's Guide

zallen edited this page Oct 21, 2021 · 7 revisions

The Monorepo

We use a monorepo because we have many inter-dependencies between our own packages (ex. react-core depends on react-styles). Taken from babel's monorepo:

Pros:

  • Single lint, build, test and release process
  • Easy to coordinate changes across modules
  • Single place to report issues
  • Easier to setup a development environment
  • Tests across modules are run together which finds bugs that touch multiple modules easier

Cons:

  • Codebase looks more intimidating
  • Repo is bigger in size
  • Can't use npm install
  • Makes @jeff-phillips-18 upset

Dependencies

PatternFly uses yarn workspaces to manage our monorepo dependencies and inter-dependencies. Node module resolution works great, except for sass in catalog-view-extension, which we created an importer for.

Our packages themselves have few dependencies for better maintainability. Since PatternFly is widely used it's best to keep the dependencies to a minimum so the bundle size stays small.

Building

We are a component library which differs from a traditional SPA in distributing our JS and CSS. Rather than using Webpack for our components to create a bundle of JS, we use Typescript to convert our source TS files to various module types, including esm, commonjs, and umd. We also publish our .d.ts types for our Typescript consumers which is currently about half of all consumers.

CSS

Our react-styles package consumes CSS from our @patternfly/patternfly and extracts the class names to Javascript variables for use in React. react-styles also writes require('component-name.css'); so that bundlers include CSS only for the components that they use.

Incremental build

Many of our packages are not updated in-between local or CI builds. We leverage tscs incremental build option to track file changes between builds and only rebuild necessary packages.

Continuous Deployment

We use GitHub Actions to continually test and generate preview links for PRs and create rolling prereleases for pushes to main.

Our pipeline looks like this:

Pipeline

Only pushes to main trigger the deploy step which uses lerna to publish packages. Lerna bumps the versions all changed packages and their dependents, pushes git tags, pushes a git commit to main, and then publishes to npm using GH_TOKEN and NPM_TOKEN.

Our GitHub actions config is here, along with our scripts for some of the steps. It's difficult to conditionally check whether the workflow is running in a PR or on the main branch so we need two workflow files. Most steps are duplicated between the 2, so in order to keep the pipeline DRY there's a simple templating script to generate the workflow files from the partials.

Some things to note about the config:

  • We cache node_modules for each sub-project based off the root yarn.lock
  • We cache dist folders and selectively build the projects that have changed based off the contents of their src directories