DEV Community

Cover image for VueJS - Building a basic Monorepo project
Pablo Veiga
Pablo Veiga

Posted on

VueJS - Building a basic Monorepo project

What is Monorepo?

Monorepo is an architectural concept and stands for Mono Repository, which is self-explanatory. When a project requires multiple modules, for example, instead of building them using separated repositories you use only one.

Repositories Types - Monorepo, Single-repo monolith and multiple repos

As Alexander Noel mentions in this great article,

Imagine that, instead of a small app, you need to maintain a huge platform consisting of a lot of functional areas. If you are thinking about architecture, you will want to do two main things: Separate concerns and avoid code dupes.

So, think of Monorepo as an approach to help you deal with multiple modules/projects independently inside the same repository.

Here are some of the advantages of using Monorepo:

  • One place to store all configs and tests;
  • Easily refactor global features with atomic commits;
  • Simplified package publishing;
  • Easier dependency management;
  • Re-use code with shared packages while still keeping them isolated;

And some disadvantages:

  • No way to restrict access only to some parts of the app;
  • Poor Git performance when working on large-scale projects;
  • Higher build time;

You can find more details about each one of these topics in
the same article I've mentioned before.

TL;DR

The goal of this article is to help you to build a monorepo boilerplate to allow you to:

  • manage multiple applications using Lerna;
  • configure test files properly;
  • importing shared components within the application;

Building a Monorepo VueJS Project using Lerna

I assume you already have NodeJS + NPM installed in your computer in order to follow the steps below.

1. Lerna

According to its Github, Lerna is "A tool for managing JavaScript projects with multiple packages." I advise you to read a little bit more about it to check its main features and how to use them.

Let's get our hands dirty.

i) In an empty folder, run npm init to start a new project. Answer each one of the questions ans, after everything is set up, run the following command to install Lerna:

npm install lerna
Enter fullscreen mode Exit fullscreen mode

ii) Create a new folder named modules in the root directory of your project. This is where all of your VueJS applications will be put.

iii) Create e new file named lerna.json in the root folder of your project with the following content.

{
  "packages": [
    "modules/*"
  ],
  "version": "independent"
}
Enter fullscreen mode Exit fullscreen mode

By setting version property as "independent" you will be able to publish packages independently in NPM, for example.

Obs.: Today, we still won't publish anything, but I might cover this topic in a further article, if you guys find it would be useful. :)

2. Install ESLint dependencies and plugins

In the next step, if you choose to create a new VueJS application using a default preset of Vue CLI, it will install ESLint dependencies within the module level, but, as we are dealing with a monorepo, it's good to install them globally in the root directory.

In a terminal window, go to the main folder of the project and run the following command:

npm install --save-dev eslint babel-eslint eslint-plugin-vue
Enter fullscreen mode Exit fullscreen mode

I won't dive deep into the ESLint settings I usually use in my projects, but I intend to write a short article about it to share with you.

After installing the dependencies, close and reopen the workspace for changes to take effect.

3. Create the VueJS modules

Inside the modules folder, create two new VueJS projects using Vue CLI. In a terminal command execute:

vue create shared --packageManager npm && vue create admin --packageManager npm
Enter fullscreen mode Exit fullscreen mode

I'm going to use the default preset [VueJS 2] babel, eslint for both projects and force the CLI to use NPM instead of yarn, but you may select your favorite settings to proceed.

If your shared module is not an executable one, you can remove the files App.vue, main.js and the public folder from it.

4. Create jsconfig.json and workspace files (Skip if you do not use VSCode)

When building multiple applications using monorepo , it is nice to use aliases to import components and functions across modules. So, combining VSCode with Vetur extension, you can create a jsconfig.json file to tell the editor which alias correspond to each path.

All you need to do is place the file in the root directory with the following content:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
    "paths": {
      "@shared/*": ["./modules/shared/src/*"],
      "@admin/*": ["./modules/admin/src/*"]
    }
  },
  "exclude": [
    "node_modules"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Another good practice is to create a workspace file containing global configurations for you editor. When using VSCode, you need to create a file with the .code-workspace extension in the root folder of the project. Other editors will use their own files.

This is how I've configured my workspace file for this monorepo sample project:

{
    "folders": [{
      "path": "."
    }],
    "settings": {
      "editor.formatOnSave": false,
      "editor.tabSize": 2,
      "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
      },
      "typescript.format.enable": false,
      "javascript.format.insertSpaceAfterCommaDelimiter": false,
      "javascript.format.semicolons": "remove",
      "eslint.alwaysShowStatus": true,
      "eslint.options": {
        "extensions": [
          ".html",
          ".js",
          ".vue"
        ]
      },
      "eslint.validate": [
        "vue",
        "html",
        "vue-html"
      ],
      "vetur.validation.script": false,
      "vetur.validation.template": false
    }
  }

Enter fullscreen mode Exit fullscreen mode

5. Using shared components

As you might have guessed, the admin module we've created will import components from the shared module.
To make this possible, we need to customize some configurations in the vue.config.js.

If you've used the default preset when creating the admin module, there won't be any file with this name there, so you need to manually create a vue.config.js in the admin folder.

This content, is enough to make the admin module "see" and import shared components or functions:

const path = require('path')
const adminNodeModules = path.resolve(__dirname, './node_modules')
const sharedNodeModules = path.resolve(__dirname, '../shared/node_modules')

const adminSrc = path.resolve(__dirname, 'src')
const sharedSrc = path.resolve(__dirname, '../shared/src')

module.exports = {
  configureWebpack: {
    resolve: {
      modules: [adminNodeModules, sharedNodeModules], // this will allow `admin` module to import NPM packages from `shared`
      alias: { 
        '@shared': sharedSrc,
        '@admin': adminSrc
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Another thing that can be done is configure your VueJS project to also be able to import NPM packages from other modules.
This means that you may have dependencies installed in a common module and shared with several applications without the need to duplicate them per module.

6. Global NPM commands

Now that your first VueJS + Lerna monorepo structure is ready, you may want to add new commands to the root package.json file, so that you are able to install all packages at once when first loading your project in a new machine.
Tests and linting may also be executed altogether.

In the root package.jsonfile, add the following scripts

{
  "scripts": {
    "postinstall": "lerna bootstrap",
    "admin": "cd ./modules/admin && npm run serve",
    "admin:build": "cd ./modules/admin&& npm run build",
    "admin:test": "cd ./modules/admin&& npm run test",
    "test": "lerna exec -- npm run test",
    "lint": "lerna exec -- npm run lint"
  }
}

Enter fullscreen mode Exit fullscreen mode

The first command will install all of the dependencies of all projects inside modules folder after you run npm run install in the root directory.

All of the other commands are just shortcuts to access specific scripts of the modules or to test and lint all modules with one command.


You can find a fully working example of the code demonstrated here.

I hope you liked it.
Please, share and comment!

See you next week :)

Cover image by @gndclouds

Oldest comments (1)

Collapse
 
tolerious profile image
tolerious

lerna bootstrap is outdated now.