Apps Imports

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

App Imports Guide

Complete guide to import strategies, path aliases, barrel exports, and Angular static analysis requirements for NX applications.

Import Strategy Overview

In our NX monorepo, apps import libraries using path aliases that resolve to source code, not built distributions. Angular's build system handles bundling automatically.

// ✅ Correct - Import from alias (resolves to source)
import { UxPortalService } from '@libs-ux/ux-portal';
import { StorageService } from '@storage-system';

// ❌ Wrong - Don't import from dist
import { UxPortalService } from '../../dist/libs/ux-portal';

// ❌ Wrong - Don't use relative paths across libraries
import { StorageService } from '../../libs/storage-system/src/lib/storage.service';

Path Alias Configuration

Workspace-Level Aliases (tsconfig.base.json)

Define all library aliases at the workspace root:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      // Root alias - imports from barrel (index.ts)
      "@ux-portal": ["libs/ux-portal/src/index"],
      
      // Wildcard alias - allows deep imports
      "@ux-portal/*": ["libs/ux-portal/src/*"],
      
      "@storage-system": ["libs/storage-system/src/index"],
      "@storage-system/*": ["libs/storage-system/src/*"],
      
      "@ux-ui-core": ["libs/ux-ui-core/src/index"],
      "@ux-ui-core/*": ["libs/ux-ui-core/src/*"]
    }
  }
}

Why both aliases?

App Configuration

Apps inherit these aliases through extends:

// apps/my-app/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  // Paths are automatically available - don't redefine them!
}

Library Barrel Exports

Standard Barrel Pattern (src/index.ts)

Each library should export its public API from src/index.ts:

// libs/ux-portal/src/index.ts

// Services
export { UxPortalService } from './lib/services/portal.service';
export { UxPortalConfig } from './lib/models/portal-config';

// Components (for static analysis)
export { UxModalComponent } from './lib/components/modal/modal.component';
export { UxDrawerComponent } from './lib/components/drawer/drawer.component';

// Types
export * from './lib/models';

Guidelines:

Angular Static Analysis

The Challenge

Angular's compiler requires imports in @Component({ imports: [...] }) to be statically analyzable. This means Angular must determine the exact component class at compile time.

Barrel Imports (Services, Utilities)

Use barrel imports for services, utilities, and models:

// ✅ Perfect for services
import { UxPortalService, UxPortalConfig } from '@libs-ux/ux-portal';
import { StorageService } from '@storage-system';

@Component({
  // Services work fine from barrels
})
export class MyComponent {
  private portal = inject(UxPortalService);
  private storage = inject(StorageService);
}

Component Imports (Static Analysis)

Two approaches for standalone components:

Approach 1: Barrel Import (If Exported Properly)

// ✅ Works if component is exported from barrel
import { UxTabsComponent, UxTabComponent } from '@ux-ui-core';

@Component({
  standalone: true,
  imports: [UxTabsComponent, UxTabComponent]
})
export class MyComponent {}

Approach 2: Deep Import (Always Works)

// ✅ Always works - direct file import
import { UxTabsComponent } from '@ux-ui-core/lib/tabs/tabs.component';
import { UxTabComponent } from '@ux-ui-core/lib/tabs/tab.component';

@Component({
  standalone: true,
  imports: [UxTabsComponent, UxTabComponent]
})
export class MyComponent {}

When to use which:

Import Patterns by Use Case

Services and Dependency Injection

// Always use barrel imports
import { ThemeService } from '@theme-system';
import { StorageService } from '@storage-system';
import { LogService } from '@log-system';

@Component({
  // ...
})
export class MyComponent {
  private theme = inject(ThemeService);
  private storage = inject(StorageService);
  private logger = inject(LogService);
}

Standalone Components

// Option 1: Barrel (if available)
import { UxButtonComponent, UxCardComponent } from '@ux-ui-core';

// Option 2: Deep (always works)
import { UxButtonComponent } from '@ux-ui-core/lib/button/button.component';
import { UxCardComponent } from '@ux-ui-core/lib/card/card.component';

@Component({
  standalone: true,
  imports: [UxButtonComponent, UxCardComponent]
})
export class MyComponent {}

Types and Interfaces

// Use barrel imports for types
import type { SafeAny, DeepPartial } from '@libs-foundational/core-models';
import type { ThemeConfig, ThemeMode } from '@theme-system';
import type { StorageOptions } from '@storage-system';

// Type-only imports for optimization
import type { ComponentType } from '@angular/cdk/portal';

Utilities and Helper Functions

// Barrel imports work perfectly
import { deepClone, isEqual } from '@utilities';
import { formatDate, parseDate } from '@date-utils';
import { validateEmail, sanitizeInput } from '@validators';

Common Import Errors

❌ "Cannot find module '@library'"

Causes:

  1. Library not built
  2. Alias not in tsconfig.base.json
  3. Missing src/**/*.ts in app's tsconfig.app.json

Solutions:

# 1. Build the library
npx nx build library-name

# 2. Add to tsconfig.base.json
{
  "paths": {
    "@library-name": ["libs/library-name/src/index"],
    "@library-name/*": ["libs/library-name/src/*"]
  }
}

# 3. Update tsconfig.app.json
{
  "include": ["src/**/*.ts", "src/**/*.d.ts"]
}

# 4. Restart TypeScript server

❌ "'imports' must be an array... Value could not be determined statically"

Cause: Angular can't analyze the component import.

Solution: Use deep import:

// ❌ Might fail if barrel is complex
import { MyComponent } from '@my-lib';

// ✅ Always works
import { MyComponent } from '@my-lib/lib/my-component/my.component';

❌ "Circular dependency detected"

Cause: Barrel re-exports creating loops.

Solution: Use direct imports or fix barrel structure:

// ❌ Circular through barrel
import { ServiceA } from '@my-lib';
import { ServiceB } from '@my-lib';

// ✅ Direct imports break the cycle
import { ServiceA } from '@my-lib/lib/service-a';
import { ServiceB } from '@my-lib/lib/service-b';

Adding New Library Imports

Checklist

When adding a new library to import from:

  1. Add path aliases to tsconfig.base.json:
{
  "@new-lib": ["libs/new-lib/src/index"],
  "@new-lib/*": ["libs/new-lib/src/*"]
}
  1. Create barrel export (libs/new-lib/src/index.ts):
export { MyService } from './lib/my.service';
export { MyComponent } from './lib/my.component';
  1. Build the library:
npx nx build new-lib
  1. Import in your app:
import { MyService } from '@new-lib';
  1. Restart TypeScript server in your IDE

Best Practices

DO ✅

DON'T ❌

Import Organization

Recommended Import Order

// 1. Angular core
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';

// 2. Third-party libraries
import { Observable } from 'rxjs';
import { map, filter } from 'rxjs/operators';

// 3. Workspace libraries (alphabetical)
import { LogService } from '@log-system';
import { StorageService } from '@storage-system';
import { ThemeService } from '@theme-system';

// 4. Workspace UI components (alphabetical)
import { UxButtonComponent } from '@ux-ui-core';
import { UxCardComponent } from '@ux-ui-core';

// 5. Local imports (relative paths)
import { MyLocalService } from './services/my-local.service';
import type { MyLocalType } from './models/my-local.model';

@Component({
  // ...
})
export class MyComponent {}

TypeScript Import Syntax

Named Imports

// Multiple named exports
import { ServiceA, ServiceB, TypeC } from '@my-lib';

// Renamed imports
import { ServiceA as MyServiceA } from '@my-lib';

Type-Only Imports

// Type-only (better tree-shaking)
import type { MyType, MyInterface } from '@my-lib';

// Mixed (not recommended)
import { MyService, type MyType } from '@my-lib';

Namespace Imports

// Import entire namespace (use sparingly)
import * as MyLib from '@my-lib';

// Usage
const service = new MyLib.MyService();

Related Documentation


Key Takeaway: Apps import from library source code (libs/...) using path aliases, and Angular's build system handles everything else automatically!