Getting Started with Monorepo, Linter, and Formatter
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:
mkdir huchet-vue && cd huchet-vue && npm init -y
Open this folder in your code editor and modify the package.json
file:
{
"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:
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:
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:
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint --fix ."
}
}
Update the eslint.config.ts
file to support Vue, TypeScript, and Stylistic:
import antfu from '@antfu/eslint-config'
export default antfu({
stylistic: true,
vue: true,
typescript: true
})
Now, you can lint your code:
pnpm run lint
And automatically fix issues:
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:
.
├── .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.
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:
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:
pnpm install
You'll see that pnpm recognizes 2 packages:
Scope: all 2 workspace projects
In the next article, we will create our first component in our newly established monorepo.
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!