Getting Started with Integrated Repos

Create a New Workspace

Start by creating a new workspace. We can use the following command that will help us set it up.

npx create-nx-workspace@latest myorg --preset=ts

The file structure should look like this:

1myorg/ 2├── packages/ 3├── tools/ 4├── nx.json 5├── package.json 6├── README.md 7└── tsconfig.base.json 8

Create a Package

Nx comes with generators that can help with scaffolding applications. Run this generator to make a new library named is-even:

npx nx generate @nx/js:library is-even \

--directory=libs/is-even \

--publishable \

--importPath=@myorg/is-even

Nx 15 and lower use @nrwl/ instead of @nx/

This command:

  • Uses the @nx/js plugin's library generator to scaffold a new library named is-even.
  • The --publishable flag makes sure we also get a package.json generated and a publish target we can invoke to publish to NPM.
  • The --importPath allows us to define the name of the NPM package.

You should now have the following structure:

1packages/ 2└── is-even/ 3 ├── src/ 4 | └── lib/ 5 | | ├── is-even.spec.ts 6 | | ├── is-even.ts 7 | └── index.ts 8 ├── project.json 9 ├── package.json 10 ├── ... 11 └── tsconfig.json 12

Update is-even.ts with this content:

packages/is-even/src/lib/is-even.ts
1export function isEven(x: number): boolean { 2 return x % 2 === 0; 3} 4

The Nx plugins use a project-level project.json to manage the metadata around the available targets that can be run for a given project. The generated project.json for is-even contains build, publish, lint and test targets:

packages/is-even/project.json
1{ 2 "name": "is-even", 3 "targets": { 4 "build": { 5 /* ... */ 6 }, 7 "publish": { 8 /* ... */ 9 }, 10 "lint": { 11 /* ... */ 12 }, 13 "test": { 14 /* ... */ 15 } 16 } 17} 18

You can dive into the various settings to fine-tune the options used for building, publishing, linting or testing the package. The low-level details are being taken care of by the plugin.

Running

  • npx nx build is-even builds the src files and places a ready-to-be-published package in dist/packages/is-even at the root of your workspace
  • npx nx publish is-even runs a publish script from dist/packages/is-even to push your package to NPM
  • npx nx test is-even runs the pre-configured Jest tests for the package
  • npx nx lint is-even runs the pre-configured ESLint checks for the package

Local Linking of Packages

The local linking of packages in an integrated monorepo style is handled by Nx automatically by leveraging TypeScript path mappings in the tsconfig.base.json file.

To illustrate that, let's create another package is-odd. We can again run the generator for that:

npx nx generate @nx/js:library is-odd \

--directory=libs/is-odd \

--publishable \

--importPath=@myorg/is-odd

Nx 15 and lower use @nrwl/ instead of @nx/

Note how the tsconfig.base.json now has two entries:

tsconfig.base.json
1{ 2 "compileOnSave": false, 3 "compilerOptions": { 4 ... 5 "paths": { 6 "@myorg/is-even": ["packages/is-even/src/index.ts"], 7 "@myorg/is-odd": ["packages/is-odd/src/index.ts"] 8 } 9 } 10} 11

Update the is-odd.ts implemention in the is-odd package to import isEven from the @myorg/is-even package:

packages/is-odd/src/lib/is-odd.ts
1import { isEven } from '@myorg/is-even'; 2 3export function isOdd(x: number): boolean { 4 return !isEven(x); 5} 6

This is all that needs to be done.

Typescript Paths

When a library is created, Nx adds a new Typescript path to the tsconfig.base.json file. The running Typescript server process inside of your editor sometimes doesn't pick up these changes and you have to restart the server to remove inline errors on your import statements. This can be done in VS Code from the command palette when viewing a typescript file (Command-Shift-P) "Typescript: Restart TS server"

Task Dependencies

In a monorepo there are not just dependencies among packages, but also among their tasks.

For example, whenever we build is-odd we need to ensure that is-even is built beforehand. Nx can define such task dependencies by adding a targetDefaults property to nx.json.

In an integrated monorepo style this is already being dealt with by the generators. The current nx.json file already comes with defaults that work out of the box:

nx.json
1{ 2 ... 3 "targetDefaults": { 4 "build": { 5 "dependsOn": ["^build"], 6 ... 7 }, 8 ... 9 }, 10 ... 11} 12

This tells Nx to run the build target of all the dependent projects first, before the build target of the package itself is being run.

Remove any existing dist folder at the root of the workspace and run:

npx nx build is-odd

It will automatically first run npx nx build is-even, so you should end up with both packages in your dist folder. Note that if is-even has been built before, it would just be restored out of the cache.

Cache Build Results

To build the is-even package run:

npx nx build is-even

Run the same command a second time and you'll see the build cache is being used:

~/workspace

npx nx build is-even

1> nx run is-even:build 2 3Compiling TypeScript files for project "is-even"... 4Done compiling TypeScript files for project "is-even". 5 6————————————————————————————————————————————————————————————————————— 7 8NX Successfully ran target build for project is-even (713ms) 9

Running Multiple Tasks

To run the build target for all the packages in the workspace, use:

npx nx run-many -t build

What you would get is the following:

~/workspace

npx nx run-many -t build

1✔ nx run is-even:build [existing outputs match the cache, left as is] 2✔ nx run is-odd:build (906ms) 3 4————————————————————————————————————————————————————————————————— 5 6NX Successfully ran target build for 2 projects (914ms) 7 8Nx read the output from the cache instead of running the command for 1 out of 2 tasks. 9

Note how on the is-even:build it didn't run the build but rather pulled it out of the cache because the build has ran before. If you re-run the run-many command all of the builds would be cached.

You can also only run tasks on packages that got changed by using

npx nx affected -t build

Learn More