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:

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:

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:

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:

  1. Run symlink setup script:

    node scripts/setup-storage-symlinks.js
    
  2. Build dependencies first:

    
    yarn nx build storage-system
    
  3. Add 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:

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:

See Also