Configurations

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

NX Libraries Configuration Guide

📌 Note: All libraries use src/index.ts as the standard entry point (we migrated from public-api.ts in October 2025).

Build System Migration (January 2025)

Problem Solved ✅

Issue: TypeScript build artifacts (.js, .d.ts, .js.map) were being generated in source directories (libs/*/src/) instead of only in dist/.

Root Cause: Libraries using @nx/angular:ng-packagr-lite executor had two problems:

  1. Incompatible with TypeScript 5.8.x (caused build failures)
  2. Could generate files in source directories despite proper tsconfig settings

Solution: Migrated ALL 18 Angular libraries to use our custom builder which:


Standard Library Configuration

Every Angular Library Should Have:

1. project.json - Custom Builder

{
  "targets": {
    "build": {
      "executor": "nx:run-commands",
      "outputs": ["{workspaceRoot}/dist/libs/LIBRARY-NAME"],
      "options": {
        "command": "node tools/scripts/build-scripts/build-angular-library.js LIBRARY-NAME"
      },
      "configurations": {
        "production": {
          "command": "node tools/scripts/build-scripts/build-angular-library.js LIBRARY-NAME"
        }
      },
      "defaultConfiguration": "production",
      "cache": true
    }
  }
}

2. tsconfig.lib.json - TypeScript Configuration

There are two approaches for how your library imports other libraries. Let's understand both with concrete examples.


🎯 Approach A: Import from Built Output (Dist-Based)

What this means: Your library imports the compiled JavaScript from other libraries.

Real-world example:

You're building libs/ux-panel which uses @storage-system. Here's what happens:

// libs/ux-panel/src/lib/some-component.ts
import { StorageService } from '@storage-system';  // ← This resolves to...

With dist-based setup, @storage-system points to: dist/libs/storage-system/index.js (the built version)

Configuration needed:

File: libs/ux-panel/tsconfig.lib.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "rootDir": "./src",           
    "outDir": "../../dist/libs/ux-panel",
    "composite": false,
    "noEmit": false,
    "baseUrl": ".",
    "paths": {
      // Point to the BUILT version in dist/
      "@storage-system": ["../../dist/libs/storage-system/index"],
      "@storage-system/*": ["../../dist/libs/storage-system/*"]
    }
  }
}

File: tsconfig.base.json (workspace root)

{
  "compilerOptions": {
    "paths": {
      // All libraries point to dist/
      "@storage-system": ["dist/libs/storage-system/index"],
      "@storage-system/*": ["dist/libs/storage-system/*"]
    }
  }
}

Workflow:

# 1. Change code in storage-system
vim libs/storage-system/src/services/storage.service.ts

# 2. Must build storage-system first!
yarn nx build storage-system  # Takes 5-10 seconds

# 3. Now ux-panel can see the changes
yarn nx serve ux-panel  # Will use the built files from dist/

Pros:

Cons:


🚀 Approach B: Import from Source (with rootDirs)

What this means: Your library imports the actual TypeScript source files from other libraries.

Real-world example:

Same setup: libs/ux-panel uses @storage-system. But now:

// libs/ux-panel/src/lib/some-component.ts
import { StorageService } from '@storage-system';  // ← This resolves to...

With source-based setup, @storage-system points to: libs/storage-system/src/index.ts (the source TypeScript file)

Configuration needed:

File: libs/ux-panel/tsconfig.lib.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "rootDirs": [
      "./src",                      // ← Your library's source
      "../storage-system/src"       // ← storage-system's source
    ],
    "outDir": "../../dist/libs/ux-panel",
    "composite": false,
    "noEmit": false
    // No "paths" needed - inherits from tsconfig.base.json
  }
}

File: tsconfig.base.json (workspace root)

{
  "compilerOptions": {
    "paths": {
      // All libraries point to SOURCE files
      "@storage-system": ["libs/storage-system/src/index.ts"],
      "@storage-system/*": ["libs/storage-system/src/*"]
    }
  }
}

Workflow:

# 1. Change code in storage-system
vim libs/storage-system/src/services/storage.service.ts

# 2. Save the file
# 3. Changes are IMMEDIATELY visible in ux-panel! ✨
yarn nx serve ux-panel  # Automatically picks up changes via hot-reload

Pros:

Cons:


🤔 What is rootDirs? (Explained for Beginners)

The Problem:

TypeScript has a rule: "All files you import must be inside your rootDir"

When your tsconfig.lib.json says:

{
  "compilerOptions": {
    "rootDir": "./src"  // TypeScript expects ALL imports to be under libs/ux-panel/src/
  }
}

But you import from @storage-system, which resolves to libs/storage-system/src/index.ts - that's OUTSIDE your rootDir!

TypeScript error:

❌ File 'libs/storage-system/src/index.ts' is not under 'rootDir' 'libs/ux-panel/src'

The Solution: rootDirs

Instead of one rootDir, you can have multiple "root directories":

{
  "compilerOptions": {
    "rootDirs": [
      "./src",                    // ← My library's source is valid
      "../storage-system/src"     // ← storage-system's source is ALSO valid
    ]
  }
}

TypeScript now thinks: "Okay, files can come from EITHER of these directories, that's fine!"

Visual explanation:

Without rootDirs:
  ✅ libs/ux-panel/src/lib/component.ts        (allowed)
  ❌ libs/storage-system/src/index.ts          (ERROR!)

With rootDirs:
  ✅ libs/ux-panel/src/lib/component.ts        (allowed)
  ✅ libs/storage-system/src/index.ts          (allowed!)

📊 Quick Comparison Table

Feature Dist-Based Source-Based (rootDirs)
Import points to dist/libs/X/index.js libs/X/src/index.ts
Rebuild needed after changes ✅ Yes (5-10s per lib) ❌ No (instant)
IDE Go-to-Definition shows Compiled .d.ts Real .ts source
Configuration complexity Simple Moderate
TypeScript speed Faster Slightly slower
Best for Stable dependencies Active development

🎯 Our Current Standard (January 2025)

We use source-based imports with rootDirs because:


Workspace-Level Configuration

Understanding tsconfig.base.json (For Beginners)

What is this file?

tsconfig.base.json is a special file at your workspace root that tells TypeScript where to find libraries when you import them.

Example: When you write:

import { StorageService } from '@storage-system';

TypeScript asks: "Where is @storage-system?"

The answer comes from tsconfig.base.json.


How Path Mappings Work

File location: /Users/you/my-monorepo/tsconfig.base.json (at workspace root)

What it looks like:

{
  "compilerOptions": {
    "paths": {
      "@storage-system": ["libs/storage-system/src/index.ts"],
      "@ux-panel": ["libs/ux-panel/src/index.ts"],
      "@theme-system": ["libs/theme-system/src/index.ts"]
    }
  }
}

What this means:

The * wildcard:

{
  "paths": {
    "@storage-system": ["libs/storage-system/src/index.ts"],
    "@storage-system/*": ["libs/storage-system/src/*"]
  }
}

This allows deep imports:

// This works:
import { StorageService } from '@storage-system';  // → index.ts

// And this also works:
import { IndexedDBAdapter } from '@storage-system/core/adapters/indexeddb-adapter';
// → libs/storage-system/src/core/adapters/indexeddb-adapter.ts

Two Configuration Styles

Style 1: Point to Source Files (Current Standard)

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@storage-system": ["libs/storage-system/src/index.ts"],
      "@storage-system/*": ["libs/storage-system/src/*"],
      "@ux-panel": ["libs/ux-panel/src/index.ts"],
      "@ux-panel/*": ["libs/ux-panel/src/*"]
    }
  }
}

What this means:

Example file path resolution:

import { StorageService } from '@storage-system';
// ↓ Resolves to:
// libs/storage-system/src/index.ts (SOURCE FILE)

Style 2: Point to Built Output

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@storage-system": ["dist/libs/storage-system/index"],
      "@storage-system/*": ["dist/libs/storage-system/*"],
      "@ux-panel": ["dist/libs/ux-panel/index"],
      "@ux-panel/*": ["dist/libs/ux-panel/*"]
    }
  }
}

What this means:

Example file path resolution:

import { StorageService } from '@storage-system';
// ↓ Resolves to:
// dist/libs/storage-system/index.js (BUILT FILE)
// dist/libs/storage-system/index.d.ts (TYPE DEFINITIONS)

Side-by-Side Comparison

Scenario: You have 3 libraries:

Source-Based Setup (Our Current Standard)

File: tsconfig.base.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@storage-system": ["libs/storage-system/src/index.ts"],
      "@storage-system/*": ["libs/storage-system/src/*"],
      "@ux-panel": ["libs/ux-panel/src/index.ts"],
      "@ux-panel/*": ["libs/ux-panel/src/*"],
      "@theme-system": ["libs/theme-system/src/index.ts"],
      "@theme-system/*": ["libs/theme-system/src/*"]
    }
  }
}

File: libs/ux-panel/tsconfig.lib.json

{
  "compilerOptions": {
    "rootDirs": [
      "./src",
      "../storage-system/src",
      "../theme-system/src"
    ],
    "outDir": "../../dist/libs/ux-panel"
  }
}

Result:


Dist-Based Setup

File: tsconfig.base.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@storage-system": ["dist/libs/storage-system/index"],
      "@storage-system/*": ["dist/libs/storage-system/*"],
      "@ux-panel": ["dist/libs/ux-panel/index"],
      "@ux-panel/*": ["dist/libs/ux-panel/*"],
      "@theme-system": ["dist/libs/theme-system/index"],
      "@theme-system/*": ["dist/libs/theme-system/*"]
    }
  }
}

File: libs/ux-panel/tsconfig.lib.json

{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "../../dist/libs/ux-panel"
  }
}

Result:


Migration Summary

Libraries Migrated (18 total):

Root Level (15):

Nested in theme-system (3):

Result

✅ ZERO source pollution - All build artifacts go to dist/ only
✅ TypeScript 5.8.x compatible - No more ng-packagr errors
✅ Faster builds - Custom builder is more efficient
✅ Standardized - All libraries use same build system


Testing Protocol

# 1. Clean everything
rm -rf dist/
find libs -name "*.js" -o -name "*.d.ts" -o -name "*.js.map" | xargs rm -f

# 2. Build a library
yarn nx build LIBRARY-NAME --skip-nx-cache

# 3. Verify NO pollution in source (should be empty)
find libs/LIBRARY-NAME/src -name "*.js" -o -name "*.d.ts" -o -name "*.js.map"

# 4. Verify output in dist (should have files)
ls -la dist/libs/LIBRARY-NAME/

# Expected: ZERO files in source, ALL files in dist

For New Libraries

When creating a new Angular library, use the custom builder from the start:

# After generating with nx
yarn nx generate @nx/angular:library my-new-lib

# Then update project.json to use custom builder
# (or use our migration script)
node tools/scripts/migrate-all-to-custom-builder.js

Troubleshooting

❌ Error: "File 'X' is not under 'rootDir'"

What you see:

File '/Users/you/monorepo/libs/storage-system/src/index.ts' 
is not under 'rootDir' '/Users/you/monorepo/libs/ux-panel/src'

What this means (in plain English):

You wrote this in your component:

// libs/ux-panel/src/lib/component.ts
import { StorageService } from '@storage-system';

TypeScript is saying: "Hey, that file is outside your library's folder! I'm not allowed to use it!"

Why it happens:

Your tsconfig.lib.json has:

{
  "compilerOptions": {
    "rootDir": "./src"  // ← Only allows files from libs/ux-panel/src/
  }
}

But @storage-system points to libs/storage-system/src/index.ts (different folder!)

The fix - Step by step:

  1. Open libs/ux-panel/tsconfig.lib.json
  2. Find the line with "rootDir": "./src"
  3. Replace it with:
    "rootDirs": [
      "./src",                    // ← Your library
      "../storage-system/src"     // ← Allow storage-system too
    ]
    

Complete example:

Before (causes error):

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "rootDir": "./src",              // ← Problem: only one folder allowed
    "outDir": "../../dist/libs/ux-panel"
  }
}

After (fixed):

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "rootDirs": [                    // ← Solution: multiple folders allowed
      "./src",
      "../storage-system/src"
    ],
    "outDir": "../../dist/libs/ux-panel"
  }
}

If you use multiple libraries:

Add ALL of them to the array:

{
  "rootDirs": [
    "./src",
    "../storage-system/src",
    "../theme-system/src",
    "../ux-icons/src"
  ]
}

❌ Error: "Cannot find module '@storage-system'"

What you see:

Cannot find module '@storage-system' or its corresponding type declarations.

What this means:

TypeScript doesn't know where @storage-system is located.

Common causes & fixes:

Cause 1: Wrong path in tsconfig.base.json

Check this file: tsconfig.base.json (at workspace root)

Look for:

{
  "paths": {
    "@storage-system": ["libs/storage-system/src/index.ts"]  // ← Does this file exist?
  }
}

Fix: Make sure the file path actually exists on your computer.

# Test if the file exists:
ls libs/storage-system/src/index.ts

# If it says "No such file or directory", the path is wrong!

Cause 2: Missing baseUrl

Check: libs/your-lib/tsconfig.lib.json

Make sure it has:

{
  "compilerOptions": {
    "baseUrl": "."  // ← This line is REQUIRED for paths to work!
  }
}

Without baseUrl, path mappings don't work at all.

Cause 3: Library not built yet (dist-based setup)

If tsconfig.base.json points to dist/:

{
  "paths": {
    "@storage-system": ["dist/libs/storage-system/index"]  // ← dist/ folder
  }
}

You must build the library first:

yarn nx build storage-system

Then check:

ls dist/libs/storage-system/index.d.ts  # Should exist now

Cause 4: IDE cache issue

Try these in order:

  1. Restart TypeScript Server:

    • VS Code/Cursor: Cmd+Shift+P → "TypeScript: Restart TS Server"
  2. Reload IDE:

    • Cursor: Cmd+R
    • VS Code: Cmd+Shift+P → "Developer: Reload Window"
  3. Full cache clear:

    # Stop IDE first, then:
    rm -rf node_modules/.cache
    yarn nx reset
    # Restart IDE
    

❌ Build succeeds but IDE shows red squiggles

What's happening:

Your code builds fine:

yarn nx build ux-panel  # ✅ Success!

But your IDE shows errors (red underlines).

Cause: IDE is using stale cache.

Fix:

  1. First try: Restart TS Server

    • Cmd+Shift+P → "TypeScript: Restart TS Server"
  2. If that doesn't work: Reload IDE

    • Cursor: Cmd+R
    • VS Code: Cmd+Shift+P → "Developer: Reload Window"
  3. Nuclear option:

    # Quit IDE completely
    # Then run:
    rm -rf ~/Library/Application\ Support/Cursor/User/workspaceStorage/*
    yarn nx reset
    # Reopen IDE
    

Pro tip: If build succeeds, trust the build. IDE errors are just visual noise from cache.


❌ Files appear in source after build

What you see:

After running yarn nx build my-lib, you find .js files inside libs/my-lib/src/:

libs/my-lib/src/
  ├── index.ts
  ├── index.js          ← This shouldn't be here!
  ├── index.d.ts        ← This shouldn't be here!
  └── lib/
      ├── component.ts
      └── component.js  ← This shouldn't be here!

What should happen:

ALL build output should go to dist/:

dist/libs/my-lib/
  ├── index.js          ← Build output belongs here
  ├── index.d.ts
  └── lib/
      └── component.js

Why it happens:

Your tsconfig.lib.json is misconfigured.

The fix:

Open libs/my-lib/tsconfig.lib.json and ensure:

{
  "compilerOptions": {
    "outDir": "../../dist/libs/my-lib",  // ← MUST point to dist/
    "rootDir": "./src",                  // ← OR use rootDirs
    "composite": false                   // ← MUST be false
  }
}

Then clean up:

# Remove the wrongly-placed files
node tools/scripts/cleanup-all-source-pollution.js

# Or manually:
find libs/ -name "*.js" -o -name "*.d.ts" -o -name "*.js.map" | xargs rm -f

🐌 TypeScript is slow with rootDirs

What's happening:

TypeScript is taking 10-30 seconds to check your code.

Why:

rootDirs makes TypeScript analyze files from multiple libraries:

{
  "rootDirs": [
    "./src",                    // ← Checks ~50 files
    "../storage-system/src",    // ← Checks ~200 files
    "../theme-system/src",      // ← Checks ~300 files
    "../ux-icons/src"           // ← Checks ~100 files
  ]
  // Total: ~650 files to analyze every time!
}

Solution 1: Only use rootDirs for active libraries

If you rarely edit @ux-icons, switch it to dist-based:

Remove from rootDirs:

{
  "rootDirs": [
    "./src",
    "../storage-system/src",    // ← Keep these (actively developed)
    "../theme-system/src"
    // Removed ux-icons
  ]
}

Override in paths:

{
  "compilerOptions": {
    "rootDirs": ["./src", "../storage-system/src"],
    "paths": {
      "@ux-icons": ["../../dist/libs/ux-icons/index"]  // ← Use dist for this one
    }
  }
}

Solution 2: Use skipLibCheck

Add to tsconfig.lib.json:

{
  "compilerOptions": {
    "skipLibCheck": true  // ← Skip type-checking of .d.ts files (much faster)
  }
}

Trade-off: You might miss some type errors in dependencies.


Last Updated: January 19, 2025
Status: ✅ Complete - All libraries using custom builder + rootDirs pattern documented
Maintenance: None needed - prevention-based solution