angular-cli: Angular16 esbuild extremely slow

Command

build, serve

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

Wit the advent of Angular 16 I figured I’d give the esbuild builder another try after it not providing much benefit for my project with Angular 15 and initially not working with angular-material back then.

Fully on v16, the esbuild does run. However, instead of faster it is extremely slow compared to the default builder.

I’m talking 658s for ng-serve --proxy-config proxy.config.json vs 119831ms with the default builder image

And 694s for ng build -c=production --subresource-integrity vs 149683ms with the default builder image

At the moment, I’m in the dark as to what could be causing this issue. The only possible hunch would be the iban commonjs module?

Minimal Reproduction

The only things I have changed were in the configuration: adding the -esbuild suffix to the specified builder and importing the material icons font via the styles array instead of via an scss file.

Package JSON:

{
  "name": "esl-apor-portalweb",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "start:proxy": "ng serve --proxy-config proxy.config.json",
    "build": "ng build",
    "build:prod": "ng build -c=production --subresource-integrity",
    "watch": "ng build --watch --configuration development",
    "test": "jest --runInBand",
    "test:watch": "jest --watch --changedSince master",
    "test:local": "jest --coverage",
    "test:changed": "jest --coverage --changedSince master",
    "docs:json": "compodoc -p ./tsconfig.json -e json -d .",
    "storybook": "ng run esl-apor-portalweb:storybook",
    "build-storybook": "ng run esl-apor-portalweb:build-storybook",
    "prepare": "husky install",
    "lint": "ng lint --cache",
    "generate:version": "echo 'todo'"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.3",
    "@angular/cdk": "16.1.3",
    "@angular/common": "^16.1.3",
    "@angular/compiler": "^16.1.3",
    "@angular/core": "^16.1.3",
    "@angular/forms": "^16.1.3",
    "@angular/material": "16.1.3",
    "@angular/material-luxon-adapter": "^16.1.3",
    "@angular/platform-browser": "^16.1.3",
    "@angular/platform-browser-dynamic": "^16.1.3",
    "@angular/router": "^16.1.3",
    "@ngx-translate/core": "^15.0.0",
    "@ngx-translate/http-loader": "^8.0.0",
    "esl-apor-portalapi-v1-api": "^1.3.0",
    "iban": "^0.0.14",
    "luxon": "^3.3.0",
    "material-icons": "^1.13.8",
    "rxjs": "~7.8.1",
    "tslib": "^2.6.0",
    "zone.js": "~0.13.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.2",
    "@angular-eslint/builder": "16.0.3",
    "@angular-eslint/eslint-plugin": "16.0.3",
    "@angular-eslint/eslint-plugin-template": "16.0.3",
    "@angular-eslint/schematics": "16.0.3",
    "@angular-eslint/template-parser": "16.0.3",
    "@angular/cli": "~16.1.2",
    "@angular/compiler-cli": "^16.1.3",
    "@chiragrupani/karma-chromium-edge-launcher": "^2.3.1",
    "@compodoc/compodoc": "^1.1.21",
    "@storybook/addon-essentials": "^7.0.24",
    "@storybook/addon-interactions": "^7.0.24",
    "@storybook/addon-links": "^7.0.24",
    "@storybook/angular": "^7.0.24",
    "@storybook/preset-scss": "^1.0.3",
    "@storybook/testing-library": "^0.2.0",
    "@types/iban": "^0.0.32",
    "@types/jest": "^29.5.2",
    "@types/luxon": "^3.3.0",
    "@types/node": "^20.3.2",
    "@typescript-eslint/eslint-plugin": "^5.60.1",
    "@typescript-eslint/parser": "^5.60.1",
    "css-loader": "^6.8.1",
    "eslint": "^8.43.0",
    "eslint-plugin-rxjs": "^5.0.3",
    "eslint-plugin-rxjs-angular": "^2.0.1",
    "eslint-plugin-storybook": "^0.6.12",
    "husky": "^8.0.3",
    "jest": "^29.5.0",
    "jest-preset-angular": "^13.1.1",
    "ngx-translate-testing": "^7.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "sass": "^1.63.6",
    "sass-loader": "^13.3.2",
    "storybook": "^7.0.24",
    "style-loader": "^3.3.3",
    "typescript": "~5.1.6"
  }
}

angular.json

{
	"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
	"version": 1,
	"cli": {
		"packageManager": "pnpm",
		"schematicCollections": ["@angular-eslint/schematics"],
		"analytics": false
	},
	"newProjectRoot": "projects",
	"projects": {
		"esl-apor-portalweb": {
			"projectType": "application",
			"schematics": {
				"@schematics/angular:component": {
					"style": "scss",
					"displayBlock": true
				}
			},
			"root": "",
			"sourceRoot": "src",
			"prefix": "cl",
			"architect": {
				"build": {
					"builder": "@angular-devkit/build-angular:browser-esbuild",
					"options": {
						"outputPath": "dist/esl-apor-portalweb",
						"index": "src/index.html",
						"main": "src/main.ts",
						"polyfills": "src/polyfills.ts",
						"tsConfig": "tsconfig.app.json",
						"inlineStyleLanguage": "scss",
						"assets": ["src/favicon.ico", "src/assets"],
						"styles": [
							"src/googleFonts.scss",
							"node_modules/material-icons/iconfont/material-icons.scss",
							"src/styles.scss",
							"src/theme.scss"
						],
						"scripts": []
					},
					"configurations": {
						"production": {
							"budgets": [
								{
									"type": "initial",
									"maximumWarning": "500kb",
									"maximumError": "1mb"
								},
								{
									"type": "anyComponentStyle",
									"maximumWarning": "2kb",
									"maximumError": "4kb"
								}
							],
							"fileReplacements": [
								{
									"replace": "src/environments/environment.ts",
									"with": "src/environments/environment.prod.ts"
								}
							],
							"outputHashing": "all"
						},
						"development": {
							"buildOptimizer": false,
							"optimization": false,
							"vendorChunk": true,
							"extractLicenses": false,
							"sourceMap": true,
							"namedChunks": true
						}
					},
					"defaultConfiguration": "production"
				},
				"serve": {
					"builder": "@angular-devkit/build-angular:dev-server",
					"configurations": {
						"production": {
							"browserTarget": "esl-apor-portalweb:build:production",
							"headers": {
								"Content-Security-Policy": "trusted-types angular angular#unsafe-bypass angular#bundler; require-trusted-types-for 'script';"
							}
						},
						"development": {
							"browserTarget": "esl-apor-portalweb:build:development",
							"headers": {
								"Content-Security-Policy": "trusted-types angular angular#unsafe-bypass angular#bundler; require-trusted-types-for 'script';"
							}
						}
					},
					"defaultConfiguration": "development"
				},
				"extract-i18n": {
					"builder": "@angular-devkit/build-angular:extract-i18n",
					"options": {
						"browserTarget": "esl-apor-portalweb:build"
					}
				},
				"lint": {
					"builder": "@angular-eslint/builder:lint",
					"options": {
						"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
					}
				},
				"storybook": {
					"builder": "@storybook/angular:start-storybook",
					"options": {
						"browserTarget": "esl-apor-portalweb:build",
						"port": 6006,
						"compodoc": false,
						"configDir": ".storybook"
					}
				},
				"build-storybook": {
					"builder": "@storybook/angular:build-storybook",
					"options": {
						"browserTarget": "esl-apor-portalweb:build",
						"outputDir": "__storybook",
						"quiet": true,
						"compodoc": false
					}
				}
			}
		}
	},
	"schematics": {
		"@angular-eslint/schematics:application": {
			"setParserOptionsProject": true
		},
		"@angular-eslint/schematics:library": {
			"setParserOptionsProject": true
		}
	}
}

Exception or Error

No response

Your Environment

Angular CLI: 16.1.2
Node: 18.12.1
Package Manager: pnpm 8.6.5
OS: win32 x64

Angular: 16.1.3
... animations, cdk, common, compiler, compiler-cli, core, forms
... material, material-luxon-adapter, platform-browser
... platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1601.2 (cli-only)
@angular-devkit/build-angular   16.1.2
@angular-devkit/core            16.1.2 (cli-only)
@angular-devkit/schematics      16.1.2 (cli-only)
@angular/cli                    16.1.2
@schematics/angular             16.1.2 (cli-only)
rxjs                            7.8.1
typescript                      5.1.6

Anything else relevant?

I am working on a Windows VDI via Citrix as mandated by the client, which is considerably slower than my own machine. However, the difference is nowhere near big enough to explan a 650+ second ng-serve command.

Package manager is PNPM

My app is split up in

  • the start/general code (services, pipes, interceptors, first screen components, …)
  • 2 regular modules (layout, shared)
  • 3 lazy loaded modules (1 per screen; they all import shared & layout)

Packages that may be relevant:

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 2
  • Comments: 16 (6 by maintainers)

Most upvoted comments

Tried out switching from PNPM to regular NPM first, that shaved almost 600 seconds off the build time, resulting in 59s for ng serve and 70s for ng build. So that ends up in a ~50% improved compilation time if I can count correctly.

If the impact of the package manager is that dramatic, it should probably be mentioned in the guide and might be worth investigating if there is a possible way to mitigate this?

image

We are using 16.1.5 and Pnpm with Tailwind, and ESBuild.

Even for a very small project, rebuilds are 12 seconds consistently, while it used to be less than a second. Not sure what’s going on

Edit: reverting to the old build: rebuilds are now 1 second, so 12x faster. I assume this is the Sass/EsBuild @import/@use issue. Will have to stick to the old builder until this is somehow resolved. 😦

Edit2: I’m also using pnpm so that’s probably the reason… oh well

Edit3: NG16.2 seems to have resolved the issues

We’re experiencing the same problem. We thought it would be only related to our project because there was nothing similar to find in the web. And we’d never thought that it would be related to pnpm.

Is there any hope that there will be a fix for this in a timely manner? Otherwise we would have to think about migrating away from pnpm.

I’m also facing a similar issue 🤔 not so dramatic but I just compared the previous webpack build for our app with the current esbuild from 16.1.1. Is this a know issue ? I mean the build time was never great and I hopped that it will be better with this change but going to a new tool which makes it slower is never desired 🤔 . Does anyone know why that might be ?

Build Time esbuild on my machine: Cold startup: 120s Rebuilds: 6-12s Build Time webpack on my machine: Cold startup: 102s Rebuilds: 3-6s

Machine specs:

Angular CLI: 16.1.3
Node: 16.18.1
Package Manager: npm 8.19.2
OS: darwin arm64

Angular: 16.1.3
... animations, cdk, cli, common, compiler, compiler-cli, core
... elements, forms, google-maps, language-service, material
... platform-browser, platform-browser-dynamic, platform-server
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1601.3
@angular-devkit/build-angular   16.1.3
@angular-devkit/core            16.1.3
@angular-devkit/schematics      16.1.3
@nguniversal/builders           16.1.1
@nguniversal/express-engine     16.1.1
@schematics/angular             16.1.3
rxjs                            7.5.7
typescript                      5.0.4
webpack                         5.86.0

Also, I noticed that pnpm was being used. Is the Angular project also using Sass? Unfortunately, there is a module resolution limitation within the Sass compiler that can cause long resolution times when using either pnpm or yarn PnP. Can you also try using npm as the package manager to see if that has an effect?

@LorinRenodeyn There is a pending PR to update the docs angular/angular#51006 !

@eneajaho Yes npm/yarn were much faster but I have to use pnpm as the app is part of a large monorepo with 10+ apps.

Cheers

Same here. Angular 16 (with es-build and vite), storybook 7. For me it’s 150s - 200s for bootstrap, 50s-60s for each change, about the same as if not worst than what I had before es-build / vite…