Ng Packagr Build Setup
Documentation for Developers
ng-packagr Build Setup for NX Libraries
Overview
This guide documents the standardized approach for building Angular libraries in our NX monorepo using ng-packagr instead of custom build scripts.
The Problem We Solved
Previously, some libraries (like storage-angular) used custom build scripts that worked but had limitations:
- ❌ Other libraries couldn't resolve their types during ng-packagr builds
- ❌ Non-standard build process
- ❌ Harder to maintain and debug
- ❌ Caused cascading build failures
The Solution: Symlinks + ng-packagr + Path Mappings
We use a three-part approach to enable ng-packagr for all Angular libraries:
1. Symlink Setup Script
Purpose: Create symlinks in node_modules pointing to built library outputs so ng-packagr can resolve cross-library imports.
Script: scripts/setup-storage-symlinks.js
// Creates symlinks for:
// - @core -> libs/core-models/src (source, not dist)
// - @storage-system -> dist/libs/storage-system
When to run:
- After
yarn install - After building dependent libraries
- Before building libraries that import from these
node scripts/setup-storage-symlinks.js
2. Path Mappings in tsconfig.lib.prod.json
Purpose: Tell TypeScript/ng-packagr where to find cross-library dependencies during build.
Example (libs/storage/storage-angular/tsconfig.lib.prod.json):
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false,
"paths": {
"@storage/storage-core": ["../../../dist/libs/storage/storage-core/src/index.d.ts"],
"@storage/storage-core/*": ["../../../dist/libs/storage/storage-core/src/*"],
"@storage/sync": ["../../../dist/libs/storage/storage-sync/src/index.d.ts"],
"@storage/sync/*": ["../../../dist/libs/storage/storage-sync/src/*"]
}
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}
Key Points:
- Point to dist output, not source
- Include both exact path and wildcard (
/*) patterns - Use relative paths from the library's location
3. Correct ng-package.json Configuration
Purpose: Ensure ng-packagr knows the correct entry point and output location.
Example (libs/flash-persistence/ng-package.json):
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/libs/flash-persistence",
"lib": {
"entryFile": "src/index.ts" // ← Must match your actual entry file!
}
}
Standard Practice: Always use src/index.ts as the entry point for all libraries
4. Update project.json to use ng-packagr
Before (custom script):
{
"build": {
"executor": "nx:run-commands",
"options": {
"command": "node tools/scripts/build-angular-library.js storage/storage-angular"
}
}
}
After (ng-packagr):
{
"build": {
"executor": "@nx/angular:ng-packagr-lite",
"outputs": ["{workspaceRoot}/dist/libs/storage-system"],
"options": {
"project": "libs/storage-system/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/storage-system/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "libs/storage-system/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
}
}
Build Order
Libraries must be built in dependency order:
# 1. Build core dependencies first
yarn nx build storage-core
yarn nx build storage-sync
# 2. Build dependent Angular libraries
yarn nx build storage-angular
# 3. Build libraries that depend on the above
yarn nx build flash-persistence
Troubleshooting
Error: "Cannot destructure property 'pos'"
Cause: Incorrect entry file path in ng-package.json
Solution: Verify that entryFile is set to src/index.ts:
ls libs/your-library/src/index.ts
# Should always be "src/index.ts" (our standard)
Error: "Cannot find module '@storage-system'"
Cause: Missing symlinks or path mappings
Solution:
Run symlink setup script:
node scripts/setup-storage-symlinks.jsBuild dependencies first:
yarn nx build storage-systemAdd path mappings to
tsconfig.lib.prod.json
Error: Library builds but dependent library fails
Cause: Output directory mismatch
Solution: Check ng-package.json dest path:
- For
libs/my-library/ng-package.json, use"dest": "../../dist/libs/my-library" - For
libs/folder/my-library/ng-package.json, use"dest": "../../../dist/libs/folder/my-library"
Benefits
✅ Standard Build Process: All libraries use ng-packagr
✅ Type Safety: Full TypeScript resolution during builds
✅ Better DX: Standard NX commands work consistently
✅ Maintainability: No custom build scripts to maintain
✅ IDE Support: Better IntelliSense and navigation
Migration Checklist
When converting a library from custom script to ng-packagr:
- Update
project.jsonto use@nx/angular:ng-packagr-lite - Create or verify
ng-package.jsonwith correct entry file - Add path mappings to
tsconfig.lib.prod.jsonfor dependencies - Build all dependency libraries first
- Run symlink setup script
- Test build:
yarn nx build your-library --skip-nx-cache - Update documentation
See Also
- core-lib-fix.md - Original symlink approach for @core
- imports-exports.md - Import path strategies