JS Modules monorepo - A library and application repository where all necessary components and services required for production-ready full-stack applications are designed and implemented.
This monorepo is designed to primarily contain TypeScript code. However, packages can also be developed in JavaScript and Solidity.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v<nvm version>/install.
sh | bashnvm install 18npm install -g pnpm nodemon ts-node -yThere are two ways to execute a package's package.json script:
- Open a terminal, navigate to the package's root directory and execute
pnpm <script name> - Open a terminal in the monorepo's root directory and execute
pnpm --filter <package name> <script name>
As stated in monorepo.tools, 'a monorepo is a single repository containing multiple distinct projects, with well-defined relationships.'
While there may be different interpretations for 'well-defined', the term
often implies not only separation and organization of code but also strict
encapsulation which is the case for this monorepo. Strict encapsulation
means that if package_1 consumes a type, variable or function from
package_2, it must always import it by adding package_2 to the
dependencies of package_1's package.json.
Just to be clear, NO script should import anything from a different package using a relative path!
Building up on the concept of 'well-defined' relationships, defining the nature of packages is also important. In this monorepo, any given package can be in only one of the following two categories:
- Library: A package that exports one or more artifacts, which are consumed by other packages
- App: A package that builds into an application to be executed in a production runtime
No package should do both of these things!
The monorepo implements a hierarchical structure of package.json files.
The root package.json contains a set of scripts to bootstrap, lint, test
and build all packages. It also contains common dependencies and configs.
Each package's package.json inherits from the package.jsons in its
parent directories and can include additional dependencies and/or
configurations required for the package's specific purpose.
There are several scripts in the root package.json of the monorepo:
pnpm clean:main # Remove node_modules, build and coverage directories, as well as .husky and .ignore filespnpm clean:tsbuildinfo # Remove .tsbuildinfo filespnpm clean:graph # Remove dependency-graph filespnpm clean:lock-files # Remove package manager lock files pnpm clean:git-hooks # Disable husky git hookspnpm reset:clean # Run the clean:main & clean:tsbuildinfo scriptspnpm reset:install # Create symbolic links to .gitignore for libraries that use .ignore files and install package dependenciespnpm reset:reset # Run the reset:clean & reset:install scriptspnpm git-hooks:set # Configure and enable husky git hookspnpm git-hooks:reset # Run the git-hooks:clean & git-hooks:set scriptspnpm test:test # Run all tests in the monorepopnpm test:watch # Automatically run tests when relevant code is changedpnpm test:coverage # Run all tests in the monorepo and generate a coverage reportpnpm lint:check # Check code for linting errorspnpm lint:fix # Check code for linting errors and automatically correct errors when possiblepnpm graph:dependency-map # Generate dependency graph filespnpm graph:package-tree # Print package tree to the terminalpnpm lerna:build # Run the build script of all packagespnpm prepublish # Run the lint:fix, test:coverage & lerna:build scriptsAll dependencies of the root package.json are installed in all packages.
The monorepo's root package.json contains configuration metadata for the
following libraries, which are shared by all packages:
- eslintConfig
- prettier
Each package can extend and/or override the root configuration in their own
package.json.
The monorepo uses two separate TypeScript configuration files.
tsconfig.jsonfor developmenttsconfig.build.jsonfor production
Just like with the package.json setup, there are the root tsconfigs and
each package's tsconfigs.
The root tsconfig.json extends the root tsconfig.build.json` and adds
path aliases for all library packages whose artifacts, as explained above,
are consumed by other packages.
These path aliases allow
consumers of library artifacts to execute the raw TypeScript code, as they
would if the artifact were imported using a relative path. Without path
aliases, a library package must be compiled and published to the package
registry so that consumer packages can install it as a dependency in their
node_modules directory and consume the library's artifacts from there. This
setup allows for the simultaneous development of different interrelated
packages, without breaking encapsulation, while avoiding the drag of having to
build and publish changes to library packages before said changes are
propagated to their consumers.
Unlike the root tsconfig.json, a package's tsconfig.json does not extend
the package's tsconfig.build.json. Instead, a package's tsconfig.json
extends the root tsconfig.json and its tsconfig.build.json extends the
root tsconfig.build.json.
Unit testing of JavaScript and TypeScript code is carried out using Jest.
Each package has its own jest configuration specified in its jest.config. ts file which usually imports a common configuration defined in the jest. config-common.ts file at the root of the project.
The only .ignore that is actively maintained is .gitignore. All other
.ignores are symbolic links, generated during the execution of the
reset:install script when the project is bootstrapped. Hence, no .ignore
file, aside from .gitignore should be edited manually.
All packages in the monorepo are organized in a directory structure, within
the packages directory, with two levels.
The first level is one of the following general categories:
api: Library packages whose artifacts' main purpose is to support back-end/server features of packages in theapiand/orappscategoriesapps: App packages and library packages that directly support said appscommon: Library packages whose artifacts are consumed by packages in more than one ot the other categoriesweb: Library packages whose artifacts' main purpose is to support front-end web features of packages in theweband/orappscategories
The second level, inside the main categories, serves as a package subcategory and doesn't have to follow a strict nomenclature pattern.
The actual packages then go inside the subcategory directories and follow some basic guidelines for the sake of consistency:
- Package names are always prefixed as follows:
<category>-<subcategory>-<package name>, e.g.api-nest-utils,common-react-hooksorweb-react-utils. - The only place with
indexfiles are used is in each package'ssrcdirectory. Everywhere else, files have meaningful names regardless of the fact that import statements are slightly more verbose. - All package artifacts are exposed in the package's
index.tsmentioned in the previous point.
*IMPORTANT Note: NestJs always builds the app's code for production
before serving the API, regardless of whether the app is run in development or
production with the nest start or nest build, respectively. This means
that app packages which build NestJs servers
don't use the root tsconfig.json path aliases. For this reason, when
changes are made in these packages' dependencies, the dependency packages
must be re-built as does the NestJs app package before said changes take
effect, even when the NestJs app is run with nest start in dev mode.
I developed this project entirely in my free time and without monetary retribution. You are welcome and encouraged to use it free of charge but if it serves your purpose and you want to contribute to the project, any amount of donation is greatly appreciated!
| Paypal | BTC | ETH |
|---|---|---|
![]() |
![]() |
![]() |
| https://paypal.me/pools/c/8t2WvAATaG | bc1q7gq4crnt2t47nk9fnzc8vh488ekmns7l8ufj7z | 0x220E622eBF471F9b12203DC8E2107b5be1171AA8 |


