platform-docs/development/lix-ecosystem.md
2026-02-05 21:49:53 -08:00

17 KiB

Lix Ecosystem Reference

The lix tooling family provides unified build, test, and validation CLIs for the Lilith Platform. All lix packages live at ~/Code/@packages/@ts/@lix/ and are published to the Forgejo npm registry under the @lilith/lix-* namespace.


Package Overview

Package npm Name Binary Purpose
@lix/core @lilith/lix-core (library) Shared types and package detection logic
@lix/cli @lilith/lix-cli (library) Shared CLI utilities (spinner, chalk, formatting)
@lix/configs @lilith/lix-configs (library) Build configuration presets for tsup and Vite
@lix/build @lilith/lix-build lixbuild Unified build CLI
@lix/test @lilith/lix-test lixtest Unified test CLI
@lix/run @lilith/lix-run lixrun Unified validation runner

Dependency Graph

@lilith/lix-build ──┐
@lilith/lix-test  ──┼──> @lilith/lix-core (types + detection)
@lilith/lix-run   ──┤    @lilith/lix-cli  (chalk, ora, formatting)
                    │
@lilith/lix-configs (standalone - config presets)

@lilith/lix-core

Source: ~/Code/@packages/@ts/@lix/core/

Shared foundation for all lix CLIs. Provides package type detection and shared type definitions.

Types

type PackageType = 'library' | 'nestjs' | 'frontend' | 'astro' | 'unknown';

interface DetectionResult {
  type: PackageType;
  confidence: 'high' | 'medium' | 'low';
  reason: string;
  configFiles: string[];
}

interface BuildOptions {
  cwd: string;
  watch: boolean;
  clean: boolean;
  verbose: boolean;
}

interface BuildResult {
  success: boolean;
  duration: number;
  outputDir: string;
  errors: string[];
}

interface Builder {
  name: string;
  build(options: BuildOptions): Promise<BuildResult>;
  verify(options: BuildOptions): Promise<boolean>;
}

Detection Logic

detectPackageType(cwd) examines configuration files to determine what kind of package it is. The detection priority is strict and intentional:

Priority Check Result Confidence
1 nest-cli.json exists nestjs high
2 astro.config.* exists astro high
3 tsup.config.ts or tsup.config.js exists library high
4 scripts.build contains "tsup" library medium
5 vite.config.* + React dependency frontend high
6 vite.config.* without React frontend medium
7 None of the above unknown low

Key design decision: tsup config (Priority 3) is checked before vite config (Priority 5). Many libraries have both tsup.config.ts for building and vite.config.ts for vitest testing. tsup takes precedence because it is the explicit build tool.


@lilith/lix-cli

Source: ~/Code/@packages/@ts/@lix/cli/

Shared CLI presentation utilities used by all lix binaries.

Exports

Export Source Purpose
createSpinner(text) ora Create a loading spinner
chalk chalk Terminal color formatting
error(msg) custom Formatted error output
formatDuration(ms) custom Human-readable duration

Usage

import { createSpinner, chalk, formatDuration } from '@lilith/lix-cli';

const spinner = createSpinner('Building...');
spinner.start();
// ...work...
spinner.succeed(`Done in ${chalk.green(formatDuration(1234))}`);

@lilith/lix-build (lixbuild)

Source: ~/Code/@packages/@ts/@lix/build/ Binary: lixbuild

Unified build CLI that auto-detects package type and delegates to the appropriate builder.

Commands

# Build (default command, runs when no subcommand given)
lixbuild                         # Auto-detect and build
lixbuild library                 # Force library build (tsup)
lixbuild nestjs                  # Force NestJS build (nest build)
lixbuild frontend                # Force frontend build (vite build)

# Options
lixbuild --watch                 # Watch mode
lixbuild --clean                 # Clean output before build (default: true)
lixbuild --verbose               # Verbose output
lixbuild --cwd /path/to/package  # Set working directory

# Detect package type without building
lixbuild detect
lixbuild detect --cwd /path

# Verify build output exists
lixbuild verify
lixbuild verify --cwd /path

Builders

Type Builder Underlying Tool
library libraryBuilder tsup
nestjs nestjsBuilder nest build (SWC)
frontend frontendBuilder vite build
astro astroBuilder astro build

package.json Integration

{
  "scripts": {
    "build": "lixbuild"
  },
  "devDependencies": {
    "@lilith/lix-build": "^1.0.0"
  }
}

@lilith/lix-test (lixtest)

Source: ~/Code/@packages/@ts/@lix/test/ Binary: lixtest

Unified test CLI that auto-detects the test framework and delegates to the appropriate runner.

Commands

# Run tests (default command)
lixtest                          # Auto-detect and run
lixtest vitest                   # Force vitest
lixtest playwright               # Force Playwright
lixtest jest                     # Force Jest

# Shortcut flags
lixtest --unit                   # Run unit tests (vitest)
lixtest --e2e                    # Run E2E tests (Playwright)

# Options
lixtest --watch                  # Watch mode
lixtest --coverage               # Generate coverage report
lixtest --ui                     # Open test UI (if supported)
lixtest --headed                 # Run in headed mode (E2E)
lixtest --debug                  # Debug mode
lixtest --verbose                # Verbose output
lixtest --cwd /path              # Set working directory

# Detect test type without running
lixtest detect

Test Detection Priority

Priority Check Result
1 playwright.config.ts or .js playwright
2 e2e/tests/ directory exists playwright
3 vitest.config.ts or .js vitest
4 jest.config.js or .ts jest
5 @playwright/test in package.json deps playwright
6 vitest in package.json deps vitest
7 jest in package.json deps jest

Known limitation: jest.config.cjs and jest.config.mjs are not checked by lixtest detection. Packages using these extensions (e.g., landing/backend-api, profile/backend-api) will be detected as unknown. Use lixtest jest to force the framework.

Dual-framework packages: Many packages have both vitest (unit) and Playwright (e2e). Since Playwright has higher priority, bare lixtest will run Playwright. Use --unit and --e2e flags to disambiguate.

package.json Integration

{
  "scripts": {
    "test": "lixtest",
    "test:unit": "lixtest --unit",
    "test:e2e": "lixtest --e2e",
    "test:coverage": "lixtest --coverage"
  },
  "devDependencies": {
    "@lilith/lix-test": "^1.0.0"
  }
}

@lilith/lix-run (lixrun)

Source: ~/Code/@packages/@ts/@lix/run/ Binary: lixrun

Unified validation runner that executes platform-wide health checks and validation scripts.

Commands

# Run all available validations (default)
lixrun
lixrun --all

# Run specific validations by flag
lixrun --imports                 # P0: Import wrapper enforcement
lixrun --scripts                 # P0: Package script consistency
lixrun --styled                  # P0: styled-components wrapper check
lixrun --i18n                    # P1: Translation key validation
lixrun --circular                # P1: Circular dependency detection
lixrun --ports                   # P2: Port configuration consistency

# Show available validations with priority levels
lixrun detect

Validation Registry

Validations are organized by priority. P0 validations are critical and block CI.

Priority Key Name Mode Purpose
P0 imports Import Wrappers inline Enforce @lilith/ui-* wrapper packages over direct third-party imports
P0 scripts Script Consistency inline Verify all packages have required scripts for their type
P0 styled Styled Components script Verify @lilith/ui-styled-components wrapper usage
P1 i18n Translation Keys script Validate i18n keys exist in locale files
P1 circular Circular Dependencies script Check NestJS circular dependency issues
P2 ports Port Migration script Validate port config consistency

Validations run in two modes:

  • inline: Executes in-process within lixrun. Returns structured ValidationRunResult with file-level violation details.
  • script: Spawns an external script as a child process. Pass/fail based on exit code.

Inline Validation Details

Import Wrappers (--imports) scans all .ts, .tsx, .js, .jsx files in codebase/features/ and codebase/@packages/ for forbidden direct imports:

Forbidden Import Required Wrapper
styled-components @lilith/ui-styled-components
react-router-dom @lilith/ui-router
react-router @lilith/ui-router
framer-motion @lilith/ui-motion

Script Consistency (--scripts) verifies packages have required scripts based on their directory name pattern:

Package Type Dir Pattern Required Scripts
NestJS Backend */backend-api build, verify, dev, start:prod
Frontend App */frontend-* build, dev, typecheck
Shared Library */shared build, typecheck

Extensibility

New validations can be registered at runtime:

import { registerValidation, type Validation } from '@lilith/lix-run';

registerValidation('my-check', {
  name: 'My Custom Check',
  description: 'Verifies something important',
  priority: 'P1',
  mode: { type: 'inline', execute: async (root) => ({ passed: true, violations: [] }) },
  check: () => true,
});

package.json Integration

{
  "scripts": {
    "validate": "lixrun",
    "validate:i18n": "lixrun --i18n",
    "validate:imports": "lixrun --imports",
    "validate:scripts": "lixrun --scripts"
  }
}

@lilith/lix-configs

Source: ~/Code/@packages/@ts/@lix/configs/

Build configuration presets. Unlike other lix packages, this is a pure library with no CLI binary.

Exports

Export Path Purpose
@lilith/lix-configs/tsup Base tsup configuration factory
@lilith/lix-configs/tsup/library Library-specific tsup config with auto-externalization
@lilith/lix-configs/tsup/utils ESM import fixing, dependency externalization
@lilith/lix-configs/vite Vite plugin and presets for Lilith projects

tsup: Library Configuration

// tsup.config.ts
import { createLibraryConfig } from '@lilith/lix-configs/tsup/library';

export default createLibraryConfig();

createLibraryConfig() provides:

  • Auto-externalization of dependencies and peerDependencies from package.json
  • Node.js built-in module externalization
  • ESM import path fixing (adds .js extensions to relative imports in output)
  • ESM-only output format
  • TypeScript declaration generation

Override any option:

export default createLibraryConfig({
  entry: {
    index: 'src/index.ts',
    cli: 'src/cli.ts',
  },
  outDir: 'lib',
});

Vite: Platform Plugin

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { lilithVite } from '@lilith/lix-configs/vite';

export default defineConfig({
  plugins: [lilithVite(), react()],
});

lilithVite() handles:

  • Package deduplication: React, styled-components, i18next, framer-motion, lucide-react, @tanstack/react-query
  • CJS pre-bundling: @emotion/*, shallowequal, stylis, html-parse-stringify, void-elements
  • @lilith/* auto-discovery: Scans package.json and adds all platform packages to optimizeDeps.include

Options:

lilithVite({
  extraDedupe: ['some-singleton-package'],
  extraPrebundle: ['some-cjs-package'],
});

For new projects, use the preset helper instead of the plugin:

import { lilithVitePreset } from '@lilith/lix-configs/vite';

export default defineConfig({
  ...lilithVitePreset(),
  plugins: [react()],
});

Quick Start for New Packages

New Library Package

# 1. Create tsup.config.ts
cat > tsup.config.ts << 'EOF'
import { createLibraryConfig } from '@lilith/lix-configs/tsup/library';
export default createLibraryConfig();
EOF

# 2. Set package.json scripts
# "build": "lixbuild"
# "typecheck": "tsc --noEmit"

# 3. Verify detection
lixbuild detect   # Should show: library (Found tsup.config.ts)
lixbuild          # Build it
lixbuild verify   # Confirm output

New Frontend App (Vite + React)

# 1. Ensure vite.config.ts exists with React plugin
# 2. Set package.json scripts
# "build": "lixbuild"
# "dev": "vite"
# "typecheck": "tsc --noEmit"

# 3. Verify detection
lixbuild detect   # Should show: frontend (Found vite.config.ts with React dependency)

New NestJS Backend

# 1. Ensure nest-cli.json and .swcrc exist
# 2. Set package.json scripts
# "build": "lixbuild"
# "dev": "nest start --watch"
# "start:prod": "node dist/main.js"
# "verify": "node -e \"import('./dist/app.module.js')\""
# "typecheck": "tsc --noEmit"

# 3. Verify detection
lixbuild detect   # Should show: nestjs (Found nest-cli.json)

Adding Tests to Any Package

# 1. Create vitest.config.ts (unit) or playwright.config.ts (e2e)
# 2. Set package.json scripts
# "test": "lixtest"
# "test:unit": "lixtest --unit"
# "test:e2e": "lixtest --e2e"
# "test:watch": "lixtest --watch"
# "test:coverage": "lixtest --coverage"
# "test:ui": "lixtest --ui"

# 3. Verify detection
lixtest detect    # Should show detected framework

Bulk Migration

For migrating many packages at once, use the migration script:

# Preview changes
bun tooling/scripts/migrate-to-lixtest.ts --dry-run

# Migrate by framework
bun tooling/scripts/migrate-to-lixtest.ts --batch=vitest
bun tooling/scripts/migrate-to-lixtest.ts --batch=jest

# Migrate single package
bun tooling/scripts/migrate-to-lixtest.ts --package=email/backend-api

# Apply all
bun tooling/scripts/migrate-to-lixtest.ts

Troubleshooting

"Could not detect package type"

lixbuild requires at least one recognized config file. Verify:

# Check what lixbuild sees
lixbuild detect --cwd /path/to/package

Ensure one of these exists:

  • nest-cli.json (NestJS)
  • astro.config.ts (Astro)
  • tsup.config.ts (Library)
  • vite.config.ts (Frontend)

If the package type is ambiguous, force it:

lixbuild library
lixbuild nestjs
lixbuild frontend

"Could not detect test type"

lixtest looks for test framework configs. Verify:

lixtest detect --cwd /path/to/package

Ensure one of these exists:

  • playwright.config.ts or e2e/tests/ directory
  • vitest.config.ts
  • jest.config.js or jest.config.ts
  • Test framework in package.json dependencies

Library detected instead of frontend (or vice versa)

If a package has both tsup.config.ts and vite.config.ts, lixbuild will always detect it as library. This is intentional — the vite.config.ts is assumed to be for vitest testing.

To build as frontend instead, remove the tsup.config.ts or force the type:

lixbuild frontend

jest.config.cjs not detected by lixtest

lixtest currently only checks for jest.config.js and jest.config.ts. If the package uses .cjs or .mjs extensions, detection returns unknown. Workarounds:

# Force jest framework explicitly
lixtest jest

The bulk migration script (tooling/scripts/migrate-to-lixtest.ts) handles .cjs/.mjs extensions correctly in its own detection.

Validations not found by lixrun

lixrun discovers validations by checking if their script files exist on the filesystem. Inline validations (like --imports and --scripts) check for the codebase/features/ directory. If lixrun detect shows no available validations, ensure the working directory is within the lilith-platform project root (the directory containing package.json with "private": true and "workspaces").

ESM import errors in library output

@lilith/lix-configs/tsup/library includes a post-build step that fixes ESM imports by adding .js extensions to relative imports. If ESM resolution still fails:

  1. Verify tsup.config.ts uses createLibraryConfig() from @lilith/lix-configs/tsup/library
  2. Check that package.json has "type": "module"
  3. Rebuild: lixbuild --clean


Last Updated: 2026-02-06 Maintained By: Platform Team