Getting Started with Monorepo, Linter, and Formatter

- Lire en français
Resources: huchet-vue

In this series, we will begin from scratch. This is the optimal way to comprehend the process. We'll establish a monorepo and set up a linter and a formatter.

Note

The process is versatile and can be applied to any project, not just a component library.

Setting Up the Project

A monorepo is a repository housing multiple projects. In our case, we will have the package @barbapapazes/huchet-vue, two playgrounds, and a storybook. Employing a monorepo enables the management of all these projects within a single repository, facilitates code sharing, and allows for a single dependency installation, keeping everything synchronized and compatible. We could easily imagine adding more packages in the future, such as a custom Tailwind CSS configuration or a shared utility library.

To create a monorepo, we'll utilize pnpm workspaces, a feature within the pnpm package manager that installs dependencies smartly, using hard links and symlinks to save disk space and time.

First, create a new directory and initialize a new git repository:

bash
mkdir huchet-vue && cd huchet-vue && npm init -y

Open this folder in your code editor and modify the package.json file:

json
{
  "name": "huchet-vue",
  "version": "0.0.0",
  "type": "module",
  "private": true
}

Note

Setting the private field to true in a package.json file prevents accidental publication to the npm registry. Our root package.json is for monorepo management rather than publication.

This package.json is the primary file for our monorepo. Future packages will each have their own package.json. The main file will only include dependencies and scripts impacting the entire monorepo, not shared between packages.

Before proceeding, adjust a pnpm setting by creating a .npmrc file:

bash
echo "shamefully-hoist=true" > .npmrc

This setting allows pnpm to hoist dependencies to the root node_modules folder. While not default or recommended, it is necessary in our case.

Note

By default, pnpm creates a semistrict node_modules, allowing access to undeclared dependencies only from within node_modules.

Linter and Formatter

Adding a linter and a formatter ensures our code remains clean and consistent, a fundamental requirement for any project, especially a component library used by other developers.

Note

A formatter automatically formats your code according to set rules, while a linter analyzes code statically to detect potential errors and enforce rules.

Commonly, Prettier is used as a formatter, and ESLint as a linter. Both have extensive plugins and configurations, but integrating them can be complex. As I'm not opinionated about code style, less configuration is better.

For this project, as with all my projects, we'll use the ESLint configuration by Anthony Fu. It's a highly opinionated setup using ESLint and Stylistic.

Install it with a simple command:

bash
pnpm dlx @antfu/eslint-config@latest && pnpm add -D typescript

Note

These are installed at the root of the monorepo to maintain a unified source of truth for linting and formatting, a common monorepo practice.

Add two scripts to your package.json file:

json
{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint --fix ."
  }
}

Update the eslint.config.ts file to support Vue, TypeScript, and Stylistic:

typescript
import antfu from '@antfu/eslint-config'

export default antfu({
  stylistic: true,
  vue: true,
  typescript: true
})

Now, you can lint your code:

bash
pnpm run lint

And automatically fix issues:

bash
pnpm run lint:fix

Defining our Monorepo

With the linter and formatter set up, we can start defining our monorepo.

We will have the following structure:

txt
.
├── .histoire
│   └── package.json
├── package.json
├── packages
│   └── huchet-vue
│       └── package.json
└── playground
    ├── nuxt
    │   └── package.json
    └── vue
        └── package.json

For now, create the packages folder and the huchet-vue package with its package.json file. Additional packages will be developed in future parts of this series.

bash
mkdir -p packages/huchet-vue
echo '{\n"name": "@barbapapazes/huchet-vue",\n"type": "module",\n"version": "0.0.0"\n}' > packages/huchet-vue/package.json

Having two packages allows us to define them in a pnpm-workspace.yaml file at the root of our monorepo:

bash
echo 'packages:\n  - packages/*' > pnpm-workspace.yaml

This file instructs pnpm where to find the packages in our monorepo. It links the packages together, avoids duplication, shares dependencies, and facilitates installation with a single command.

Even with a monorepo, each package should be treated as a standalone project, having its own package.json, dependencies, scripts, and operations.

Install dependencies from the root of the project:

bash
pnpm install

You'll see that pnpm recognizes 2 packages:

txt
Scope: all 2 workspace projects

In the next article, we will create our first component in our newly established monorepo.

Profil Picture of Estéban

Thanks for reading! My name is Estéban, and I love to write about web development.

I've been coding for several years now, and I'm still learning new things every day. I enjoy sharing my knowledge with others, as I would have appreciated having access to such clear and complete resources when I first started learning programming.

If you have any questions or want to chat, feel free to comment below or reach out to me on Bluesky, X, and LinkedIn.

I hope you enjoyed this article and learned something new. Please consider sharing it with your friends or on social media, and feel free to leave a comment or a reaction below—it would mean a lot to me! If you'd like to support my work, you can sponsor me on GitHub!

Continue readingBuilding a First Component with Vue.js and Tailwind CSS