<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> Setup angular incremental builds doc does not contain info on esbuild ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> Add information about using incremental builds with angular. Also callout that the performance gain may not be as effective as it was with webpack ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #27716
370 lines
9.0 KiB
Markdown
370 lines
9.0 KiB
Markdown
# Setup incremental builds for Angular applications
|
||
|
||
In this guide we’ll specifically look into which changes need to be made to enable incremental builds for Angular
|
||
applications.
|
||
|
||
## Use buildable libraries
|
||
|
||
To enable incremental builds you need to use buildable libraries.
|
||
|
||
{% callout type="note" title="Directory Flag Behavior Changes" %}
|
||
The command below uses the `as-provided` directory flag behavior, which is the default in Nx 16.8.0. If you're on an
|
||
earlier version of Nx or using the `derived` option, omit the `--directory` flag. See
|
||
the [as-provided vs. derived documentation](/deprecated/as-provided-vs-derived) for more details.
|
||
{% /callout %}
|
||
|
||
You can generate a new buildable library with:
|
||
|
||
```shell
|
||
nx g @nx/angular:lib my-lib --directory=libs/my-lib --buildable
|
||
```
|
||
|
||
The generated buildable library uses the `@nx/angular:ng-packagr-lite` executor which is optimized for the incremental
|
||
builds scenario:
|
||
|
||
```jsonc
|
||
{
|
||
"projectType": "library",
|
||
...
|
||
"targets": {
|
||
"build": {
|
||
"executor": "@nx/angular:ng-packagr-lite",
|
||
"outputs": [
|
||
"{workspaceRoot}/dist/libs/my-lib"
|
||
],
|
||
"options": {
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
},
|
||
"defaultConfiguration": "production"
|
||
},
|
||
...
|
||
},
|
||
...
|
||
},
|
||
```
|
||
|
||
{% callout type="warning" title="More details" %}
|
||
Please note that it is important to keep the `outputs` property in sync with the `dest` property in the file
|
||
`ng-package.json` located inside the library root. When a library is generated, this is configured correctly, but if the
|
||
path is later changed in `ng-package.json`, it needs to be updated as well in the project configuration.
|
||
|
||
The `@nx/angular:package` executor also supports incremental builds. It is used to build and package an Angular library
|
||
to be distributed as an NPM package following the Angular Package Format (APF) specification. It will be automatically
|
||
configured when generating a publishable library (`nx g @nx/angular:lib my-lib --publishable --importPath my-lib`).
|
||
{% /callout %}
|
||
|
||
## Adjust the application executor
|
||
|
||
{% callout type="note" title="Angular Esbuild Performance" %}
|
||
From internal testing done at Nx, the build time saved from using incremental builds when using Esbuild with Angular is
|
||
not as effective as the time saved when using Webpack with Angular.
|
||
Angular's build time with Esbuild already provides a great performance boost and therefore overall time saved may not
|
||
warrant using incremental builds with Esbuild for Angular
|
||
{% /callout %}
|
||
|
||
Change your Angular application’s "build" target executor to Nx's version of builder you're currently using and the "
|
||
serve" target executor to `@nx/angular:dev-server` as shown below.
|
||
|
||
- `@angular-devkit/build-angular:application` -> `@nx/angular:application`
|
||
- `@angular-devkit/build-angular:browser-esbuild` -> `@nx/angular:browser-esbuild`
|
||
- `@angular/build:browser` -> `@nx/angular:webpack-browser`
|
||
|
||
{% tabs %}
|
||
{% tab label="@angular-devkit/build-angular:application" %}
|
||
|
||
```jsonc {% fileName="project.json" %}
|
||
{
|
||
"projectType": "application",
|
||
...
|
||
"targets": {
|
||
"build": {
|
||
"dependsOn": ["^build"],
|
||
"executor": "@nx/angular:application",
|
||
"outputs": [
|
||
"{options.outputPath}"
|
||
],
|
||
"options": {
|
||
"buildLibsFromSource": false
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
},
|
||
"defaultConfiguration": "production"
|
||
},
|
||
"serve": {
|
||
"executor": "@nx/angular:dev-server",
|
||
"options": {
|
||
"buildTarget": "my-app:build",
|
||
"buildLibsFromSource": false
|
||
},
|
||
"configurations": {
|
||
"production": {
|
||
"buildTarget": "my-app:build:production"
|
||
}
|
||
}
|
||
},
|
||
...
|
||
}
|
||
},
|
||
```
|
||
|
||
{% /tab %}
|
||
{% tab label="@angular-devkit/build-angular:browser-esbuild" %}
|
||
|
||
```jsonc {% fileName="project.json" %}
|
||
{
|
||
"projectType": "application",
|
||
...
|
||
"targets": {
|
||
"build": {
|
||
"dependsOn": ["^build"],
|
||
"executor": "@nx/angular:browser-esbuild",
|
||
"outputs": [
|
||
"{options.outputPath}"
|
||
],
|
||
"options": {
|
||
"buildLibsFromSource": false
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
},
|
||
"defaultConfiguration": "production"
|
||
},
|
||
"serve": {
|
||
"executor": "@nx/angular:dev-server",
|
||
"options": {
|
||
"buildTarget": "my-app:build",
|
||
"buildLibsFromSource": false
|
||
},
|
||
"configurations": {
|
||
"production": {
|
||
"buildTarget": "my-app:build:production"
|
||
}
|
||
}
|
||
},
|
||
...
|
||
}
|
||
},
|
||
```
|
||
|
||
{% /tab %}
|
||
{% tab label="@angular-devkit/build-angular:browser" %}
|
||
|
||
```jsonc {% fileName="project.json" %}
|
||
{
|
||
"projectType": "application",
|
||
...
|
||
"targets": {
|
||
"build": {
|
||
"dependsOn": ["^build"],
|
||
"executor": "@nx/angular:webpack-browser",
|
||
"outputs": [
|
||
"{options.outputPath}"
|
||
],
|
||
"options": {
|
||
"buildLibsFromSource": false
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
},
|
||
"defaultConfiguration": "production"
|
||
},
|
||
"serve": {
|
||
"executor": "@nx/angular:dev-server",
|
||
"options": {
|
||
"buildTarget": "my-app:build",
|
||
"buildLibsFromSource": false
|
||
},
|
||
"configurations": {
|
||
"production": {
|
||
"buildTarget": "my-app:build:production"
|
||
}
|
||
}
|
||
},
|
||
...
|
||
}
|
||
},
|
||
```
|
||
|
||
{% /tab %}
|
||
{% /tabs %}
|
||
|
||
### Add Executor to Target Defaults
|
||
|
||
If you'd like to avoid adding `"dependsOn": ["^build"]` to every application in your workspace that uses one of the
|
||
required executors you can add it to the `"targetDefaults"` section of the `nx.json`:
|
||
|
||
{% tabs %}
|
||
{% tab label="@nx/angular:application" %}
|
||
|
||
```json {% fileName="nx.json" %}
|
||
{
|
||
"targetDefaults": {
|
||
"@nx/angular:application": {
|
||
"dependsOn": ["^build"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
{% /tab %}
|
||
{% tab label="@nx/angular:browser-esbuild" %}
|
||
|
||
```json {% fileName="nx.json" %}
|
||
{
|
||
"targetDefaults": {
|
||
"@nx/angular:browser-esbuild": {
|
||
"dependsOn": ["^build"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
{% /tab %}
|
||
{% tab label="@nx/angular:webpack-browser" %}
|
||
|
||
```json {% fileName="nx.json" %}
|
||
{
|
||
"targetDefaults": {
|
||
"@nx/angular:webpack-browser": {
|
||
"dependsOn": ["^build"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
{% /tab %}
|
||
{% /tabs %}
|
||
|
||
## Running and serving incremental builds
|
||
|
||
To build an application incrementally use the following command:
|
||
|
||
```shell
|
||
nx build my-app --parallel
|
||
```
|
||
|
||
To serve an application incrementally use this command:
|
||
|
||
```shell
|
||
nx serve my-app
|
||
```
|
||
|
||
### Build target name
|
||
|
||
It is required to use the same target name for the build target (target using one of the executors that support
|
||
incremental builds: `@nx/angular:application`, `@nx/angular:browser-esbuild`, `@nx/angular:webpack-browser`,
|
||
`@nx/angular:package` and `@nx/angular:ng-packagr-lite`) in the
|
||
project being built and the buildable libraries it depends on. The executors that support incremental builds rely on the
|
||
build target name of the project to identify which of the libraries it depends on are buildable.
|
||
|
||
If you need to have a different build target name for an application (or library) build (e.g. when composing different
|
||
targets), you need to make sure the build target name of all the relevant projects is the same.
|
||
|
||
Say you have the same application above with a configuration as follows:
|
||
|
||
```jsonc {% fileName="project.json" %}
|
||
{
|
||
"projectType": "application",
|
||
...
|
||
"targets": {
|
||
"build-base": {
|
||
"executor": "@nx/angular:webpack-browser",
|
||
"outputs": [
|
||
"{options.outputPath}"
|
||
],
|
||
"options": {
|
||
"buildLibsFromSource": false
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
}
|
||
},
|
||
"build": {
|
||
"executor": "nx:run-commands",
|
||
"outputs": [
|
||
"{options.outputPath}"
|
||
],
|
||
"options": {
|
||
"commands": [
|
||
"node ./tools/scripts/important-script.js",
|
||
"node ./tools/scripts/another-important-script.js"
|
||
],
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
}
|
||
},
|
||
"serve": {
|
||
"executor": "@nx/angular:dev-server",
|
||
"options": {
|
||
"buildTarget": "my-app:build-base",
|
||
"buildLibsFromSource": false
|
||
},
|
||
"configurations": {
|
||
"production": {
|
||
"buildTarget": "my-app:build-base:production"
|
||
}
|
||
}
|
||
},
|
||
...
|
||
}
|
||
},
|
||
```
|
||
|
||
And the `targetDefaults` configured in the `nx.json` as:
|
||
|
||
```json
|
||
{
|
||
"targetDefaults": {
|
||
"build": {
|
||
"dependsOn": ["build-base"]
|
||
},
|
||
"build-base": {
|
||
"dependsOn": ["^build-base"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
The build target name of the application is `build-base`. Therefore, the build target name of the buildable libraries it
|
||
depends on must also be `build-base`:
|
||
|
||
```jsonc
|
||
{
|
||
"projectType": "library",
|
||
...
|
||
"targets": {
|
||
"build-base": {
|
||
"executor": "@nx/angular:ng-packagr-lite",
|
||
"outputs": [
|
||
"{workspaceRoot}/dist/libs/my-lib"
|
||
],
|
||
"options": {
|
||
...
|
||
},
|
||
"configurations": {
|
||
...
|
||
},
|
||
"defaultConfiguration": "production"
|
||
},
|
||
...
|
||
},
|
||
...
|
||
},
|
||
```
|
||
|
||
## Example repository
|
||
|
||
Check out the [nx-incremental-large-repo](https://github.com/nrwl/nx-incremental-large-repo) for a live example.
|
||
|
||
{% github-repository url="https://github.com/nrwl/nx-incremental-large-repo" /%}
|