Using Angular CLI and Wireit
As Angular and front-end projects grow in complexity, build and test times inevitably increase, impacting developer productivity and CI/CD pipeline efficiency. Long build times can slow down development cycles, delay deployments, and increase infrastructure costs. Wireit, a open source build orchestration tool, offers a solution by introducing intelligent caching and task orchestration to your npm scripts.
In this post, we'll explore how to integrate Wireit with the Angular CLI to significantly improve build and test performance through caching, while maintaining the familiar Angular development experience.
Angular CLI
The Angular CLI is the standard tool for creating, building, and testing Angular applications. Starting with Angular v20, creating a new project is as simple as running ng new
followed by your project name. The CLI provides a comprehensive set of commands that handle everything from development servers to production builds.
At its core, the Angular CLI provides two essential commands for any Angular project:
ng build
- Compiles your Angular application into static files ready for deploymentng test
- Runs your unit tests using Karma and Jasmine
A typical Angular project's package.json includes these standard scripts:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
}
While these commands work well for small to medium projects, as your codebase grows, you'll notice build times increasing from seconds to minutes. This is where Wireit comes in to help optimize your workflow.
Wireit
Wireit is a build orchestration tool that adds intelligent caching and task dependencies to npm scripts. Instead of running the same build or test commands repeatedly, Wireit caches the results and only re-runs them when the input files have changed. This can dramatically reduce build times, especially in CI/CD environments where builds run frequently.
To get started with Wireit, first install it as a dev dependency:
npm install --save-dev wireit
Then, you can convert your existing npm scripts to use Wireit. Here's how to transform the basic Angular build script:
"scripts": {
"build": "wireit"
...
},
"wireit": {
"build": {
"command": "ng build",
"files": [
"src/**/*",
"!src/**/*.spec.ts",
"angular.json",
"package.json",
"tsconfig.json",
"tsconfig.app.json"
],
"output": [
"dist/angular-wireit/**/*"
]
}
}
The Wireit configuration has two critical properties:
files
: Specifies which files Wireit should monitor for changes. When any of these files change, Wireit knows it needs to re-run the build. Notice we exclude test files (!src/**/*.spec.ts
) since they don't affect the build output.output
: Defines the build artifacts that Wireit should cache. If the input files haven't changed, Wireit can skip the build entirely and restore these cached outputs.
Let's see the caching in action. When you run the build for the first time:
✅ Ran 1 script and skipped 0 in 2.2s.
Wireit executes the Angular build normally. But when you run the same command again without changing any source files:
✅ Ran 0 scripts and skipped 1 in 0s.
The build completes instantly! Wireit detected that no input files changed, so it skipped the build entirely. This is particularly powerful in CI environments where multiple jobs might need the same build artifacts.
Configuring Tests with Wireit
Tests require a slightly different configuration since they don't produce deployable artifacts but still benefit from caching:
"scripts": {
...
"test": "wireit"
},
"wireit": {
...
"test": {
"command": "ng test --no-watch --no-progress --browsers=ChromeHeadless",
"files": [
"src/**/*",
"angular.json",
"package.json",
"tsconfig.json",
"tsconfig.app.json",
"tsconfig.spec.json"
],
"output": [
"dist/test-out/**/*"
]
}
}
Note that the test configuration:
- Includes all source files (both application and test files) in the
files
array - Uses a different output directory (
dist/test-out
) than the build, typically for artifacts like test coverage reports. - Runs tests in headless mode for consistent CI execution
This separation ensures that build and test caches don't interfere with each other, allowing you to cache both independently.
Wireit Service
So far, we've configured Wireit for one-time build and test commands. But what about long-running processes like development servers or watch modes? Wireit handles these with a special service
configuration.
A Wireit service is a long-running process that doesn't produce cacheable output but may depend on other tasks. Services are perfect for development workflows where you want the benefits of Wireit's dependency management without caching.
"scripts": {
"start": "wireit",
"test:watch": "wireit"
},
"wireit": {
"start": {
"command": "ng serve",
"dependencies": [
"build"
],
"service": true
},
"test:watch": {
"command": "ng test",
"service": true
}
}
The service: true
flag tells Wireit that these commands:
- Run continuously until manually stopped
- Don't produce cacheable output
- Should be restarted if their dependencies change
Notice that the start
script depends on build
. This means Wireit will ensure a fresh build exists before starting the development server, useful when switching branches or pulling changes.
Wireit Dependencies
One of Wireit's most powerful features is its dependency management system. You can define relationships between scripts, and Wireit will automatically execute them in the correct order, running independent tasks in parallel for optimal performance.
The dependencies
array allows any script to specify which other scripts must complete successfully before it runs. For example, you might want to ensure your code builds successfully before running tests, or that linting passes before building.
Wireit analyzes the entire dependency tree and executes tasks as efficiently as possible. If two tasks don't depend on each other, they'll run in parallel. This automatic parallelization can significantly speed up complex workflows.
Creating a CI Entry Point
A common pattern is to create a single entry point for CI pipelines that runs all necessary checks:
"scripts": {
"ci": "wireit",
"build": "wireit",
"test": "wireit",
...
},
"wireit": {
"ci": {
"dependencies": [
"build",
"test"
]
},
"build": {
"command": "ng build",
"files": ["src/**/*", "!src/**/*.spec.ts", "angular.json", "package.json", "tsconfig.json", "tsconfig.app.json"],
"output": ["dist/angular-wireit/**/*"]
},
"test": {
"command": "ng test --no-watch --no-progress --browsers=ChromeHeadless",
"files": ["src/**/*", "angular.json", "package.json", "tsconfig.json", "tsconfig.app.json", "tsconfig.spec.json"],
"output": ["dist/test-out/**/*"]
}
}
Now your CI pipeline can simply run npm run ci
, and Wireit will:
- Check if the build cache is valid
- Check if the test cache is valid
- Run both in parallel if they're independent, or in sequence if one depends on the other
- Skip any tasks where the cache is still valid
This approach scales very well to monorepos where you might have dozens of interdependent projects. Wireit ensures everything runs in the correct order while maximizing parallelization and caching.
Conclusion
Wireit levels up the Angular CLI development experience by adding intelligent caching and dependency management without changing your familiar workflow. By wrapping your existing npm scripts with Wireit, you can achieve:
- Dramatic speed improvements: Skip redundant builds and tests when files haven't changed
- Better CI/CD efficiency: Reduce pipeline times
- Simplified orchestration: Define dependencies between tasks and let Wireit handle the execution order
- Monorepo readiness: Scale your build system as your project grows
You can also adopt Wireit incrementally. Start with your slowest scripts and expand from there. Your team can continue using the same npm commands they're familiar with while benefiting from Wireit's optimizations under the hood.
To see a complete example of Angular CLI with Wireit integration, check out the demo below!