<palign="center"><strong>Monorepo concepts, tips and tricks oriented around NextJs</strong></p>
</div>
> Howtos for monorepo. New to monorepos ? [check this FAQ](./README.md#monorepo). This example is managed by [Yarn 3.2+](https://dev.to/arcanis/yarn-32-libc-yarn-explain-next-major--o22)
> / [typescript path aliases](https://www.typescriptlang.org/tsconfig#paths). Not the only way to do.
Useful to
- Establish a **structure** and present a lifecycle perspective (dx, ci/cd, deployments...)
- How to create and consume **shared packages**, locales, assets, api types...
- Clarify some **advantages** of monorepos (team cohesion, consistency, duplication, refactorings, atomic commits...).
- Create nextjs/vercel/prisma... bug reports with **reproducible examples**_(initial goal of this repo)_.
## Sponsors :heart:
If you are enjoying some this guide in your company, I'd really appreciate a [sponsorship](https://github.com/sponsors/teable-group), a [coffee](https://ko-fi.com/teable-group) or a dropped star.
That gives me some more time to improve it to the next level.
## Structure
[![Open in Gitpod](https://img.shields.io/badge/Open%20In-Gitpod.io-%231966D2?style=for-the-badge&logo=gitpod)](https://gitpod.io/#https://github.com/teable-group/teable)
- [packages/db-main-prisma](./packages/db-main-prisma): used by web-app. [README](./packages/db-main-prisma/README.md) | [CHANGELOG](./packages/db-main-prisma/CHANGELOG.md)
> PS: If your shared package make use of scss bundler... A custom webpack configuration will be necessary
> or use [next-transpile-modules](https://github.com/martpie/next-transpile-modules), see FAQ below.
#### Step 3.4: Using the package
The packages are now linked to your app, just import them like regular packages: `import { poney } from '@teable-group/magnificent-poney'`.
### 4. Publishing
> Optional
If you need to share some packages outside of the monorepo, you can publish them to npm or private repositories.
An example based on microbundle is present in each package. Versioning and publishing can be done with [atlassian/changeset](https://github.com/atlassian/changesets),
and it's simple as typing:
```bash
$ yarn g:changeset
```
Follow the instructions... and commit the changeset file. A "Version Packages" P/R will appear after CI checks.
When merging it, a [github action](./.github/workflows/release-or-version-pr.yml) will publish the packages
with resulting semver version and generate CHANGELOGS for you.
> PS:
>
> - Even if you don't need to publish, changeset can maintain an automated changelog for your apps. Nice !
> - To disable automatic publishing of some packages, just set `"private": "true"` in their package.json.
> - Want to tune the behaviour, see [.changeset/config.json](./.changeset/config.json).
## 4. Monorepo essentials
### Monorepo scripts
Some convenience scripts can be run in any folder of this repo and will call their counterparts defined in packages and apps.
| `yarn deps:check --dep dev` | Will print what packages can be upgraded globally (see also [.ncurc.yml](https://github.com/sortlist/packages/blob/main/.ncurc.yml)) |
You'll find some example workflows for github action in [.github/workflows](./.github/workflows).
By default, they will ensure that
- You don't have package duplicates.
- You don't have typecheck errors.
- You don't have linter / code-style errors.
- Your test suite is successful.
- Your apps (nextjs) or packages can be successfully built.
- Basic check-size example in nextjs-app.
Each of those steps can be opted-out.
To ensure decent performance, those features are present in the example actions:
- **Caching** of packages (node_modules...) - install around 25s
- **Caching** of nextjs previous build - built around 20s
- **Triggered when changed** using [actions paths](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths), ie:
> ```
> paths:
> - "apps/nextjs-app/**"
> - "packages/**"
> - "package.json"
> - "tsconfig.base.json"
> - "yarn.lock"
> - ".yarnrc.yml"
> - ".github/workflows/**"
> - ".eslintrc.base.json"
> - ".eslintignore"
> ```
## 6. Editor support
### 6.1 VSCode
The ESLint plugin requires that the `eslint.workingDirectories` setting is set:
```
"eslint.workingDirectories": [
{
"pattern": "./apps/*/"
},
{
"pattern": "./packages/*/"
}
],
```
More info [here](https://github.com/microsoft/vscode-eslint#mono-repository-setup)
## 7. Deploy
### Vercel
Vercel support natively monorepos, see the [vercel-monorepo-deploy](./docs/deploy/deploy-vercel.md) document.
### Docker
There's a basic example for building a docker image, read the [docker doc](./docs/docker/docker.md).
### Others
Netlify, aws-amplify, k8s-docker, serverless-nextjs recipes might be added in the future. PR's welcome too.
## FAQ
### Monorepo
#### Benefits
- [x]**Ease of code reuse.** You can easily extract shared libraries (like api, shared ui, locales, images...) and use them across apps without
the need of handling them in separate git repos (removing the need to publish, version, test separately...). This limit the tendency to create code duplication
amongst developers when time is short.
- [x]**Atomic commits.** When projects that work together are contained in separate repositories, releases need to sync which versions of one project work
with the other. In monorepo CI, sandboxes and releases are much easier to reason about (ie: [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)...).
A pull-request contains all changes at once, no need to coordinate multiple packages versions to test it integrally (multiple published canary versions...).
- [x]**Code refactoring.** Changes made on a library will immediately propagate to all consuming apps / packages.
Typescript / typechecks, tests, ci, sandboxes... will improve the confidence to make a change _(or the right one thanks to improved discoverability of
possible side effects)_. It also limits the tendency to create tech debt as it invites the dev to refactor all the code that depends on a change.
- [x]**Collaboration across teams**. Consistency, linters, discoverability, duplication... helps to maintain
cohesion and collaboration across teams.
#### Drawbacks
- [x]**Increased build time**. Generally a concern but not relevant in this context thanks to the combination of
nextjs/webpack5, typescript path aliases and yarn. Deps does
not need to be build... modified files are included as needed and properly cached (nextjs webpack5, ci, deploy, docker/buildkit...).
- [x]**Versioning and publishing**. Sometimes a concern when you want to use the shared libraries outside of the monorepo.
See the notes about [atlassian changeset](https://github.com/atlassian/changesets). Not relevant here.
- [x]**Git repo size**. All packages and apps and history will fit in the same git repository increasing its size and
checkout time. Generally when you reach size problems, check for assets like images first and extract
packages that don't churn anymore.
- [x]**Multi-languages**. Setting up a monorepo containing code in multiple languages (php, ruby, java, node) is extremely
difficult to handle due to nonexistence of mature tooling (bazel...).The general idea is
to create a monorepo with the same stack (node, typescript...) and managed by the same
package manager (yarn, pnpm,...)
#### Exact vs semver dependencies
Apps dependencies and devDependencies are pinned to exact versions. Packages deps will use semver compatible ones.
For more info about this change see [reasoning here](https://docs.renovatebot.com/dependency-pinning/) and our
To help keeping deps up-to-date, see the `yarn deps:check && yarn deps:update` scripts and / or use the [renovatebot](https://github.com/marketplace/renovate).
> When adding a dep through yarn cli (i.e.: yarn add something), it's possible to set the save-exact behaviour automatically
> by setting `defaultSemverRangePrefix: ""` in [yarnrc.yml](./.yarnrc.yml). But this would make the default for packages/\* as well.
> Better to handle `yarn add something --exact` on per-case basis.