Setting Up Nx Libraries
Complete guide to setting up and configuring NX libraries in our monorepo
Setting Up NX Libraries
This guide explains how to properly configure NX libraries in our monorepo, including the build system fixes we've implemented to resolve ng-packagr compatibility issues.
🚨 Common Build Issues & Solutions
Problem: ng-packagr ES Module Errors
Error Message:
Cannot destructure property 'pos' of 'file.referencedFiles[index]' as it is undefined.
require() of ES Module /path/to/find-cache-directory/index.js not supported.
Root Cause: The @nx/angular:ng-packagr-lite executor has compatibility issues with newer Node.js versions and complex dependency chains.
Solution: Replace ng-packagr with direct TypeScript compilation using nx:run-commands.
📋 Standard Library Configuration
1. Project Structure
Every library MUST have this structure with models directory:
libs/your-library/
├── src/
│ ├── lib/
│ │ ├── models/ # ← REQUIRED: All types go here
│ │ │ ├── index.ts # ← Export all models
│ │ │ └── your-types.ts # ← Type definitions
│ │ ├── components/
│ │ ├── services/
│ │ └── index.ts # ← Must export models first
│ └── test-setup.ts
├── project.json
├── package.json
├── tsconfig.json
├── tsconfig.lib.json
├── tsconfig.lib.prod.json
└── ng-package.json
🚨 CRITICAL: No Local Types Policy
NEVER define types/interfaces in service or component files!
✅ Correct: All types in models/ directory
❌ Wrong: Local type definitions in services/components
2. project.json Configuration
✅ Recommended Build Configuration
{
"name": "your-library",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/your-library/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"build": {
"executor": "nx:run-commands",
"options": {
"commands": [
"echo 'Building your-library...'",
"tsc --project libs/your-library/tsconfig.lib.json --outDir dist/libs/your-library",
"cp -r libs/your-library/src/lib/styles dist/libs/your-library/ 2>/dev/null || true",
"cp libs/your-library/package.json dist/libs/your-library/ 2>/dev/null || true",
"echo 'Your-library build completed'"
],
"parallel": false
},
"outputs": [
"{workspaceRoot}/dist/libs/your-library"
],
"configurations": {
"production": {
"commands": [
"echo 'Building your-library (production)...'",
"tsc --project libs/your-library/tsconfig.lib.prod.json --outDir dist/libs/your-library",
"cp -r libs/your-library/src/lib/styles dist/libs/your-library/ 2>/dev/null || true",
"cp libs/your-library/package.json dist/libs/your-library/ 2>/dev/null || true",
"echo 'Your-library production build completed'"
]
},
"development": {
"commands": [
"echo 'Building your-library (development)...'",
"tsc --project libs/your-library/tsconfig.lib.json --outDir dist/libs/your-library",
"cp -r libs/your-library/src/lib/styles dist/libs/your-library/ 2>/dev/null || true",
"cp libs/your-library/package.json dist/libs/your-library/ 2>/dev/null || true",
"echo 'Your-library development build completed'"
]
}
},
"defaultConfiguration": "production",
"parallelism": true,
"cache": true
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/your-library/jest.config.ts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
❌ Avoid This Configuration
{
"build": {
"executor": "@nx/angular:ng-packagr-lite", // ❌ Causes ES module errors
"options": {
"project": "libs/your-library/ng-package.json" // ❌ Complex ng-packagr config
}
}
}
🔧 Build Command Breakdown
What Each Command Does
echo 'Building your-library...'- Shows progress in build logs
- Helps with debugging and monitoring
tsc --project libs/your-library/tsconfig.lib.json --outDir dist/libs/your-library- Compiles TypeScript to JavaScript
- Generates
.d.tsdeclaration files - Creates source maps (
.js.map) - Uses the library's TypeScript configuration
cp -r libs/your-library/src/lib/styles dist/libs/your-library/ 2>/dev/null || true- Copies CSS/SCSS files to build output
2>/dev/nullsuppresses error messages|| trueensures the build continues even if no styles exist
cp libs/your-library/package.json dist/libs/your-library/ 2>/dev/null || true- Copies package.json for npm publishing
- Required for proper library distribution
echo 'Your-library build completed'- Confirms successful completion
- Helps identify which library finished building
Configuration Differences
| Setting | Development | Production |
|---|---|---|
| TypeScript Config | tsconfig.lib.json |
tsconfig.lib.prod.json |
| Optimization | Basic | Full optimization |
| Source Maps | Yes | Yes (for debugging) |
| Minification | No | Yes |
📁 Required Files
1. tsconfig.lib.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"types": []
},
"exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}
2. tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "es2022"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{ "path": "./tsconfig.lib.json" },
{ "path": "./tsconfig.spec.json" }
]
}
3. ng-package.json
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/libs/your-library",
"lib": {
"entryFile": "src/index.ts"
}
}
Note: We use src/index.ts as the standard entry point for all libraries.
4. package.json
{
"name": "@your-library",
"version": "0.0.1",
"private": true,
"peerDependencies": {
"@angular/common": "^20.0.0",
"@angular/core": "^20.0.0"
}
}
🚀 Build Commands
Standard Build Commands
# Build specific library
nx run your-library:build
# Build with production configuration
nx run your-library:build:production
# Build with development configuration
nx run your-library:build:development
# Build all libraries
yarn build:libs
Custom Build Script (Alternative)
# Use the custom build script for problematic libraries
node tools/scripts/build-angular-library.js your-library
🔍 Troubleshooting
Common Issues
TypeScript Compilation Errors
- Check
tsconfig.lib.jsonextends path - Ensure base tsconfig exists at
../../tsconfig.base.json - Verify all imports are resolvable
- Check
Missing Dependencies
- Check
package.jsonpeer dependencies - Ensure all imported libraries are built first
- Use direct import paths instead of aliases if needed
- Check
Build Output Issues
- Verify output path in
outputsarray - Check that
dist/libs/your-librarydirectory is created - Ensure all required files are copied
- Verify output path in
Debug Commands
# Build with verbose output
nx run your-library:build --verbose
# Skip cache for fresh build
nx run your-library:build --skip-nx-cache
# Check build output
ls -la dist/libs/your-library/
📚 Examples
Working Libraries
ux-ui-core- UI components librarytheme-showcase- Theme demonstration libraryux-space- Space mapping components
Build Script Pattern
All working libraries follow this pattern:
- Use
nx:run-commandsexecutor - Direct TypeScript compilation
- Asset copying with error handling
- Proper output configuration
- Development and production configurations
🎯 Best Practices
- Always use the recommended build configuration to avoid ng-packagr issues
- Test both development and production builds before committing
- Use descriptive echo messages for better build monitoring
- Handle missing assets gracefully with
2>/dev/null || true - Keep TypeScript configurations simple and consistent
- Document any special build requirements in library README
This configuration ensures reliable, fast builds that work consistently across different Node.js versions and environments.
Updated Analysis: TypeScript 5.8.x Compatibility Issue
After upgrading to Node.js 24 and ng-packagr 20.3.0, we discovered that the "Cannot destructure property 'pos'" error is actually a TypeScript 5.8.x compatibility issue, not specifically an ng-packagr problem. The error occurs in the TypeScript compiler when processing complex dependency graphs with referenced files.
Root Cause
- TypeScript Version: 5.8.2/5.8.3
- Error Location:
typescript/lib/typescript.js:125702:10ingetReferencedFileLocation - Trigger: Complex dependency chains and file reference processing
- Impact: Affects
@nx/angular:ng-packagr-liteexecutor but not directtsccompilation
Why nx:run-commands Works
The nx:run-commands approach bypasses this TypeScript issue because:
- Direct Compilation: Uses
tscdirectly without ng-packagr's complex file processing - Simpler Pipeline: Avoids the problematic file reference resolution
- Better Control: More transparent and debuggable build process
Future Considerations
- TypeScript Update: Monitor TypeScript releases for fixes to this issue
- Alternative Solutions: Consider using
@nx/angular:packageexecutor as an alternative - Dependency Optimization: Review and simplify complex dependency chains where possible
The nx:run-commands approach remains the most reliable solution for this workspace until the TypeScript compatibility issue is resolved upstream.