How to create a shared Prettier configuration
Published: 2022-10-14 13:13:25

> This post was originally posted over on my personal website. Maybe you want to check it out [[HERE]](https://christian-hollaender.de/posts/2-how-to-create-a-shared-prettier-configuration).

---

Keeping configs for styling and linting tools consistent across multiple projects can be challenging. Many tools support shared configs, which are created once and used everywhere. This article will discover how to create a shared configuration for prettier.

---

## Motivation
Recently we decided, that we want to have a common styling on how to write our Typescript code. This would make it easier for us to read and understand code others had written or even that we have written ourselves a few months ago. Therefore we agreed on integrating [Prettier](https://prettier.io/). An opinionated code formatter.

When we first adopted prettier on all our dozens of projects we copy/pasted the configuration from one project to the other. As we updated the config to fit our needs we were faced with inconsistent options in our projects. So we needed to figure out, how to change the prettier options globally for all our projects at the same time. Luckily the prettier maintainers got you covered. They already thought of that problem and provide a native way to share the config across multiple projects by creating a separate npm package. ([Sharing Configurations](https://prettier.io/docs/en/configuration.html#sharing-configurations))

In this guide, I want to show you how we structured our shared config to meet several projects with different prettier plugins, like the phenomenal [Tailwind Prettier plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss). But also to make it extensible if a project needs some special config options only for this specific project.

## Get Started
First of all, we need to initialize our repository. As I love typescript I wanted that the shared config itself is written in typescript and gets transpiled to javascript through a built-step. I will do this by using Rollup.js, but Webpack should do the trick as well. So let’s get started..

Installing Typescript should be straightforward by running `npm i -D typescript` and creating a `tsconfig.json` in the project root. I am using this one:

```json
{
  "compilerOptions": {
    "target": "es6",
    "module": "ESNext",
    "strict": true,
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "outDir": "dist",
    "declaration": true,
    "declarationDir": "dist/types",
    "declarationMap": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2015",
      "es2017"
    ]
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

Now I want to initialize the Rollup.js setup by adding the `rollup.config.js`:

```javascript
import excludeDependenciesFromBundle from 'rollup-plugin-exclude-dependencies-from-bundle';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import { visualizer } from 'rollup-plugin-visualizer';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('./package.json');

export default {
    input: 'src/index.ts',
    inlineDynamicImports: true,
    output: [
        {
            file: packageJson.main,
            format: 'cjs',
            exports: 'default',
            sourcemap: true,
        },
    ],
    plugins: [
        excludeDependenciesFromBundle(),
        resolve(),
        commonjs(),
        typescript({
            useTsconfigDeclarationDir: true,
        }),
        visualizer({
            filename: 'package-deps.html',
            template: 'sunburst',
            gzipSize: true,
            brotliSize: true,
        }),
    ],
};
```

> I will skip the installation of the necessary dependencies, as I think you already know how to do this when you are interested in creating a shared prettier config ;)

Now we are finally ready to actually create the shared config in our `src` folder:

```typescript
import type { Config } from 'prettier';

const defaultConfig: Config = {
    singleQuote: true,
    bracketSameLine: true,
    tabWidth: 4,
    plugins: [],
    overrides: [
        {
            files: '**/*.{json,yml,yaml}',
            options: {
                tabWidth: 2,
            },
        },
        {
            files: '**/*.{yml,yaml}',
            options: {
                singleQuote: false,
                bracketSpacing: false,
            },
        },
    ],
};

export default defaultConfig;
```

Last but not least I want to show you a cool little trick to use the just-built config right in this project. When do this by creating a `prettier.config.js` file in our project root. In there we put the following content:

```javascript
/** @type {import('prettier').Options} */
module.exports = require('./dist/prettier-config');
```

We are actually just importing our build module and re-export it so our prettier command can find it. But note: You need to build before you can apply the changes of the shared config to the project because there is a built-step involved!