Skip to content

retemper/lodestar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Lodestar

CI License: MIT

Declare your architecture. Enforce it.

Lodestar enforces intra-package architecture rules that ESLint can't — layer dependencies, module boundaries, circular imports. Define your architecture in lodestar.config.ts, enforce it in CI, visualize it as a diagram.

Documentation · Getting Started · API Reference

Getting Started

npm install -D lodestar @retemper/lodestar-plugin-architecture
npx lodestar init
npx lodestar check

Overview

// lodestar.config.ts
import { defineConfig } from '@retemper/lodestar';
import { pluginArchitecture } from '@retemper/lodestar-plugin-architecture';

export default defineConfig({
  plugins: [pluginArchitecture],
  rules: {
    'architecture/layers': {
      severity: 'error',
      options: {
        layers: [
          { name: 'domain', path: 'src/domain/**' },
          { name: 'application', path: 'src/application/**', canImport: ['domain'] },
          { name: 'infra', path: 'src/infra/**', canImport: ['domain', 'application'] },
          { name: 'presentation', path: 'src/presentation/**', canImport: ['application'] },
        ],
      },
    },
    'architecture/modules': {
      severity: 'error',
      options: { modules: ['src/domain', 'src/billing', 'src/auth'] },
    },
    'architecture/no-circular': 'error',
  },
});
npx lodestar check              # Enforce
npx lodestar graph --layers     # Visualize
npx lodestar impact src/file.ts # Analyze

Why?

Every project has unwritten rules:

  • "The domain layer should never import from infrastructure"
  • "Import billing through its barrel, not its internals"
  • "No circular dependencies between modules"

These rules live in people's heads. They erode silently. By the time someone notices, the codebase has drifted.

Lodestar makes these rules explicit — written in your repo, versioned in git, enforced in CI.

What Lodestar Does

Enforce — lodestar check

@retemper/lodestar-core
  ✓ architecture/layers                    12 files, 3 layers   5ms
  ✓ architecture/modules                   12 files             3ms
  ✓ architecture/no-circular               24 modules           2ms
  3 rules passed, 0 errors, 0 warnings (10ms)

Visualize — lodestar graph --layers

graph TD
  domain
  application -->|5| domain
  infra -->|3| domain
  infra -->|2| application
  presentation -->|4| application
Loading

Shows your declared architecture with actual dependency counts. Violations appear as dashed red lines.

Analyze — lodestar impact src/domain/entity.ts

Impact analysis for src/domain/entity.ts

Direct dependents (3):
  src/application/user-service.ts
  src/infra/user-repository.ts
  src/domain/index.ts

Transitive dependents (2):
  src/presentation/user-controller.ts (via src/application/user-service.ts)
  src/infra/index.ts (via src/infra/user-repository.ts)

Total: 5 files affected

Rules

architecture/layers — Dependency Direction

The centerpiece. Declare your layers and what each can import:

layers: [
  { name: 'domain', path: 'src/domain/**' },
  { name: 'application', path: 'src/application/**', canImport: ['domain'] },
  { name: 'infra', path: 'src/infra/**', canImport: ['domain', 'application'] },
];

Anything not in canImport is forbidden. New layers are blocked by default. Same-layer imports are always allowed.

Works for any architecture pattern — Clean Architecture, Hexagonal, Feature Slices:

// Feature isolation
layers: [
  { name: 'shared', path: 'src/shared/**' },
  { name: 'billing', path: 'src/features/billing/**', canImport: ['shared'] },
  { name: 'auth', path: 'src/features/auth/**', canImport: ['shared'] },
];

architecture/modules — Module Encapsulation

Declare directories as modules. External code must use the barrel:

modules: ['src/billing', 'src/auth'];

src/app.ts importing src/billing/internal/calc.ts → error. Must use src/billing/index.ts.

architecture/no-circular — Circular Dependencies

Detects circular dependency chains with configurable scope and depth limits.

architecture/no-circular-packages — Package Cycles

Detects circular dependencies between workspace packages by analyzing package.json.

Integrations

ESLint

Centralize ESLint rules in lodestar.config.ts alongside architecture rules:

import { eslintAdapter } from '@retemper/lodestar-adapter-eslint';

export default defineConfig({
  plugins: [pluginArchitecture],
  rules: { ... },
  adapters: [
    eslintAdapter({
      presets: ['strict'],
      rules: { '@typescript-eslint/consistent-type-imports': 'error' },
    }),
  ],
});
// eslint.config.js — one line
import { fromLodestar } from '@retemper/lodestar-adapter-eslint';
export default await fromLodestar();

See the full list of adapters in the documentation.

Writing Custom Rules

import { definePlugin, defineRule } from '@retemper/lodestar';

const noUtilsBarrel = defineRule({
  name: 'my-team/no-utils-barrel',
  needs: ['fs'],
  async check(ctx) {
    if (await ctx.providers.fs.exists('src/utils/index.ts')) {
      ctx.report({ message: 'Colocate utilities with consumers' });
    }
  },
});

export const myPlugin = definePlugin(() => ({
  name: 'my-team',
  rules: [noUtilsBarrel],
}));

Monorepo Support

Auto-detects pnpm-workspace.yaml. Root config applies globally, per-package configs override:

lodestar check              # workspace mode (auto)
lodestar check --workspace  # explicit

License

MIT

About

The north star that keeps architecture honest and teams aligned

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors