Git Product home page Git Product logo

nextjs-monorepo-example's Introduction

Monorepo concepts, tips and tricks oriented around NextJs

build Codefactor Maintainability Techdebt Codacy grade LoC TS Licence

WARNING This document covers the most recent version based on Yarn 3.0 and NextJs 10.2+. Docs and examples are still WIP.

Useful to

  • Establish a structure and show a lifecycle perspective (dx, ci/cd...)
  • How to create shared packages, shared locales, assets, images folders, api types...
  • Integrate tools & configs (ts, jest, changelogs, versioning...).
  • Clarify some advantages of monorepos (team cohesion, consistency, duplication...).
  • Create nextjs/vercel/prisma/webpack5... bug reports with reproducible examples (initial goal of this repo).

The approach doesn't rely on monorepo tools such as Rush or Nx. It does not try to compete, accent is on recipes with a focus on workspace enabled package managers like yarn 3.0, pnpm, npm v7... By keeping the examples as agnostic as possible, it should be very easy to apply them in others tools. Code is shared through typescript aliases (no build necessary), topology and dependency graph handled by the package manager, caches by NextJs. See also the FAQ about differences.

Open in Gitpod

FOSSA Status

1. Structure

All in typescript, latest nextjs 10.2+, webpack5, yarn v3, ts-jest, prettier, eslint, emotion, tailwind, prisma 2... add as much as you like.

Two apps

Some shared code

Static shared assets

If needed static resources like locales, images,... can be shared by using symlinks in the repo.

  • See the global static folder.

Folder overview

.
├── apps
│   ├── blog-app                 (NextJS SSG app)
│   │   ├── public/
│   │   │   └── shared-assets/   (symlink to global static/assets)
│   │   ├── src/
│   │   ├── CHANGELOG.md         (autogenerated with changesets)
│   │   ├── jest.config.js
│   │   ├── next.config.js
│   │   ├── package.json         (define package workspace:package deps)
│   │   └── tsconfig.json        (define path to packages)
│   │
│   └── web-app                  (NextJS app with api-routes)
│       ├── public/
│       │   ├── shared-assets/   (possible symlink to global assets)
│       │   └── shared-locales/  (possible symlink to global locales)
│       ├── src/
│       │   └── pages/api        (api routes)
│       ├── CHANGELOG.md
│       ├── jest.config.js
│       ├── next.config.js
│       ├── package.json         (define package workspace:package deps)
│       └── tsconfig.json        (define path to packages)
│
├── packages
│   ├── core-lib                 (basic ts libs)
│   │   ├── src/
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── main-db-prisma          (basic db layer with prisma)
│   │   ├── prisma/
│   │   ├── src/
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   └── ui-lib                  (basic design-system in react)
│       ├── src/
│       ├── CHANGELOG.md
│       ├── package.json
│       └── tsconfig.json
│
├── static                       (no code: images, json, locales,...)
│   ├── assets
│   └── locales
├── .yarnrc.yml
├── docker-compose.yml           (database service for now)
├── package.json                 (the workspace config)
└── tsconfig.base.json           (base typescript config)

2. Howtos ?

2.1 How create a new shared package ?

  1. Workspace config lives in the root package.json, see workspace section. there's already 2 roots defined: ./packages/_ and ./apps/_. So nothing to do.

  2. Create a new folder, i.e: mkdir packages/magnificent-poney.

  3. Initialize a package.json, set a name and dependencies you'll need. For inspiration, take the ui-lib as an example. Copy/paste other files you might need (tsconfig.json...). Place sources in the magnificent-poney/src folder.

  4. To use it in an app first declare the dependency in its package.json deps by adding "@your-org/magnificent-poney": "workspace:*". Inspiration in web-app/package.json.

  5. Run yarn install to update the workspace and create symlinks.

  6. Add tsconfig paths in the app tsconfig.json, take an example in web-app/tsconfig.json

    {
       "compilerOptions": {
         "baseUrl": "./src",
         "paths": {
           // regular app aliases
           "@/components/*": ["./components/*"],
           // packages aliases, relative to app_directory/baseUrl
           "@your-org/magnificent-poney/*": ["../../../packages/magnificent-poney/src/*"],
           "@your-org/magnificent-poney": ["../../../packages/magnificent-poney/src/index"]
         },
    }

    PS: The packages aliases should be declared per app (not in the tsconfig.base.json), so to keep being explicit with the dependencies.

  7. Be sure your next.config.js app overrides webpack like in nextjs.config.js:

    webpack: function(config, { defaultLoaders }) {
       // Will allow transpilation of shared packages through tsonfig paths
       // @link https://github.com/vercel/next.js/pull/13542
       const resolvedBaseUrl = path.resolve(config.context, '../../');
       config.module.rules = [
         ...config.module.rules,
         {
           test: /\.(tsx|ts|js|jsx|json)$/,
           include: [resolvedBaseUrl],
           use: defaultLoaders.babel,
           exclude: (excludePath) => {
             return /node_modules/.test(excludePath);
           },
         },
       ];
       return config;
     }

    PS:

  8. Using the package in your app

    The packages are now linked to your app, just import them like regular packages: import { poney } from '@your-org/magnificent-poney'.

  9. Optional package publishing.

    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, and it's simple as typing:

    $ yarn 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 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.

3. Monorepo essentials

3.1 Monorepo scripts

Some convenience global scripts are defined in the root package.json, they generally call their counterparts defined in packages and apps.

{
  scripts: {
    // Global workspaces commands, ie:
    clean: 'yarn workspaces foreach -ptv run clean',
    typecheck: 'yarn workspaces foreach -ptv run typecheck',
    lint: 'yarn workspaces foreach -ptv run lint',
    test: "run-s 'test:*'",
    'test:unit': 'yarn workspaces foreach -ptv run test:unit',
    // Manage versions and releases with atlassion/changesets
    changeset: 'changeset',
    release: 'yarn build && changeset publish',
    // Utility script to check/upgrade deps across the entire monorepo
    'deps:check': 'npm-check-updates --deep --dep prod,dev,optional',
    'deps:update': 'npm-check-updates -u --deep --dep prod,dev,optional',
    // Some extras you can set based on needs.
    'apps:build': "yarn workspaces foreach -ptv --include '*-app' run build",
    'packages:build': "yarn workspaces foreach -ptv --include '@your-org/*' run build",
  },
}

PS:

  • Convention: whatever the script name (ie: test:unit), keeps it consistent over root commands, packages and apps.
  • The use of yarn workspaces commands can be replicated in pnpm, nmp7+lerna...

3.2 Maintaining deps updated

The commands yarn deps:check and yarn deps:updates will help to maintain the same versions across the entire monorepo.

4. Quality

4.1 Linters

An example of base eslint configuration can be found in ./.eslint.base.json, apps and packages extends it in their own root folder, as an example see ./apps/web-app/.eslintrc.json. Prettier is included in eslint configuration as well as eslint-config-next for nextjs apps.

4.2 Hooks / Lint-staged

Check the .husky folder content to see what hooks are enabled. Lint-staged is used to guarantee that lint and prettier are applied automatically on commit and/or pushes.

4.2 Tests

Tests relies on ts-jest with support for typescript path aliases. React-testing-library is enabled whenever react is involved. Configuration lives in the root folder of each apps/packages. As an example see ./apps/web-app/jest.config.js.

4.3 CI

You'll find some example workflows for github action in .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.

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, ie:

     paths:
       - "apps/blog-app/**"
       - "packages/**"
       - "package.json"
       - "tsconfig.base.json"
       - "yarn.lock"
       - ".yarnrc.yml"
       - ".github/workflows/**"
       - ".eslintrc.base.json"
       - ".eslintignore"
    

8. Deploy

Vercel

Vercel support natively monorepos, see the vercel-monorepo-deploy document.

Others

Netlify, aws-amplify, k8s-docker, serverless-nextjs recipes might be added in the future. PR's welcome too.

FAQ

Quid next-transpile-modules ?

And why this repo example doesn't use it to support package sharing.

next-transpile-modules is one of the most installed packages for nextjs. It basically allows you to transpile some 3rd party packages present in your node_modules folder. This can be helpful for transpiling packages for legacy browser support (ie11), esm packages (till it lands in nextjs) and handle shared packages.

In this repo, we use next-transpile-modules only for ie11 and esm. The monorepo management is done through tsconfig path. It will work best when external tooling is involved (ts-jest...), but comes with some limitations if your shared package use an scss compiler for example. Note that future version of NextJs might improve monorepo support through experimental.externalDir option.

See here a quick comparison:

Support matrix tsconfig paths next-transpile-module
Typescript
Javascript
NextJs Fast refresh
CSS custom webpack cfg
SCSS custom webpack cfg
CSS-in-JS
ts-jest custom aliases
Vercel monorepo
Yarn 2 PNP
Webpack5
Publishable (npm) ❌ (ntm relies on "main")

nextjs-monorepo-example's People

Contributors

belgattitude avatar github-actions[bot] avatar fossabot avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.