Skip to content

CheckerBoundaries ​

Проверяет архитектурные границы импортов: проходит по TypeScript-файлам проекта, резолвит импорты через tsconfig.json и сообщает о запрещенных импортах, группируя нарушения по файлам.

ts
tsconfigPath: string

Путь к tsconfig для резолвинга импортов и типов

ts
extensions: Array<string>

Массив расширений файлов для проверки

ts
rules: Array<{
  entries: Array<string>;
  exclude?: RegExp;
  allow: {
    imports: Array<string>;
    importsInsideScope: 
      'none' | 'all' | 'sameTopLevel';
    node?: boolean;
    external?: boolean;
  };
}>

Массив правил, каждое из которых содержит:

entries — glob-паттерны поиска файлов относительно rootDir, например features/**
exclude — регулярное выражение для исключения файлов из обработки

allow — объект с полями:

imports — glob-паттерны разрешённых импортов относительно rootDir
importsInsideScope — разрешение импортов внутри области
node — разрешить импорты с node: префиксом
external — разрешить модули, определённые TypeScript как внешние библиотеки

importsInsideScope ​

  • none — запрещает импорты внутри той же области, если они явно не разрешены через allow.imports;
  • all — разрешает любые кросс-импорты внутри области;
  • sameTopLevel — разрешает импорты внутри одного модуля / feature, но запрещает импортировать из соседнего модуля / feature.

Пример: импорты между слоями API и models

API-файлы могут импортировать модели, но не соседние API-файлы. Модели могут импортировать только друг друга; тестовые файлы исключены из проверки

ts
new CheckerBoundaries({
  tsconfigPath: path.resolve(rootDir, 'tsconfig.json'),
  extensions: ['.ts'],
  rules: [
    {
      entries: ['api/**'],
      exclude: /\.test\.ts$/,
      allow: {
        imports: ['models/**'],
        importsInsideScope: 'none',
      },
    },
    {
      entries: ['models/**'],
      allow: {
        imports: [],
        importsInsideScope: 'all',
      },
    },
  ],
}),
Запустить Sandbox
USER: tsconfig.json создан с содержимым:
json
{
  "include": ["**/*.ts"],
  "compilerOptions": {
    "target": "ES2023",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true
  }
}
USER: models/status.ts создан с содержимым:
ts
export type TypeStatus = 'draft' | 'confirmed';
USER: models/order.ts создан с содержимым:
ts
import type { TypeStatus } from './status';

export type TypeOrder = { id: string; status: TypeStatus };
USER: api/orders.ts создан с содержимым:
ts
import type { TypeOrder } from '../models/order';

export type TypeResponse = { orders: Array<TypeOrder> };
USER: api/orders.test.ts создан с содержимым:
ts
import type { TypeResponse } from './orders';

export type TypeTestResponse = TypeResponse;
ARFA Generator выводит логи:
txt
[ARFA-GEN] finished in 0ms

Пример: изоляция feature-модулей

sameTopLevel разрешает импорты внутри одного feature-модуля и из shared, но останавливает прямой импорт соседнего модуля даже через алиас и при широком allow.imports

ts
new CheckerBoundaries({
  tsconfigPath: path.resolve(rootDir, 'tsconfig.json'),
  extensions: ['.ts'],
  rules: [
    {
      entries: ['features/**'],
      allow: {
        imports: ['shared/**', 'features/**'],
        importsInsideScope: 'sameTopLevel',
      },
    },
  ],
}),
Запустить Sandbox
USER: tsconfig.json создан с содержимым:
json
{
  "include": ["**/*.ts"],
  "compilerOptions": {
    "target": "ES2023",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "paths": {
      "features/*": ["./features/*"],
      "shared/*": ["./shared/*"]
    }
  }
}
USER: shared/formatId.ts создан с содержимым:
ts
export const formatId = (id: string) => id.toUpperCase();
USER: features/orders/model.ts создан с содержимым:
ts
export type TypeOrder = { id: string };
USER: features/customers/model.ts создан с содержимым:
ts
export type TypeCustomer = { id: string };
USER: features/orders/view.ts создан с содержимым:
ts
import type { TypeOrder } from 'features/orders/model';
import { formatId } from 'shared/formatId';

export const orderLabel = (order: TypeOrder) => formatId(order.id);
ARFA Generator выводит логи:
txt
[ARFA-GEN] finished in 0ms

Пример: внешние пакеты и Node.js

Серверным файлам разрешены node:* импорты, а клиентским — внешние пакеты. Каждое разрешение действует отдельно

ts
new CheckerBoundaries({
  tsconfigPath: path.resolve(rootDir, 'tsconfig.json'),
  extensions: ['.ts'],
  rules: [
    {
      entries: ['server/**'],
      allow: {
        imports: [],
        importsInsideScope: 'none',
        node: true,
      },
    },
    {
      entries: ['client/**'],
      allow: {
        imports: [],
        importsInsideScope: 'none',
        external: true,
      },
    },
  ],
}),
Запустить Sandbox
USER: tsconfig.json создан с содержимым:
json
{
  "include": ["**/*.ts"],
  "compilerOptions": {
    "target": "ES2023",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true
  }
}
USER: server/report.ts создан с содержимым:
ts
import path from 'node:path';

export const reportPath = path.join('reports', 'today');
USER: client/value.ts создан с содержимым:
ts
import type { Ref } from 'vue';

export type TypeValue = Ref<string>;
ARFA Generator выводит логи:
txt
[ARFA-GEN] finished in 0ms

MIT License