Over the past week, I've been transforming a large monolithic repository (npm/typescript) into a monorepo (yarn/lerna/typescript). Initially, the transition was smooth as I rearranged files into their respective folders and updated imports.
The real challenge arose when I began exploring different methods of running the monorepo in "development" mode with hot reload/watch functionality. I'm eager to avoid transpiling every single package upon each change since they all currently have dependencies on one another. After using lerna to bootstrap the project and install modules, I noticed that it links folders to node_modules, ensuring automatic updates in all repositories with each change. Despite trying various approaches, I could use some guidance on how to proceed or what industry standards dictate.
Issue:
The monorepo consists of 3 packages/projects containing service classes and 1 package serving as a CLI which utilizes those packages. Previously, executing the CLI command in the old monolithic repo involved:
ts-node --transpiler sucrase/ts-node-plugin --project tsconfig.json --require tsconfig-paths/register bin/cli.ts
This command internally transpiled all necessary ts files and executed within a reasonable time frame of 500ms-1s. Maintaining this acceptable transpile time is ideal. Additionally, utilizing tsconfig paths means having a dedicated tsconfig file per package, ideally retaining them while resorting to relative paths if necessary.
Potential Solutions:
Utilize tsc-watch for each directory, set the main entry in package.json to dist/index.js, and finalize the setup. However, the downside to this approach lies in the compilation/transpile time required by tsc – around 500ms-1s for source files, at least 1.5-2s for .d.ts file generation per package, and an additional 500ms for tspaths replacement (per project). Although testing babel-watch showed faster JS compilation, it lacked type generation.
Repeat the aforementioned command with the same file, seemingly functional yet encountering challenges with resolving tsconfig paths of nested packages. For instance, the CLI package resolves its paths using a relative tsconfig, then finds the local package Transpiler (linked folder to node_modules) with its own set of paths requiring specific definition. While sacrificing ts-paths for rapid dev transpile times may be viable, setting package.json main to src/index.ts complicates building procedures as it needs adjusting during production builds, similar to step 1's process.
I drew inspiration and referenced the AWS JS SDK monorepo to navigate through these challenges.
Project Structure:
packages
- cli *(depends on transpiler)*
- transpiler *(depends on common and statements)*
- statements *(depends on common)*
- common *(depends on nothing)*
package.json
tsconfig.json