Can a monorepo be organized into multiple levels? (Question regarding tooling and structure)

Currently in the process of researching how to transition my repositories into a monorepo and further segment the codebase by early 2023.

  • I am utilizing TypeScript
  • I have multiple servers that I plan to break down into microservices for easier development
  • I also have several React frontends that I intend to refactor into a separate set

The envisioned final structure is as follows:

/project-root
  /desktop        # electron apps
  /documentation
  /examples
  /lib            # commonly used libraries
  /mobile         # ionic apps
  /server         # node/express
    /lib          # server code libraries
      /validation
      /...
    /gateway
    /api          # public API
    /ms-xxx       # microservices (Docker containers)
    /ms-yyy
    /...
  /web            # react apps
    /lib          # frontend code libraries
      /uilib
      /...
    /webapp-1
    /webapp-2
    /...
  /website        # code for product website, potentially a CMS

After exploring various tools, these criteria appear feasible:

  • Package manager: pnpm with workspaces
  • Build tool: Vite
  • Regarding Monorepo/packaging options such as Nx and Turborepo/Turbopack, while they seem limiting, I may not be fully informed on their capabilities yet.

I aim to implement multi-level tsconfig and package.json files, particularly under server and web directories, providing a consistent environment for servers and web applications.

  • I encountered issues with @nrwl tooling versions using Nx, causing dependency conflicts
  • Turbo boilerplate leans towards Next.js, whereas Nx requires extensive customization. Without these, hardcoding packages becomes necessary
  • pnpm's handling of peer dependencies remains unresolved currently

Despite some basic examples, larger code bases do not seem prevalent with these tools, prompting the question: Is this approach viable, or am I misunderstanding something?

Answer №1

Enhancing tsconfig files

I am interested in implementing multi-level tsconfig configurations...

To achieve this, utilize the extends feature in your configuration files (for example, create a base tsconfig file that can be extended and tailored by all of your projects, refer to the documentation for an example).

Preventing duplication of dependencies

I aim to have servers and web applications share identical dependencies (such as the same versions of node/express/react).

Although it is not possible for one package.json to inherit from another like with tsconfig files, you can ensure consistent version numbers across all package.json files for your various projects. This will prompt package managers like npm to install each dependency only once in a primary node_modules directory (refer to the npm install --install-strategy flag in the documentation).

Incremental builds for TypeScript projects

You may find value in exploring TypeScript Project References, which simplify specifying project dependencies and facilitate incremental builds.

Answer №2

When embarking on a new monorepo project, my initial toolkit included Lerna and Yarn (classic) Workspace. While this setup wasn't the fastest, it got the job done. However, nowadays, my preference leans towards pnpm and leveraging the workspaces: protocol. In alignment with Wesley's earlier response, I recommend opting for Lerna-Lite over Lerna. Both Lerna and Lerna-Lite offer seamless configuration and powerful version and publish commands with the option of Conventional Commits, simplifying the publishing process and ensuring automatically updated changelogs throughout the project.

Instances when choosing Lerna-Lite would be beneficial:

  • Lerna/Lerna-Lite excels in streamlined versioning and publishing processes with optional Conventional Commits support. Alternatives like Changesets may necessitate more intricate setups and lack conventional changelog structure.
  • Lerna-Lite offers a more modular approach compared to the comprehensive features of Lerna. With fewer mandatory dependencies and only essential commands, Lerna-Lite stands out as a flexible choice.
  • Avoiding Nx integration is possible with Lerna-Lite, unlike recent versions of Lerna that incorporate Nx internally. This distinction ensures more control over your toolchain.
  • Lerna-Lite seamlessly integrates pnpm's workspace: and run functionalities, enhancing compatibility and performance within the workspace environment.
  • Even Jest has made the switch to Lerna-Lite for its simplicity and efficiency in managing projects using Yarn Berry workspace: protocol, emphasizing the strengths of version and publish commands.
  • Add-ons like TurboRepo or utilizing pnpm run can extend functionality based on individual project requirements.
  • An opt-in feature within Lerna-Lite allows automatic updates to peer dependencies, offering convenience not yet available in standard Lerna installations.

Situations where the original Lerna might be preferable:

  • For existing Nx users, leveraging Lerna is recommended due to its integrated support for Nx, facilitating a seamless workflow between tools maintained by Nrwl.
  • Lerna allows additional tool integrations like TurboRepo, albeit potentially including redundant dependencies in the process.
  • The widespread adoption and extensive user base make Lerna a solid choice, ideal for projects requiring stability and community support.

Summary

In conclusion, when working on a monorepo project, my go-to combination comprises pnpm workspaces and Lerna-Lite. These tools offer straightforward setup processes without overwhelming dev dependencies. While Lerna-Lite provides the optional lerna run, I typically favor pnpm run for its simplicity. For enhanced command performance, considering add-ons like TurboRepo or Nx can optimize project workflows.

Alternatively, directly utilizing pnpm for version publication—a strategy adopted by projects like Vite and Vue—may introduce additional complexity but remains feasible with minimal external dependencies, showcasing diverse approaches tailored to project needs.

Answer №3

If you're dealing with a similar structure, consider checking out Lerna for assistance.

In my experience, I lean towards maintaining separate and self-contained packages. Utilizing Yarn V1 Workspaces to create a monorepo can sometimes introduce unnecessary complications, especially when dealing with conflicts between nested packages like eslint or jest.

When evaluating your own structure, it may be beneficial to break things down and handle each package individually. For shared libraries, aim to keep them as general as possible so that multiple packages can easily utilize them as dependencies.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unlock the Power of Typescript: Using the Browser Console to Access Functions

Scenario Within the file ts/app.ts, the following function exists: function foo() { console.log('Hello Word'); } After successful compilation with Webpack, it generates a file named bundle.js. To load this file, use the following script tag ...

Apply a CSS class once a TypeScript function evaluates to true

Is it possible to automatically apply a specific CSS class to my Div based on the return value of a function? <div class="{{doubleClick === true ? 'cell-select' : 'cell-deselect'}}"></div> The doubleClick function will ret ...

Having trouble importing the module in NestJS with Swagger?

Currently, I am in the process of developing a boilerplate NestJS application. My goal is to integrate @nestjs/swagger into the project. However, I have encountered an import error while trying to include the module. npm install --save @nestjs/<a href=" ...

When working with Typescript, it's important to handle errors properly. One common error you might encounter is: Error:(54, 33) TS2686: 'fabric' refers to a UMD global, but the current file is a module

Encountering an Issue: import {Canvas} from "fabric"; Error:(54, 33) TS2686:'fabric' refers to a UMD global, but the current file is a module. Consider adding an import instead. In my Angular project with TypeScript, I am using fabric which ...

Error in AngularFire2 typings: The property 'take' is not present in the type 'FirebaseObjectObservable<any>'

Recently, I upgraded my ionic app from beta 11 to rc0, which also involved transitioning from typescript 1.8 to version 2. After following the configuration steps for AngularFire2 on the site Getting Started with Ionic 2 RC0, Firebase 3 + AngularFire 2, I ...

Error: No 'map' property found in 'TwitterserviceService' type

Currently, I am in the process of learning how to develop simple and basic Angular applications. As part of my learning journey, I decided to incorporate my Twitter timeline into one of my projects. To aid me in this endeavor, I referred to various online ...

Sharing information between different pages in NEXT.js version 14

After performing a fetch and receiving a successful response containing data as an object, I use router.push to redirect the page to another one where I want to display the fetched data. const handleSubmit = async (event: any) => { event.preventDefa ...

Receiving sorted data from Material Table in Angular 6

Is it possible to retrieve the sorted data from a material table once the sorting has been applied? I noticed that there is a "filteredData" property in the table's datasource, but there doesn't seem to be a "sortedData" equivalent. My goal is to ...

Encountered an issue while setting up an object variable: Error - **'const' declarations must be initialized.**

When attempting to initialize an object and assign a value to it, I encountered an error while declaring the object. Error: 'const' declarations must be initialized. How can I properly initialize my object? Code: // Object export type DateRan ...

NPM is lacking in providing sufficient guidance on resolving dependency problems

While attempting to incorporate Typescript into my Gatsby project after the fact, I encountered a cryptic error from NPM: npm ERR! code EOVERRIDE npm ERR! Override for @types/react@* conflicts with direct dependency npm ERR! A complete log of this run can ...

Proper method for determining return type through the use of `infer`

I need to find out the return type based on input values, like in the code below: type ReturnType<S> = { array: S extends 'number' ? number[] : S extends 'string' ? string[] : never; value: S extends 'number' ? n ...

Encountering issues with retrieving application setting variables from Azure App Service in a ReactJS TypeScript application resulting in '

My dilemma lies in my app setup which involves a Node.js runtime stack serving a ReactJs Typescript application. I have set some API URLs in the application settings, and attempted to access them in ReactJs components using process.env.REACT_APP_URL, only ...

The playwright signs in to the application through the cached session in global-setup.ts, where the wait for the selector times out when DEBUG=0 but does not time out when DEBUG=

*Updates 22.6.2022 / Identified a recurring issue with OAuth on another site, where globalSetup fails to function properly on the OAuth domain. 21.6.2022 / Analysis from Trace.zip reveals that the OAuth login URL is correct, but the screenshot depicts a bl ...

Leveraging the keyof keyword to access a specific property within a type

I have a variety of types that I need to work with. For example: type Type = { prop1: number; prop2: string; prop3: someOtherType } type Props = keyof Type I am aware that using an "indexed access type" allows me to extract the type of propN, ...

Contrast between utilizing and publishing, demanding and bringing in within Express

I have recently started learning Typescript and Express. I have a simple exported function that looks like this: export function testFunction(req: any, res: any) { console.log(req.body); return res.status(200).send('OK'); }; And ...

What is the best way to show the current values of predefined variables in VS Code, like "${workspaceFolder}" or "${fileWorkspaceFolder}"?

My current challenge involves debugging some Angular TypeScript source code in VS Code, and I suspect that the issue lies in the incorrect values of certain VS Code Variables - as mentioned here. I want to address this by checking the values of the VS Cod ...

Struggling to obtain the Variable

Trying to send a POST request to my ESP8266 HTTP Server, I need to transmit 4 variables: onhour, offhour, onminute, offminute. These variables should be retrieved from a timepicker-component imported from "ng-bootstrap" Despite numerous attempts over the ...

What functionality does the --use-npm flag serve in the create-next-app command?

When starting a new NextJS project using the CLI, there's an option to use --use-npm when running the command npx create-next-app. If you run the command without any arguments (in interactive mode), this choice isn't provided. In the documentati ...

Leveraging angular2-material with systemjs for Angular2 development

After completing the TUTORIAL: TOUR OF HEROES on this link, I attempted to integrate angular2-material into my project. Unfortunately, I am having issues with the CSS not displaying correctly. Can anyone provide insight into what I may be missing or doing ...

What is the best approach for filtering a nested array in this scenario?

Here is the response I am getting: let m = [ { name: 'Summary', subListExpanded: false, subList: [ ] }, { name: 'Upload', subListExpanded: false, subList: [ ...