Imports Exports
Documentation for Developers
🔧 NX Library Imports & Exports Guide
🚨 CRITICAL RULE: No Local Types Policy
NEVER define types/interfaces locally in service or component files!
✅ ALWAYS Use Models Directory Pattern:
// ✅ GOOD: All types in models directory
// libs/my-library/src/lib/models/my-types.ts
export interface MyConfig { ... }
// libs/my-library/src/lib/services/my-service.ts
import { MyConfig } from '../models'; // ← Import from models
❌ NEVER Define Local Types:
// ❌ BAD: Local type definitions
interface MyConfig { ... } // ← NEVER do this!
export class MyService { ... }
Problem: Module Resolution Issues with NX Library Aliases
When working with NX monorepo libraries, you may encounter TypeScript errors like:
Cannot find module '@core' or its corresponding type declarations.
This typically happens when:
- The library hasn't been built recently
- TypeScript language server hasn't refreshed module resolution
- There are path mapping issues in the specific app's configuration
✅ Solution: Direct Library Path Imports
Instead of using the configured aliases (which may fail in certain contexts), use direct imports from the library source:
❌ Problematic Import (may fail)
import type { LoadingOverlayConfig } from '@libs-foundational/core-models';
✅ Working Import (direct path)
import type { LoadingOverlayConfig } from 'libs/core-models/src/lib/models/loading.types';
🎯 Path Format Guidelines
Use this format for NX library imports:
import { SomeType } from 'libs/[library-name]/src/lib/[path-to-file]';
Examples:
// Core types
import type { LoadingOverlayConfig } from 'libs/core-models/src/lib/models/loading.types';
// UX components
import { UxComponent } from 'libs/ux-ui-extended/src/lib/components/ux-component';
// Shared utilities
import { UtilFunction } from 'libs/shared-ui/src/lib/utils/util-function';
🔧 Troubleshooting Steps
If you encounter module resolution issues:
- Try direct import first (as shown above)
- Clean and rebuild the library:
nx clean nx build [library-name] --skip-nx-cache - Restart TypeScript language server in your IDE
- Check tsconfig.base.json for correct path mappings
🏗️ When to Use Each Approach
Use Direct Paths (libs/...) when:
- Encountering module resolution errors
- Working in new apps or components
- Need guaranteed TypeScript compilation
- The alias approach fails after troubleshooting
Use Aliases (@library-name) only when the direct path fails:
- The library is properly built and cached
- Working in apps that have been tested with the alias
- Following established patterns in the codebase
📝 Best Practices
- Consistency: Once you choose an import style for a file, stick with it
- Avoid Relative Paths: Never use
../../../libs/...- uselibs/...instead - Type-Only Imports: Use
import typefor TypeScript types when possible - Document Workarounds: If you need direct imports, document why the alias failed
🎯 Example: Fixed Import in docs-angular
// Before (failing)
import type { LoadingOverlayConfig } from '@libs-foundational/core-models';
// After (working)
import type { LoadingOverlayConfig } from 'libs/core-models/src/lib/models/loading.types';
This approach ensures reliable imports and avoids the TypeScript module resolution issues that can occur with NX library aliases.
🔧 Circular Dependency Resolution
Recent Success Case: Theme System + Mock API
We successfully resolved circular dependencies between theme-runtime and mock-api libraries by applying direct imports:
Problem:
theme-runtime → imports @mock-api → imports @storage-system → imports theme-runtime
Solution:
// ✅ All circular import chains broken with direct paths
import { StorageService } from 'libs/storage-system/src/angular/storage.service';
import { MockInterceptorUtilsService } from 'libs/mock-api/src/lib/core/interceptor';
Result: ✅ HTTP + Mock API + Intelligent Storage foundation working perfectly
Key Lessons:
- NX aliases can create unexpected circular dependencies between libraries
- Direct imports break cycles while maintaining functionality
- Direct imports are the reliable solution for cross-library dependencies
- Apply direct imports proactively when working with complex inter-library patterns
This pattern should be used for any inter-library imports that may create cycles.
🚨 HISTORICAL: Nested Library Build Issues (October 2024)
Problem: ng-packagr Cannot Resolve Cross-Library Imports in Nested Folders
Note: This issue has been largely resolved through our storage library consolidation. The old nested structure (e.g., libs/storage/storage-angular) no longer exists.
When building Angular libraries located in nested folders, ng-packagr-lite may fail to resolve cross-library imports even when properly configured.
Symptoms:
Cannot find module '@ux-portal' or its corresponding type declarations.
Cannot find module '@storage-system' or its corresponding type declarations.
Root Cause:
ng-packagrhas limitations resolving path aliases to unpublished libraries- Nested folder structure compounds the issue with the custom build script
- Theme-system libraries work because they have simpler dependency chains
✅ Solution 1: Use Custom Build Script (Recommended for Complex Dependencies)
For libraries with cross-library Angular imports, use the custom build script:
{
"targets": {
"build": {
"executor": "nx:run-commands",
"options": {
"command": "node tools/scripts/build-angular-library.js storage-system"
},
"outputs": ["{workspaceRoot}/dist/libs/storage-system"]
}
}
}
Key Points:
- For nested libraries, pass the full path:
storage-system(not juststorage-system) - Use direct imports in your code:
libs/ux-portal/src/index(not@ux-portal) - Works with complex dependency chains
✅ Solution 2: Keep Using ng-packagr (If Simple Dependencies)
If your library has no cross-library Angular imports, ng-packagr-lite works fine:
{
"targets": {
"build": {
"executor": "@nx/angular:ng-packagr-lite",
"options": {
"project": "libs/theme-system/theme-runtime/ng-package.json"
}
}
}
}
Example Working Libraries:
theme-runtime,theme-styles,theme-dev-tools(nested, use ng-packagr successfully)
Import Strategy for Nested Libraries
// ❌ May fail during ng-packagr build
import { UxPortalService } from '@libs-ux/ux-portal';
import { SyncChange } from '@storage-system';
// ✅ Always works with custom build script
import { UxPortalService } from 'libs/ux-portal/src';
import { SyncChange } from 'libs/storage-system/src';
import { GgStorage } from 'libs/storage-system/src';
Decision Tree: Which Build Approach?
Does your library import from other Angular libraries?
├── YES → Use custom build script + direct imports
│ └── Nested? Pass full path: folder/library-name
└── NO → Use ng-packagr-lite
└── Standard NX library structure works
🔄 Recent Library Consolidation (2024)
Clipboard & Toast Services Consolidated
We recently consolidated duplicate services to improve the codebase architecture:
Before (Duplicated):
libs/ux-ui-extended/src/lib/services/ux-clipboard.service.ts❌libs/ux-ui-extended/src/lib/services/ux-toast.service.ts❌
After (Consolidated):
libs/ux-ui-core/src/lib/clipboard/clipboard.service.ts✅libs/ux-ui-feedback/src/lib/toast/services/simple-toast.service.ts✅
Updated Import Patterns:
// ✅ Correct imports after consolidation
import { UxClipboardService, UxClipboardDirective } from 'libs/ux-ui-core/src/lib/clipboard';
import { UxToastService } from 'libs/ux-ui-feedback/src/lib/toast';
// ❌ Old imports (no longer work)
import { UxClipboardService } from '@ux-ui-extended'; // Removed
import { UxToastService } from '@ux-ui-extended'; // Removed
Key Benefits:
- ✅ Single Source of Truth: No more duplicate services
- ✅ Better Architecture: Services in appropriate libraries
- ✅ Cleaner Dependencies: No circular dependencies
- ✅ Easier Maintenance: One place to update functionality
🚨 Angular Component & Directive Import Issues
Problem: "Can't bind to 'directiveName' since it isn't a known property"
This specific error occurs when Angular directives (like UxClipboardDirective) can't be resolved through barrel exports.
❌ Symptom:
Can't bind to 'uxClipboard' since it isn't a known property of 'p'.
✅ Solution: Use Direct Directive Import
// ✅ Use barrel file or direct path when possible
import { UxClipboardDirective } from 'libs/ux-ui-core/src/lib';
🔧 UX-UI-Feedback Library Import Issues (2024)
Problem: "@ux-ui-feedback" Module Resolution Failures
The @ux-ui-feedback library was experiencing import resolution issues despite being properly configured in tsconfig.base.json.
❌ Symptom:
Cannot find module '@ux-ui-feedback' or its corresponding type declarations.
✅ Solution: Use Direct Import Path
Note: Both alias imports and direct import paths are valid approaches in our codebase.
// ❌ Failing alias import
import { ToastService, ToastPosition, ToastType, ToastConfig } from '@ux-ui-feedback';
// ✅ Working direct import
import { ToastService, ToastPosition, ToastType, ToastConfig } from 'libs/ux-ui-feedback/src/index';
Root Cause Analysis:
- Library Build Status: The library was properly built and exported
- Path Mapping:
tsconfig.base.jsonhad correct alias configuration - TypeScript Resolution: Direct imports bypassed module resolution issues
- Barrel Export Structure: The
src/index.tsproperly exports all services and types
Key Lessons:
- Direct imports are more reliable for complex libraries with multiple exports
- Always build the library first before troubleshooting imports
- Use
src/index.tsas the import target for consistent exports - Check both alias and direct imports when troubleshooting
- ⚠️ Note: We've migrated from
public-api.tstoindex.tsfor uniformity
Problem: Event Handler Type Mismatches
Angular 20's strict typing may cause event handler errors even when component signatures are correct.
❌ Symptom:
Argument of type 'Event' is not assignable to parameter of type 'string'.
Argument of type 'Event' is not assignable to parameter of type 'number'.
✅ Solution: Use $any() Type Assertion in Template
<!-- ❌ May cause type errors -->
<ux-tabs (selectedIndexChange)="updateTab($event)">
<ux-search (search)="onSearch($event)">
<!-- ✅ Works with strict typing -->
<ux-tabs (selectedIndexChange)="updateTab($any($event))">
<ux-search (search)="onSearch($any($event))">
🔍 Angular-Specific Troubleshooting Checklist
When encountering Angular import/binding issues:
Check Component Imports Array:
@Component({ imports: [UxClipboardDirective], // ← Must be included })Try Direct Import Path:
import { DirectiveName } from 'libs/library-name/src/lib/directives/directive-name.directive';Add $any() for Event Type Issues:
(eventName)="handler($any($event))"Verify Barrel Export Structure: Check if
libs/library-name/src/lib/index.tsproperly exports the directive:export * from './directives'; export { UxClipboardDirective } from './directives/ux-clipboard.directive'; // Explicit exportCheck for Missing Dependencies: Some directives require additional components (e.g.,
UxClipboardDirectiveneedsUxToastComponentin the app root).
🎯 Template Access Import Strategy
Problem: Alias Imports Don't Work for Template Access
When components need to access HTML templates or have complex barrel export structures, alias imports may fail even when the library is properly configured.
❌ Alias Import (May Fail)
// This may not work for template access
import { UxButtonComponent } from '@ux-ui-core';
✅ Direct Path Import (Reliable)
// This always works for template access
import { UxButtonComponent } from 'libs/ux-ui-core/src/index';
When to Use Each Strategy
Use Alias Imports (@library-name) when:
- ✅ Working with simple service imports
- ✅ Library has clean barrel exports
- ✅ No template access required
- ✅ Library is properly built and cached
// ✅ Good for services
import { StorageService } from '@storage-system';
import { ThemeManagerService } from '@theme-runtime';
Use Direct Path Imports (libs/...) when:
- ✅ Components need template access
- ✅ Complex barrel export structures
- ✅ Alias imports fail after troubleshooting
- ✅ Working with Angular components that have templates
// ✅ Required for template access
import { UxButtonComponent } from 'libs/ux-ui-core/src/index';
import { UxCardComponent } from 'libs/ux-ui-core/src/index';
import { CosmicSpaceBackgroundComponent } from 'libs/cosmic-backgrounds/src/index';
Template Access Pattern
Components that need to render templates should always use direct imports:
// ✅ Landing component with template access
import { UxButtonComponent } from 'libs/ux-ui-core/src/index';
import { UxCardComponent } from 'libs/ux-ui-core/src/index';
@Component({
imports: [UxButtonComponent, UxCardComponent], // Direct imports work
template: `
<ux-card>
<ux-button>Click me</ux-button>
</ux-card>
`
})
export class LandingComponent {}
Barrel vs Direct Import Decision Tree
Need template access?
├── YES → Use direct path (libs/library/src/index)
└── NO → Try alias first (@library-name)
├── Works → Use alias
└── Fails → Fallback to direct path
📋 Quick Reference: Common Import Patterns
Directives (Always use direct imports if barrel fails):
import { UxClipboardDirective } from 'libs/ux-ui-core/src/lib/clipboard/clipboard.directive';
import { UxTooltipDirective } from 'libs/ux-ui-extended/src/lib/directives/ux-tooltip.directive';
Components (Try alias first, fallback to direct):
// Direct path (recommended)
import { UxButtonComponent } from 'libs/ux-ui-core/src/lib/components/button/button.component';
Services (Use direct paths):
import { UxClipboardService } from 'libs/ux-ui-core/src/lib/clipboard';
import { UxToastService } from 'libs/ux-ui-feedback/src/lib/toast';
Types (Always prefer direct imports):
import type { SafeAny } from 'libs/core-models/src/lib/types/safe-any.type';