Nx and TypeScript
The build system for TypeScript that TypeScript deserves
The @nrwl/js package ships with corresponding generators and executors that best work when it comes to developing TypeScript applications and libraries.
Note, you can also opt-out of TypeScript and use plain JavaScript by passing the
--jsflag to the generators.
@nrwl/js is particularly useful if you want to
- Create framework-agnostic TypeScript libraries within an existing Nx workspace (say to use in your React, Node or Angular app)
- Publish TypeScript packages to NPM
To get started with TypeScript packages in Nx, either add the @nrwl/js package to an existing Nx workspace or generate a new Nx workspace using the --preset=ts preset.
npx create-nx-workspace happynrwl --preset=tsGenerating a new workspace creates a lightweight setup with a packages and tools folder. The tools folder is where you can add monorepo specific scripts and custom Nx generators, the packages folder is where all our TS based libs will live.
happynrwl/
├── packages/
├── tools/
├── workspace.json
├── nx.json
├── package.json
└── tsconfig.base.jsonAs with most Nx plugins, @nrwl/js comes with a set of generators to quickly scaffold new TypeScript libraries. Let’s have a look at an example.
Create a new TypeScript based library
We can use Nx Console and choose the @nrwl/js:library generator or directly use the following command:
nx generate @nrwl/js:library --name=hello-tsc --buildableThis creates a new library in the packages/hello-tsc folder that already comes with both ESLint and Jest set up and ready to use.
You can run nx lint hello-tsc to run linting or nx test hello-tsc to run Jest tests.
Note, by passing the --buildable flag, our library can be built.
nx build hello-tscThe output of the build step is placed into the dist/packages/hello-tsc by default.
Create a TypeScript based application
Using either @nrwl/node or @nrwl/web, you can also setup a plain TypeScript application that is framework agnostic.
To generate a new framework agnostic TS node application, run
nx generate @nrwl/node:app demoappTo generate a new framework agnostic TS web application, run
nx generate @nrwl/web:app demoappApplications also come with a “serve” target, that allow you to run the app in watch mode:
nx serve demoappImporting Libraries
All the libraries generated within the Nx workspace are configured with corresponding TypeScript path mappings in the root-level tsconfig.base.json file:
1{
2 "compileOnSave": false,
3 "compilerOptions": {
4 ...
5 "paths": {
6 "@happynrwl/hello-swc": ["packages/hello-swc/src/index.ts"],
7 "@happynrwl/hello-tsc": ["packages/hello-tsc/src/index.ts"]
8 }
9 },
10}This allows you to easily import from libraries, by using the corresponding TypeScript path mapping. The following shows an example of importing the helloTsc function from the hello-tsc library into the tsapp application (the same method works between libraries as well):
1// file: packages/tsapp/src/index.ts
2
3// importing from hello-tsc
4import { helloTsc } from '@happynrwl/hello-tsc';
5
6// use the function
7helloTsc();
8
9console.log(`Running ${tsapp()}`);Use SWC as the compiler
Nx also ships with support to use SWC instead of TSC. When generating a new library/application just pass the --compiler=swc.
Here's an example of generating a new library:
nx generate @nrwl/js:library --name=hello-tsc --buildable --compiler=swcAlternatively, if you already have an existing tsc based library/application, you can run the @nrwl/js:convert-to-swc generator to migrate the package from TSC to SWC.
The following command converts the hello-tsc library to SWC:
nx generate @nrwl/js:convert-to-swc --name=hello-tscUsing NPM Scripts rather than Nx executors
If you want to use NPM scripts rather than Nx executors, you can use the --config=npm-scripts:
nx g @nrwl/js:lib mylib --config=npm-scriptsThe Nx generator then creates NPM scripts in the generated library's package.json (rather than in the project.json):
1// packages/mylib/package.json
2{
3 "name": "@happynrwl/mylib",
4 "version": "0.0.1",
5 "type": "commonjs",
6 "scripts": {
7 "build": "echo 'implement build'",
8 "test": "echo 'implement test'"
9 }
10}To run these scripts with Nx, use the same syntax as Nx executors. nx build mylib or nx test mylib will build or test your library, respectively.
Publish your TypeScript packages to NPM
--publishable flag
Let's start by generating a new library publish-me with the following command:
1nx g @nrwl/js:lib publish-me --publishable --importPath="@happynrwl/publish-me"Generating a library with --publishable flag does several things extra on top of --buildable. It generates a minimal publish.mjs script in tools/scripts/ directory if it does not already exist. Additionally, --publishable also adds a publish target to the library's project.json with the following content:
1{
2 "root": "packages/publish-me",
3 "sourceRoot": "packages/publish-me/src",
4 "targets": {
5 "build": {},
6 "publish": {
7 "executor": "nx:run-commands",
8 "options": {
9 "command": "node tools/scripts/publish.mjs publish-me {args.ver} {args.tag}"
10 },
11 "dependsOn": ["build"]
12 },
13 "lint": {},
14 "test": {}
15 },
16 "tags": []
17}The publish target invokes the generated publish.mjs script using nx:run-commands executor. The script does the following:
- Validate the
verargument against a simple SemVer RegExp. - Validate the
nameof the project (eg:publish-me) against the workspace existing projects. - Update the
versionproperty in thepackage.jsonof your project'sbuild.outputPath - Invoke
npm publishwith the provided tag (default tonextso you won't publish tolatestby accident)
Make sure to authenticate with
npmbefore running thepublishtarget.
1nx publish publish-me --ver=<required-version> --tag=[custom-tag]Thanks to “Target Dependencies” (dependsOn) property under the publish target, Nx runs the build target automatically before Nx runs publish. And of course, if build has already run, it won't execute again, thanks to Nx computation caching.
The generated
publish.mjsscript is a minimal version of what a publishing process looks like. You can definitely add more to it as you see fit for your project. For example: you can add a capability to automatic increment the version
Manual setup
Let's set up our hello-tsc library to be publishable as well but this time, we'll do it manually. All we have to do is to copy the publish target from publish-me library to hello-tsc and modify it to use hello-tsc project name instead.
1{
2 "root": "packages/hello-tsc",
3 "sourceRoot": "packages/hello-tsc/src",
4 "targets": {
5 "build": {},
6 "publish": {
7 "executor": "nx:run-commands",
8 "options": {
9 "command": "node tools/scripts/publish.mjs hello-tsc {args.ver} {args.tag}",
10 },
11 "dependsOn": ["build"]
12 },
13 "lint": {},
14 "test": {}
15 },
16 "tags": []
17}
18Now, you should be able to invoke nx publish hello-tsc --ver=<required-version> to publish it to npm