How to create a shared Prettier configuration

christian98

Christian Holländer

Posted on October 14, 2022

How to create a shared Prettier configuration

This post was originally posted over on my personal website. Maybe you want to check it out [HERE].


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. 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)

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. 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:

{
  "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"
  ]
}
Enter fullscreen mode Exit fullscreen mode

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

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,
        }),
    ],
};
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

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:

/** @type {import('prettier').Options} */
module.exports = require('./dist/prettier-config');
Enter fullscreen mode Exit fullscreen mode

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!

💖 💪 🙅 🚩
christian98
Christian Holländer

Posted on October 14, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024