Configurations
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
NX Libraries Configuration Guide
📌 Note: All libraries use
src/index.tsas the standard entry point (we migrated frompublic-api.tsin 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:
- Incompatible with TypeScript 5.8.x (caused build failures)
- Could generate files in source directories despite proper
tsconfigsettings
Solution: Migrated ALL 18 Angular libraries to use our custom builder which:
- ✅ Forces output only to
dist/directory - ✅ Compatible with TypeScript 5.8.x
- ✅ Has built-in cleanup as safety net
- ✅ Handles SCSS compilation properly
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:
- ✅ Simple configuration
- ✅ TypeScript is faster (fewer files to analyze)
- ✅ Clear separation between source and compiled code
Cons:
- ❌ Must rebuild every time you change a dependency
- ❌ Slow feedback loop during development
- ❌ IDE "Go to Definition" shows compiled
.d.tsfiles, not source
🚀 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:
- ✅ Instant feedback - no rebuild needed
- ✅ IDE "Go to Definition" takes you to real source code
- ✅ Better debugging experience
- ✅ ESLint can enforce module boundaries
Cons:
- ❌ Must add every dependency to
rootDirsarray - ❌ TypeScript may be slightly slower (more files to check)
- ❌ Requires understanding of
rootDirsconcept
🤔 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:
- Most of our libraries are actively developed together
- Hot-reload saves hours per day
- Better developer experience >> slight configuration cost
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:
- When you import
@storage-system, TypeScript looks atlibs/storage-system/src/index.ts - When you import
@ux-panel, TypeScript looks atlibs/ux-panel/src/index.ts - And so on...
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:
- Imports resolve to TypeScript source files (
.ts) - Requires
rootDirsin consuming libraries - Enables hot-reload
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:
- Imports resolve to compiled JavaScript (
.js) and types (.d.ts) - Simpler config (no
rootDirsneeded) - Must rebuild after changes
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:
storage-systemux-panel(usesstorage-system)theme-system
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:
- ✅ Change code in
storage-system→ See changes inux-panelinstantly - ✅ No rebuild needed
- ❌ Must maintain
rootDirsarray
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:
- ✅ Simple config (no
rootDirsneeded) - ❌ Change code in
storage-system→ Must runyarn nx build storage-system - ❌ Slower feedback loop
Migration Summary
Libraries Migrated (18 total):
Root Level (15):
- ux-ui-forms
- ux-portal
- ux-panel
- ux-loading
- ux-icons
- ux-icon-finder
- svg-connections
- prompt-system
- mock-api
- flash-animations
- dev-tools
- cosmic-backgrounds
- component-library
- catalyst-ui-kit
- auth
Nested in theme-system (3):
- theme-dev-tools
- theme-runtime
- theme-styles
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:
- Open
libs/ux-panel/tsconfig.lib.json - Find the line with
"rootDir": "./src" - 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:
Restart TypeScript Server:
- VS Code/Cursor:
Cmd+Shift+P→ "TypeScript: Restart TS Server"
- VS Code/Cursor:
Reload IDE:
- Cursor:
Cmd+R - VS Code:
Cmd+Shift+P→ "Developer: Reload Window"
- Cursor:
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:
First try: Restart TS Server
Cmd+Shift+P→ "TypeScript: Restart TS Server"
If that doesn't work: Reload IDE
- Cursor:
Cmd+R - VS Code:
Cmd+Shift+P→ "Developer: Reload Window"
- Cursor:
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