CheckerBoundaries
Checks architectural import boundaries: scans the project’s TypeScript files, resolves imports through tsconfig.json, and reports forbidden imports, grouping violations by file.
ts | The path to tsconfig for resolving imports and types |
ts | An array of file extensions to check |
ts | An array of rules, each containing:
|
importsInsideScope
none— forbids imports within the same scope unless explicitly allowed throughallow.imports;all— allows any cross-imports within the scope;sameTopLevel— allows imports within the same module or feature, but forbids imports from a neighboring module or feature.
Example: Imports between the API and models layers
API files can import models, but not neighboring API files. Models can only import each other; test files are excluded from the check
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',
},
},
],
}),
Run Sandbox
{
"include": ["**/*.ts"],
"compilerOptions": {
"target": "ES2023",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true
}
}
export type TypeStatus = 'draft' | 'confirmed';
import type { TypeStatus } from './status';
export type TypeOrder = { id: string; status: TypeStatus };
import type { TypeOrder } from '../models/order';
export type TypeResponse = { orders: Array<TypeOrder> };
import type { TypeResponse } from './orders';
export type TypeTestResponse = TypeResponse;
[ARFA-GEN] finished in 0ms
Example: Feature module isolation
sameTopLevel allows imports within the same feature module and from shared, but blocks direct imports from neighboring modules, even through aliases and with broad allow.imports patterns
new CheckerBoundaries({
tsconfigPath: path.resolve(rootDir, 'tsconfig.json'),
extensions: ['.ts'],
rules: [
{
entries: ['features/**'],
allow: {
imports: ['shared/**', 'features/**'],
importsInsideScope: 'sameTopLevel',
},
},
],
}),
Run Sandbox
{
"include": ["**/*.ts"],
"compilerOptions": {
"target": "ES2023",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"paths": {
"features/*": ["./features/*"],
"shared/*": ["./shared/*"]
}
}
}
export const formatId = (id: string) => id.toUpperCase();
export type TypeOrder = { id: string };
export type TypeCustomer = { id: string };
import type { TypeOrder } from 'features/orders/model';
import { formatId } from 'shared/formatId';
export const orderLabel = (order: TypeOrder) => formatId(order.id);
[ARFA-GEN] finished in 0ms
Example: External packages and Node.js
Server files are allowed to use node:* imports, while client files are allowed to import external packages. Each permission applies independently
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,
},
},
],
}),
Run Sandbox
{
"include": ["**/*.ts"],
"compilerOptions": {
"target": "ES2023",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true
}
}
import path from 'node:path';
export const reportPath = path.join('reports', 'today');
import type { Ref } from 'vue';
export type TypeValue = Ref<string>;
[ARFA-GEN] finished in 0ms