DEV Community

Cover image for 👥 Reproducible Nx Workspace with HugeNx’s Conventions
jogelin
jogelin

Posted on • Originally published at javascript.plainenglish.io

👥 Reproducible Nx Workspace with HugeNx’s Conventions

After migrating multiple organizations to an Nx Monorepo and maintaining it, I discovered that with each major Nx version, I was generating a new repository on the side, mimicking my existing repo, to compare configuration files and ensure they matched the latest version of Nx.

However, recreating an entire repo to mirror my original was labor-intensive. I began using Bash scripts, then Node.js scripts, and finally, I developed HugeNx, a custom Nx preset that generates a workspace from a configuration file.

After further reflection, I realized I wasn’t just creating a file to generate a workspace; I was crafting a file that would describe workspace conventions and could be utilized for many other purposes.

Huge

CI

HugeNx is a toolkit designed to dynamically generate and manage Nx workspaces by adhering to established workspace conventions.

Article

Getting Started

1. Define your HugeNx's conventions:

For example let's create a conventions file angular-monorepo.conventions.ts that match the default Nx angular-monorepo preset:

export default {
  version: '1.0',
  generators: {
    '@nx/angular:application': {
      bundler: 'esbuild',
    },
    '@nx/angular:library': {
      linter: 'eslint',
      unitTestRunner: 'jest',
    },
  },
  projectTypes: {
    'global:angular:application': {
      projectPattern: '*-app',
      generators: [{ generator: '@nx/angular:application' }],
    },
    'global:angular:lib:feature': {
      projectPattern: '*-feature',
      generators: [{ generator: '@nx/angular:library' }],
    },
  },
  workspace: {
    apps: {
      'my-app': 'global:angular:application',
    },
    libs: {
      'my-feature': 'global:angular:lib:feature',
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

2. Use create-huge-nx client to generate

…

HugeNx’s Conventions

The main concept behind this library is the HugeNx’s Conventions file — a configuration file that groups all conventional decisions you’ve made about your Nx workspace. This file will describe how your workspace should look.

Image description

If HugeNx’s Conventions file contains all the information on your targeted workspace, it means you can generate a new workspace from scratch or even maintain an existing one.

ProjectTypes

The first main convention I wanted to integrate is the concept of Nx ProjectType.

When you delve into the various resources about structuring an Nx workspace, you’ll encounter extensive explanations on categorizing your library by scope or type and creating tags that establish your boundaries:

However, I always missed a centralized way to specify this list of ProjectTypes. When you generate a project you lose the link with its source generator and its related technologies.

Image description

This is why I wanted to keep that information. With the help of HugeNx’s Conventions, you can recognize your projects because they will follow the conventions you specified in them.

I already explain the importance of conventions in my article ⚡ The Super Power of Conventions with Nx.

Reproducible Generation

When you start a project with Nx, everything is clean and aligned. But after some years we apply migrations, create custom generators, manually modify configurations, etc. All of that, coupled with developer turnover, and you end up with a good Ratatouille.

This is a common challenge we try to solve in IT, especially at the infrastructure level with the concept of infrastructure as code. The usage of tools like Ansible allows us to initialize and reconfigure an entire infrastructure from scripts and configuration files.

With Nx, you can use the list of presets, but it is hardcoded. There is some flexibility for each with some options, but not enough to generate a more advanced workspace.

If you want to create a more advanced workspace, you’ll need to create your custom preset. It is useful for creating a seed but not for quickly generating a workspace for comparison, demo, or workshop; it can be cumbersome.

This is why I decided to create a custom Nx preset that can generate an Nx workspace just by using HugeNx’s Conventions. You don’t need to create or maintain your own preset!

Image description

You can regenerate your entire repository for any Nx version. This would help for comparing what changed and also for simplifying the way we currently create Nx presets.

HugeNx supports all Nx plugins so you can easily reproduce all Nx presets and even create your custom preset.

Consistent Monorepo

Generating your repository from scratch is good, but being able to maintain it for a long time is even better, right?

The main goal of HugeNx’s Conventions is to be the guardian, the housekeeper that will describe how your workspace should look and behave.

Image description

Eslint Conventions Rules:

With the help of tools like Eslint, you can read that file and create rules to enforce conventions and:

  • Validate that each project follows the naming conventions

  • Validate the workspace structure

  • Validate that each project is correctly related to one ProjectType

  • Validate the nx.json generator’s options

Project Discovering:

With the project inference provided by the Nx Project Crystal, you can easily discover your Nx project based on your naming convention.

You can also create one Nx plugin that matches the ProjectType naming convention and attach the project configuration automatically!

Migration:

Related to the fact that you can regenerate a new workspace from scratch for a specific Nx version, you can now easily generate a workspace with the latest Nx and compare it with your workspace.

huge-nx - snappify.com

A powerful design tool to animate your code snippets in various ways.

favicon snappify.com

You can also use tools like Betterer if you want to migrate step by step your repository to your HugeNx’s Conventions.

ProjectType Generator:

There is no need to create and maintain complex custom generators. You can create a generator that will read your ProjectTypes and generate a project from it.

Stay tuned for future implementations of the HugeNx tools for consistent monorepo.

Let’s Generate Your Workspace

Let’s start with a concrete example by creating a new TypeScript file that will represent your workspace.

1. Define your conventions

You can create a file huge-angular-full-stack.conventions.ts that will contain a workspace with a full-stack application to manage a Hotel:



export default {
  version: '1.0',
  generators: {
    '@nx/angular:application': { //<-- Generator Identifier
      linter: 'eslint', //<-- List of options
      style: 'css',
      unitTestRunner: 'jest',
      bundler: 'esbuild',
      e2eTestRunner: 'playwright',
      inlineStyle: true,
      inlineTemplate: true,
    },
    '@nx/angular:library': {
      linter: 'eslint',
      unitTestRunner: 'jest',
    },
    '@nx/angular:component': {
      style: 'css',
    },
    '@nx/js:lib': {
      bundler: 'swc',
    },
  },
  projectTypes: {
    'global:angular:app': { //<-- ProjectType Identifier
      projectPattern: '*-app', //<-- Pattern matching your naming convention
      generators: [{ generator: '@nx/angular:application' }], //<-- List of generators used to generate that type of project
    },
    'backend:api': {
      projectPattern: '*-api',
      generators: [{ generator: '@nx/nest:application' }],
    },
    'global:angular:lib:data-access': {
      projectPattern: '*-data-access',
      generators: [{ generator: '@nx/angular:library' }],
    },
    'global:angular:lib:feature': {
      projectPattern: '*-feature',
      generators: [{ generator: '@nx/angular:library' }],
    },
    'global:angular:lib:ui:storybook': { //<-- This ProjectType generates a library then a storybook configuration
      projectPattern: '*-ui',
      generators: [{ generator: '@nx/angular:library' }, { generator: '@nx/storybook:configuration', options: { uiFramework: '@storybook/angular' } }],
    },
    'global:ts:lib:utils': {
      projectPattern: '*-utils',
      generators: [{ generator: '@nx/js:lib', options: { bundler: 'swc' } }],
    },
  },
  workspace: { //<-- The workspace is structured by folders and projects
    apps: {
      //<-- Generates a folder apps
      'hotel-app': 'global:angular:app', //<-- Generates a project hotel-app by using the project type global:angular:app
      'hotel-api': { //<-- Generates a project hotel-api by using the project type backend:api and extra options
        projectType: 'backend:api',
        options: {
          '@nx/angular:remote': { frontendProject: 'hotel-app' },
        },
      },
    },
    libs: { //<-- Generates a folder libs
      guest: { //<-- Generates a folder guest
        'data-access': 'global:angular:lib:data-access', //<-- Generates a project guest-data-access by using the project type global:angular:lib:data-access
        'booking-feature': 'global:angular:lib:feature', //<-- Generates a project guest-booking-feature by using the project type global:angular:lib:feature
        'feedback-feature': 'global:angular:lib:feature', //<-- Generates a project guest-feedback-feature by using the project type global:angular:lib:feature
      },
      room: { //<-- Generates a folder room
        'data-access': 'global:angular:lib:data-access',
        'list-feature': 'global:angular:lib:feature',
        'request-feature': 'global:angular:lib:feature',
      },
      shared: { //<-- Generates a folder shared
        ui: { //<-- Generates a project shared-ui by using the project type global:angular:lib:ui:storybook and extra options
          projectType: 'global:angular:lib:ui:storybook',
          options: {
            '@nx/storybook:configuration': { project: 'shared-ui' },
          },
        },
        utils: 'global:ts:lib:utils',
      },
    },
  }
};


Enter fullscreen mode Exit fullscreen mode

The Default Generator Options
This is nothing new and is already available in Nx by configuring your nx.json file. You can define default options for each generator that you are using in your workspace.

All Nx options can be found in the Nx API Documentation.

The List of ProjectTypes
Here you’ll define your list of ProjectType based on the technologies, the domain, the type of library, the team, etc.

For each ProjectType, you’ll specify which generators should be used and all conventions around them. It will use the Default Generator Options, and you can add extra options if needed.

Your Workspace Structure

Finally, you’ll define your list of projects inside a workspace layout. Each project will be linked and described by a specific ProjectType.

That section is required for the generation but not required for the maintenance.

2. Use create-huge-nx CLI

If you want to generate your workspace, you can now use the HugeNx CLI by calling:



npx create-huge-nx@latest my-workspace --hugeNxConventions=./huge-angular-full-stack.conventions.ts --nxCloud skip


Enter fullscreen mode Exit fullscreen mode

It will generate a workspace that will look like:



my-workspace/
├─ apps/
│   ├─ hotel-api/
│   ├─ hotel-api-e2e/
│   ├─ hotel-app/
│   └─ hotal-app-e2e/
├── libs/
│   ├─ guest/
│   │   ├─ data-access
│   │   ├─ booking-feature
│   │   └─ feedback-feature
│   ├─ room/
│   │   ├─ data-access
│   │   ├─ list-feature
│   │   └─ request-feature
│   └─ shared/
│       ├─ ui
│       └─ utils
├─ nx.json
├─ package.json
├─ jest.config.json
└─ huge-nx.conventions.ts


Enter fullscreen mode Exit fullscreen mode

By default, the latest version of Nx will be used but you can generate a workspace with a specific Nx version with --nxVersion:



npx create-huge-nx@latest my-workspace --nxVersion 17 --hugeNxConventions=./huge-angular-full-stack.conventions.ts --nxCloud skip


Enter fullscreen mode Exit fullscreen mode

More presets

It’s now straightforward to create various types of repositories simply by introducing a new huge-nx.conventions.ts file. This approach not only encompasses all Nx presets but also allows you to describe each type of project in detail, as outlined in the library types section of the Nx documentation.

For instance, you can define the types from the @angular-architects/ddd package and then use this definition to generate a workspace. This flexibility allows for a highly customized setup that caters to the specific needs of your project, leveraging Nx's powerful and extensible tooling ecosystem.

I also used ChatGPT to generate my convention files. I just provided an example of the file and specify:

  • It represents an Nx workspace

  • Should use Angular generators

  • Represent Hotel Business

  • Should be a full-stack app

Final Thoughts

HugeNx’s Conventions aim to provide a useful tool in the Nx landscape, particularly for those managing large and evolving monorepos.

By automating and standardizing workspace setups, it helps reduce the complexity often associated with project generation and maintenance. For me, this approach helped me save time and effort in my migration process.

I am looking forward to improving HugeNx based on your feedback. I hope it will be a valuable addition to your development toolkit. Your insights and experiences are crucial to refining this tool, so please share your thoughts.

Stay tuned! 🚀

Huge

CI

HugeNx is a toolkit designed to dynamically generate and manage Nx workspaces by adhering to established workspace conventions.

Article

Getting Started

1. Define your HugeNx's conventions:

For example let's create a conventions file angular-monorepo.conventions.ts that match the default Nx angular-monorepo preset:

export default {
  version: '1.0',
  generators: {
    '@nx/angular:application': {
      bundler: 'esbuild',
    },
    '@nx/angular:library': {
      linter: 'eslint',
      unitTestRunner: 'jest',
    },
  },
  projectTypes: {
    'global:angular:application': {
      projectPattern: '*-app',
      generators: [{ generator: '@nx/angular:application' }],
    },
    'global:angular:lib:feature': {
      projectPattern: '*-feature',
      generators: [{ generator: '@nx/angular:library' }],
    },
  },
  workspace: {
    apps: {
      'my-app': 'global:angular:application',
    },
    libs: {
      'my-feature': 'global:angular:lib:feature',
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

2. Use create-huge-nx client to generate

…

Looking for some help? 🤝
**Connect with me on Twitter • LinkedIn • Github

Top comments (0)