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:

✅ 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:

  1. Try direct import first (as shown above)
  2. Clean and rebuild the library:
    nx clean
    nx build [library-name] --skip-nx-cache
    
  3. Restart TypeScript language server in your IDE
  4. Check tsconfig.base.json for correct path mappings

🏗️ When to Use Each Approach

Use Direct Paths (libs/...) when:

Use Aliases (@library-name) only when the direct path fails:

📝 Best Practices

  1. Consistency: Once you choose an import style for a file, stick with it
  2. Avoid Relative Paths: Never use ../../../libs/... - use libs/... instead
  3. Type-Only Imports: Use import type for TypeScript types when possible
  4. 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:

  1. NX aliases can create unexpected circular dependencies between libraries
  2. Direct imports break cycles while maintaining functionality
  3. Direct imports are the reliable solution for cross-library dependencies
  4. 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:

✅ 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:

✅ 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:

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):

After (Consolidated):

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:

🚨 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:

  1. Library Build Status: The library was properly built and exported
  2. Path Mapping: tsconfig.base.json had correct alias configuration
  3. TypeScript Resolution: Direct imports bypassed module resolution issues
  4. Barrel Export Structure: The src/index.ts properly exports all services and types

Key Lessons:

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:

  1. Check Component Imports Array:

    @Component({
      imports: [UxClipboardDirective], // ← Must be included
    })
    
  2. Try Direct Import Path:

    import { DirectiveName } from 'libs/library-name/src/lib/directives/directive-name.directive';
    
  3. Add $any() for Event Type Issues:

    (eventName)="handler($any($event))"
    
  4. Verify Barrel Export Structure: Check if libs/library-name/src/lib/index.ts properly exports the directive:

    export * from './directives';
    export { UxClipboardDirective } from './directives/ux-clipboard.directive'; // Explicit export
    
  5. Check for Missing Dependencies: Some directives require additional components (e.g., UxClipboardDirective needs UxToastComponent in 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:

// ✅ Good for services
import { StorageService } from '@storage-system';
import { ThemeManagerService } from '@theme-runtime';

Use Direct Path Imports (libs/...) when:

// ✅ 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';