Back

How to keep all your Prettier configurations in sync

15 Aug, 2021

I assume that most TypeScript/JavaScript developers are familiar with Prettier? For those who aren't: in 15 words, it's a code formatter that runs in your IDE - usually on-save of each file.

Typically each project needs to contain its own Prettier configuration which if you're not careful, can lead to each of your projects being configured differently! This isn't a real problem - it's rather superficial, but hopefully I'm not the only person out there who prefers all their projects to be configured the same way?

The best way to ensure that all your projects use the same configuration is to literally make them share a single configuration file. One of the easiest ways to share a single configuration between multiple projects is to publish it in an npm package. (For spoilers, you can find mine here!). This should work if you're using Yarn, but for this article, I'll be using npm as my package manager.

Creating a Shared Config

The simplest way to publish an npm package with a prettier config is to:

  • Make a new npm project (npm init -y)
  • Create your Prettier config file (.prettierrc.json/.prettierrc.yml/.prettierrc.js/etc)
  • In your package.json
    • Modify the "main" key to be your Prettier config file name
    • Modify the "files" key to be an array containing only your Prettier config file name
  • Ensure all the other keys are to your liking (name, version, description, etc.)
  • And publish! (npm publish --access public)

When keeping it simple, it really is that easy.

When deciding on a name for your package, you can use any available name you wish; however, seeing as it's a package for your personal use, it's usually seen as a good idea to prefix it with your npm username. This also allows you to use more generic, otherwise already taken names, e.g. @tobysmith568/prettier-config.

Using the Shared Config

Once the Prettier config has been published to the npm registry (or whichever registry you use) it can be used in any other project you have which uses npm dependencies. To keep things clear, I'll be referring to npm projects which depend on your Prettier config package as 'child projects'. To use your shared config in a child project:

  • npm install your new package in the child project, but make sure it's a devDependency (npm i package-name@latest --save-dev)
  • Add a "prettier" key to your package.json in the child project where the value is the name of your new Prettier npm package
  • Remove any other prettier configurations you may have in the child project

Extras!

Technically you're done! All your projects can use the Prettier configuration you keep in this npm package, but I have some extra tips and advice for taking your config to the next level.

Use a JavaScript Configuration

The Prettier documentation says that you can keep your config in many different file types including JSON, JavaScript, and YAML; however, JavaScript is slightly different from the rest as using it allows you to customise your configuration on a per-project basis when you have/need to. This relies on the JavaScript spread syntax and the use of it to spread one config into another.

You don't want to get to a point where all your projects are overriding your shared configuration otherwise you might as well not use a shared config, but being able to override your config (when you must) can be helpful.

If you're not already using a JavaScript config and you want to overwrite fields in you config you'll need to:

  • Update your Prettier configuration to be JavaScript file (.js) and ensure you reformat the content
  • Update the name of the file in the package.json's main and files options
  • Re-publish your npm package with a higher version number
  • In the child project, install the new, latest version (npm i package-name@latest --save-dev)

To then override your shared config, in your child project:

  • Remove the "prettier" key from your package.json in the child project
  • Create a JavaScript config file called .prettierrc.js with the following content
module.exports = {
  ...require("YOUR CONFIG PACKAGE NAME"),
  // Override values here
};

Doing the above means that you're using a local config in your child project (files called .prettierrc.js get picked up automatically) where that file imports and spreads from your shared config.

Plugins

Prettier supports custom plugins, and it picks up on them automatically so long as their npm package name begins with "prettier-plugin-"; this means that you can include prettier plugins in your shared config and have them be included in all your child projects automatically. (A personal favourite of mine is prettier-plugin-organize-imports.)

To do this, simply npm install the plugins you want into your shared config; however, you need to make sure that you install them as standard dependencies - not devDependencies.

Plugins need to be standard dependencies because if you install them as devDependencies then they won't be installed in your child projects. Don't worry though, because you install the shared config as a devDependency in your child projects the plugins won't be included in your builds.

Setup a GitHub Action

Assuming your configuration is stored in a repository on GitHub, you can use a GitHub Action to automatically publish your package for you!

I'm not going to go over the basics of GitHub Actions in this article, but it's as simple as using the config below:

name: Node.js Package

on:
  release:
    types: [created]

jobs:
  publish-npm:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: 16
          registry-url: https://registry.npmjs.org/
      - run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{secrets.npm_token}}

Note that you'll also need to make an npm Automation API key (https://www.npmjs.com/settings/YOUR_NPM_USERNAME/tokens) and save it in the repository settings as a secret called npm_token.

As you can see on line 4, this Action is triggered on the creation of a GitHub release, but you could just as easily have it trigger on push to the main branch if you'd rather.

Use a TypeScript Configuration

Ok, ok - this extra is overkill for sure; but it can be done, and I've done it in my own Prettier config with the rationale that it will reveal typos in the config file and will stop them from making it into child projects.

To use TypeScript for your config file:

  • Ensure that you're currently using a JavaScript config, not JSON/YAML/TOML/etc
  • Rename your JavaScript config to a TypeScript file (.ts) and move it into a new folder called src
  • Add a tsconfig.json file and set it up with:
    • "target": "ES6"
    • "module": "CommonJS"
    • "rootDir": "./src"
    • "outDir": "./dist"
  • In the package.json
    • Set the "files" key to only contain "dist"
    • Add a "build" script with the value "tsc"
    • Change the "main" key to "dist/<your config file name>.js" <-- Note .js, not .ts!
  • npm install typescript and @types/prettier as devDependencies
  • Import Options from "prettier" at the top of your config file and add the type to your config like this:
import { Options } from "prettier";

const config: Options = {
  // Your config options here
};

export = config;

If you now introduce a typo in your config, either in a key or a value, then you will get a linting error which will fail your builds.

If you're using a GitHub action, then you'll need to add - run: npm ci and then - run: npm run build before the npm publish.

Ok, that's it. I'm out of extras, but hopefully, you found these useful? And I hope you now enjoy having your projects keep in sync with each other as much as I do!