• Blog /

  • How to Setup Monorep...

How to Setup Monorepos with Git for JavaScript and TypeScript

A guide to setting up monorepos using Git for Javascript or Typescript. With private repositories, proper versioning, and beautiful import paths

Hieu Nguyen · August 12, 2021

PXE_geYD1J641sg1EE54YoYIm5k
khKWiL_EN

When your app gets bigger, managing files within a project gets more complex. You may start to have modules shared between front-end and back-end projects. Often, you also need to manage different versions of those modules.

A monorepo is a way to structure your projects to manage that kind of complexity all in one place.

I failed to set up a monorepo with Lerna a few times. Yalc and Yarn Workspace can be troublesome when I need to move a project out of the monorepo.

Finally, I found a way to make it work using git submodules. Git is great for resolving code conflicts. Git branches can be used for versioning. You can have unlimited private repositories for free when using Github or Gitlab. Besides, with TypeScript or JavaScript (using webpack), you can configure module aliases to create beautiful import paths.

In this post, I'll show you how to set up a dependency, structure your project, and configure module aliases for a monorepo. *See *git-monorepo-project* on Github for the final result*

1. Setup a dependency

A dependency is a git repository. It can either contain a complete module (i.e with package.json and bundled/transpired JavaScripts files), or it may only have plain JavaScript or Typescript files.

Besides, we often need different versions of the dependency, known as versioning. Which allows us to make changes in a specific version, without affecting projects that use other versions.

Creating a dependency repository

You can create a public or private repository (make sure contributors have access), and push the code there.

Dependency versioning

For versioning, you can use branches. For example, using the main branch for the latest version, stable@v0.0.1 branch for the stable 0.0.1 version, and so on

2. Structure a project

The main idea of setting up a monorepo with git is to add dependencies (in step 1) as submodules.

In the project structure, a submodule is a local directory. Hence, we can easily import and treat them as a local directory. And because it’s a git repository, any committed changes will also apply to the copies in other projects (after pulling the changes)

Project structures

One way to structure your project is to have all dependencies under the src/packages directory. Here’s a project directory tree example:

project-root/
    ├── .gitsubmodules
    ├── package.json
    ├── tsconfig.json
    ├── webpack.config.js
    └── src/
        ├── index.ts
        ├── packages/
        │   ├── module1 (submodule)/ 
        │   │   ├── package.json
        │   │   └── src/
        │   │       └── index.ts
        │   ├── module2 (submodule)/
        │   │   └── index.ts
        │   └── ...
        └── ...

See the git-monorepo-project for example

Add a dependency

After creating a dependency repository, you can add it as a submodule using the git submodule add command, and store it under the src/packages directory. Here is an example:

$ git submodule add https://github.com/username/module-name.git src/packages/module-name

To add a specific version of the dependency, use the --b flag when adding the submodule. For example:

$ git submodule add -b stable@v0.0.1 https://github.com/username/module-name.git src/packages/module-name

Now, you can import the new dependency as a local directory. For example, import Module1 from “../packages/module1”;

Working from another computer

After setting up the monorepo, it’s easy to install a project or a dependency in another computer. It's useful when you have many workstations (ie. PC, laptop), or if you have someone working with you.

To set up the monorepo in another computer:

  1. Clone the main project with the --recursive flag on the new computer. It will download the repository and all the submodules. For example: git clone --recursive https://github.com/username/main-project.git
  2. Install node modules (if needed) using “npm install

Now the project should be ready to work on!

3. Configure module aliases

A common problem when setting up a monorepo as above is that it results in ugly import paths. For example, the import path in the src/pages/dashboard/profile/ProfileMenu.tsx file will be "../../../packages/module1".

Luckily, you can set up module aliases for shorter import paths. Note: if you're using webpack to transpile Typescript, you'll need to set up module aliases for both JavaScript and Typescript.

Configure module aliases for JavaScript

You can configure the module alias for webpack in the webpack.config.js file, using the resolve.alias configuration. For React apps created with CRA, you can use react-app-rewired to override the webpack configurations.

For example:

module.exports = {
    …,
    resolve: {
        alias: {
            // import Module1 from “module1”
            "module1": "path/to/src/packages/module1",

            // this config allow importing any modules 
            // under src/packages directory
            // i.e import Module1 from “packages/module1”
            "packages": "path/to/src/packages",
            ...
        }
    }
}  

See the webpack.config.js file for example

Configure module aliases for Typescript

You can configure module aliases for Typescript in the tsconfig.json file, using the compilerOptions.paths configuration.

For example:

{
    "compilerOptions": {
        …,
        "baseUrl": "./src",
        "paths": {

            // import Module1 from “module1”
            "module1": "packages/module1",
            "module1/*": "packages/module1/*",

            // this config allow importing any modules 
            // under src/packages directory
            // i.e import Module1 from “packages/module1”
            "packages": "packages",
            "packages/*": "packages/*",
            ...
        }
    }
}

Make sure the "baseUrl" (as above) is also present. It helps the compiler resolve dependency paths. See the tsconfig.extends.json file for example


Once you have set up repositories for dependencies, structured your project as above, and configured your module aliases - your monorepo is ready!

Takeaways

It’s often difficult to manage multiple projects and dependencies in a big app. A monorepo is a way to structure them all in a single repository, making it easier to work with.

Git provides submodules, branches, and the ability to manage code conflicts, which is useful for setting up a monorepo.

You can set up monorepo with git by separating each dependency into its own repository, then adding them as submodules. Besides, we get to configure module aliases to attain nice and readable import paths.

Thanks to Carl Poppa for proofreading and feedback.

logo

© Inverr 2021

By using our websites or services, you've acknowledged our privacy policy and agreed with our terms of use

  • Products

  • Plans & Pricing

  • Integrations

  • Database

  • Templates

  • Resources

  • Get started

  • Custom domain

  • Custom fonts

  • Documentation

  • CMS

  • Get familiar

  • Query Builder

  • Collections

  • Collect data