Build a scalable front-end with Rush monorepo and React — Repo Setup + Import projects + Prettier

In a multi-repository project structure, it's only a matter of time when you'll need to reuse some code from one project to another. Adopting a monorepo architecture can help the team share and contribute code in a simple manner.
I won't cover in this article the pros and cons of choosing this approach, because there are plenty of resources that debate this topic, instead, I'll focus on the implementation of a scalable monorepo using Rush.js and React.
Tools
We'll be using the following tools:
Goals
Before implementing the monorepo, let's define the goals we want to achieve using these tools:
  • Multiple applications
  • Code sharing between applications
  • Shared tools and configurations
  • Enforced rules for code quality
  • Automated workflow for development
  • TL;DR
    If you're interested in just see the code, you can find it here: https://github.com/abereghici/rush-monorepo-boilerplate
    If you want to see an example with Rush used in a real, large project, you can look at ITwin.js, an open-source project developed by Bentley Systems.
    Guide
    Create a new repository
    I assume you already created an empty Github repository for this project. Let's clone it locally and let the magic begin!
    Initialize the rush monorepo
    Inside of your project folder, run the following commands:
    npm install -g @microsoft/rush
    
    rush init
    After this command, you'll see a bunch of files and folders created. You can check the config files reference here.
    At this point, we can remove unnecessary files and create our first commit.
    rm -rf .travis.yml
    
    git add .
    git commit -m "Initial commit"
    git push origin master
    Import existing projects without loosing git history
    You don't really want to perform a migration to monorepo if you lose all the history of your projects. If everything will point to the commit where you merged the projects, you won't be able to revert to the previous commits, or run git blame or git bisect.
    We can copy all projects inside of the monorepo and keep the git history of each project with a single git command: git subtree.
    Let's suppose we want to import the following project into our monorepo https://github.com/abereghici/react-app. We'll do it using the command git subtree add
    git subtree add --prefix apps/react-app \
     https://github.com/abereghici/react-app master
    Let's decode the parameters of this command:
  • apps/react-app is used to specify the path inside of the monorepo where the project will be imported.
  • https://github.com/abereghici/react-app is the remote repository URL of the project we want to import.
  • master is the branch from where the project will be imported.
  • Now if you run git log you'll see the history of react-app project inside of our monorepo.
    Open apps/react-app/package.json and change the name of the project with @monorepo/react-app.
    The last step is to register @monorepo/react-app project in rush configuration file. Open rush.json file and add an entry like this under the projects inventory:
    "projects": [
        {
          "packageName": "@monorepo/react-app",
          "projectFolder": "apps/react-app",
          "reviewCategory": "production"
        }
      ]
    This tells to Rush that it should manage this project. Next, run rush update to install the dependencies of react-app project. This command can be launched in any subfolder of the repo folder that contains rush.json file.
    rush update
    git add .
    git commit -m "Imported react-app project"
    git push origin master
    Adding Prettier
    We want to have consistent syntax and formatting across all code files in our monorepo. So we'll apply Prettier globally for the entire repository. We'll run it during git commit.
    Let's create a configuration file in the root of the repo. Prettier allows many different names for this config file, but we'll use .prettierrc.js
    <repo root>/.prettierrc.js
    module.exports = {
      arrowParens: 'avoid',
      bracketSpacing: true,
      htmlWhitespaceSensitivity: 'css',
      insertPragma: false,
      jsxBracketSameLine: false,
      jsxSingleQuote: false,
      printWidth: 80,
      proseWrap: 'preserve',
      quoteProps: 'as-needed',
      requirePragma: false,
      semi: true,
      singleQuote: true,
      tabWidth: 2,
      trailingComma: 'es5',
      useTabs: false,
    };
    You also need to make a .prettierignore file to tell Prettier which files to skip. It is recommended for .prettierignore to extend the same patterns used in .gitignore.
    cp .gitignore .prettierignore
    Once the configuration is set up, next we need to invoke Prettier manually to reformat all the existing source files.
    # See what files Prettier will format
    # check the output and modify .prettierignore rules if needed
    npx prettier . --list-different
    
    # When you are ready, this will format all the source files
    npx prettier . --write
    To speed up the prettier process on git commit hook, we'll use prettier-quick to calculate the subset of files that are staged for commit and format them.
    Let's create a rush auto-installer, where we'll list all dependencies for formatting.
    # This creates the common/autoinstallers/rush-prettier/package.json file:
    rush init-autoinstaller --name rush-prettier
    Install the dependencies:
    cd common/autoinstallers/rush-prettier
    
    # Install the dependencies.
    # You can also manually edit the "dependencies" in the package.json file
     pnpm install prettier
     pnpm install pretty-quick
    
    # update the auto-installer
    rush update-autoinstaller --name rush-prettier
    Next, we will create a rush prettier custom command that invokes the pretty-quick tool. Add this to the "commands" section of config/rush/command-line.json file:
    . . .
      "commands": [
        {
          "name": "prettier",
          "commandKind": "global",
          "summary": "Used by the pre-commit Git hook. This command invokes Prettier to reformat staged changes.",
          "safeForSimultaneousRushProcesses": true,
    
          "autoinstallerName": "rush-prettier",
    
          // This will invoke common/autoinstallers/rush-prettier/node_modules/.bin/pretty-quick
          "shellCommand": "pretty-quick --staged"
        }
        . . .
    After saving these changes, let’s test our custom command by running rush prettier.
    The last step is to add a Git hook that invokes rush prettier automatically whenever git commit is performed.
    Let's create a file called pre-commit in the common/git-hooks folder:
    common/git-hooks/pre-commit
    #!/bin/sh
    # Called by "git commit" with no arguments.  The hook should
    # exit with non-zero status after issuing an appropriate message if
    # it wants to stop the commit.
    
    # Invoke the "rush prettier" custom command to reformat files whenever they
    # are committed. The command is defined in common/config/rush/command-line.json
    # and uses the "rush-prettier" autoinstaller.
    node common/scripts/install-run-rush.js prettier || exit $?
    Install the hook by running rush install.
    We're done! Now on every commit we'll be automatically prettified.

    54

    This website collects cookies to deliver better user experience

    Build a scalable front-end with Rush monorepo and React — Repo Setup + Import projects + Prettier