Appearance
API Reference
All public exports from ts-archunit, organized by category.
Entry Points
| Export | Signature | Description |
|---|---|---|
project | project(tsConfigPath: string): ArchProject | Load a TypeScript project. Cached per path. |
workspace | workspace(tsConfigPaths: string[]): ArchProject | Load multiple tsconfigs into a unified project for monorepo use. |
modules | modules(p: ArchProject): ModuleRuleBuilder | Rule builder for source files (imports/dependencies). |
classes | classes(p: ArchProject): ClassRuleBuilder | Rule builder for class declarations. |
functions | functions(p: ArchProject, options?: FunctionCollectionOptions): FunctionRuleBuilder | Rule builder for functions, arrow functions, class methods. options.includeObjectLiteralFunctions (default off) also collects object-literal function values. |
types | types(p: ArchProject): TypeRuleBuilder | Rule builder for interfaces and type aliases. |
slices | slices(p: ArchProject): SliceRuleBuilder | Rule builder for file groupings (cycles, layers). |
calls | calls(p: ArchProject): CallRuleBuilder | Rule builder for call expressions. |
jsxElements | jsxElements(p: ArchProject): JsxRuleBuilder | Rule builder for JSX elements in .tsx/.jsx files. |
within | within(sel: CallRuleBuilder): ScopedContext | Scoped rule builder for callback functions inside matched calls. |
tsconfig | tsconfig(p: ArchProject): TsconfigBuilder | Assert the project's resolved TypeScript compiler options. |
correspondence | correspondence(p: ArchProject): CorrespondenceBuilder | Assert two independently-derived key sets correspond ("every X has a matching Y"). |
Rule Builders
| Export | Description |
|---|---|
RuleBuilder | Base rule builder class. |
TerminalBuilder | Base terminal builder class (slices, smells, cross-layer). |
TsconfigBuilder | Builder returned by tsconfig(); adds .requires(). |
ModuleRuleBuilder | Builder returned by modules(). |
ClassRuleBuilder | Builder returned by classes(). |
FunctionRuleBuilder | Builder returned by functions(). |
TypeRuleBuilder | Builder returned by types(). |
SliceRuleBuilder | Builder returned by slices(). |
CallRuleBuilder | Builder returned by calls(). |
JsxRuleBuilder | Builder returned by jsxElements(). |
ScopedFunctionRuleBuilder | Builder returned by within().functions(). |
CorrespondenceBuilder | Builder returned by correspondence(). |
Rule Builder Methods
Chain methods available on all rule builders (RuleBuilder, SliceRuleBuilder).
| Method | Signature | Description |
|---|---|---|
.excluding() | .excluding(...patterns: (string | RegExp | SilentExclusion)[]) | Permanently suppress violations matching element name (e.g., 'MyService.doWork'), file path, or message. Strings use exact match; regex uses .test(). Warns on unused patterns. Wrap with silent() to suppress the warning. |
.because() | .because(reason: string) | Attach a human-readable rationale to the rule. |
.rule() | .rule(metadata: RuleMetadata) | Attach rich metadata (id, because, suggestion, docs). |
.check() | .check(options?: CheckOptions) | Execute rule; throw on violations. |
.warn() | .warn(options?: CheckOptions) | Execute rule; log violations without throwing, with one exception (configuration findings). |
.severity() | .severity(level: 'error' | 'warn') | Terminal — execute immediately with the given severity ('error' ≡ .check(), 'warn' ≡ .warn()). Returns void. |
.asSeverity() | .asSeverity(level: 'error'): this.asSeverity(level: 'warn', options?: { accepted?: readonly string[] }): this | Non-terminal — mark the rule's severity WITHOUT executing, and return a copy (bug 0016; it returned this through v0.20.0). Use in a rule file's export default [...] array so the CLI runs it; .violations() stamps each result with this severity. 'error' is the default, so .asSeverity('warn') is the meaningful call. Do NOT confuse with the terminal .severity(). .asSeverity('warn') alone is an advisory warning (permanent, unchanged since 0.16.0). .asSeverity('warn', { accepted }) is a deferred warning (plan 0090): a violation whose subjectOf() is in accepted stays warn; anything not in the list — a genuinely new finding — escalates to error. See violation-reporting for the full contract. |
.assertsSomething() | .assertsSomething(): boolean | Whether this rule asserts anything about what it selects (0.22.0). false means the rule can never fail, and since 0.23.0 the assertion gate turns that into an unsuppressable configuration finding on every terminal. diagnose()/doctor read it too. External TerminalBuilder subclasses are exempt by default (true) — the default is a compatibility choice, not a judgement that your builder is fine, so override it if your builder has an assertion-less state, or it stays outside the gate permanently. |
.assertionAdvice() | .assertionAdvice(): string | The remedy for this builder's assertion-less state, per state (0.22.0). One string, one place: doctor's advice and the failure message both read it verbatim, so the diagnostic and the failure cannot drift. |
.violations() | .violations(): ArchViolation[] | Execute rule, return violations without throwing (severity-stamped). For programmatic access and presets. |
.subjects() | .subjects(): readonly T[] | The elements matched by the predicate chain (post-.that(), before any condition). Powers correspondence().side() and .expectNonEmpty(). |
.expectNonEmpty() | .expectNonEmpty(): this | Redundant since 0.34.0 — an empty selection fails by default. Still reads as a statement of intent, and still legal, but it no longer changes behaviour. The finding bypasses diff-aware/baseline. |
.expectEmpty() | .expectEmpty(): this | 0.34.0. Assert the selector matches nothing, and fail the day it matches something. The escape hatch for a legitimately-empty selection — an assertion, not a silencer. Declaring it alongside .expectNonEmpty() throws a TypeError when the rule is built. |
.ownsDiscoveryDiagnosis() | .ownsDiscoveryDiagnosis(): boolean | 0.44.0. Whether this builder reports a dead discovery glob itself, with a better message than the generic gate can produce. false by default, so an external subclass is covered by the gate rather than silently exempt. Override to true only if your builder already produces a finding that names the dead population. Two builders do: slices unconditionally, because its fan-out semantics make the gate's per-tree view wrong; and crossLayer per condition — its three shipped conditions name the dead layer, and a condition that does not is covered by the gate instead. Since 0.46.1 that is decided by an internal registry, not by a property you can set. |
deadSelectorFindings() | protected deadSelectorFindings(): { selector: ArchViolation[]; discovery: ArchViolation[] } | Protected; the return type CHANGED in 0.44.0 from ArchViolation[] to the two buckets, so an external override must be updated — a TypeScript override stops compiling, and a JavaScript one throws at the call site. The split exists because the two positions have different precedence: a dead selector always wins, while a dead discovery glob defers to a builder that declares ownsDiscoveryDiagnosis(). ArchViolation carries no position, so the caller cannot recover it downstream. |
.describeRule() | .describeRule(): RuleDescription | Return rule metadata without executing. Used by explain command. |
Running Rule Arrays
| Export | Signature | Description |
|---|---|---|
checkAll | checkAll(rules: RuleBuilderLike[], options?: CheckOptions): void | Run an array of rules (e.g. a spread preset) and throw one aggregated ArchRuleError on any error-severity violation; warns are reported but never throw. The test-file terminal for the returning form. |
Exclusion Comments
| Export | Signature | Description |
|---|---|---|
parseExclusionComments | parseExclusionComments(source: string, file: string): ParseResult | Parse // ts-archunit-exclude comments from source text. |
isExcludedByComment | isExcludedByComment(violation: ArchViolation, comments: ExclusionComment[]): boolean | Check if a violation is covered by an exclusion comment. |
Types
| Export | Kind | Description |
|---|---|---|
ExclusionComment | type | Parsed exclusion comment with ruleId, reason, file, line, isBlock, endLine. |
ExclusionWarning | type | Warning about a malformed exclusion comment. |
ParseResult | type | Result of parsing: { exclusions, warnings }. |
Identity Predicates
Available on all entry points via .that().
| Export | Signature | Description |
|---|---|---|
haveNameMatching | haveNameMatching(re: RegExp) | Name matches regex. |
haveNameStartingWith | haveNameStartingWith(s: string) | Name starts with string. |
haveNameEndingWith | haveNameEndingWith(s: string) | Name ends with string. |
resideInFile | resideInFile(glob: string) | File path matches glob. |
resideInFolder | resideInFolder(glob: string) | Folder path matches glob. |
areExported | areExported | Element is exported. |
areNotExported | areNotExported | Element is not exported. |
Module Predicates
| Export | Signature | Description |
|---|---|---|
importFrom | importFrom(...globs) or importFrom(globs[], options) | Module has any edge to a path matching glob — import, export … from, import() or type X = import(…). Options: { ignoreTypeImports }. Selects MORE files since v0.28.0. |
predicateNotImportFrom | notImportFrom(...globs) or notImportFrom(globs[], options) | Module has no edge to a path matching glob — every kind counts. Options: { ignoreTypeImports }. Selects FEWER files since v0.28.0: a file whose only matching edge is a re-export now fails the predicate and drops out of the selection. |
exportSymbolNamed | exportSymbolNamed(name: string) | Module exports a symbol with the name. |
havePathMatching | havePathMatching(glob: string) | Module file path matches the glob. |
Class Predicates
| Export | Signature | Description |
|---|---|---|
extend | extend(name: string) | Class extends the named base class. |
implement | implement(name: string) | Class implements the named interface. |
haveDecorator | haveDecorator(name: string) | Class has the named decorator. |
haveDecoratorMatching | haveDecoratorMatching(re: RegExp) | Class has a decorator matching regex. |
areAbstract | areAbstract | Class is abstract. |
classHaveMethodNamed | haveMethodNamed(name: string) | Class has a method with the name. |
haveMethodMatching | haveMethodMatching(re: RegExp) | Class has a method matching regex. |
havePropertyNamed | havePropertyNamed(name: string) | Class has a property with the name. |
Function Predicates
| Export | Signature | Description |
|---|---|---|
areAsync | areAsync | Function is async. |
areNotAsync | areNotAsync | Function is not async. |
arePublic | arePublic() | Function/method is public (standalone always match). |
areProtected | areProtected() | Method is protected. |
arePrivate | arePrivate() | Method is private. |
haveParameterCount | haveParameterCount(n: number) | Function has exactly n parameters. |
haveParameterCountGreaterThan | haveParameterCountGreaterThan(n: number) | Function has more than n parameters. |
haveParameterCountLessThan | haveParameterCountLessThan(n: number) | Function has fewer than n parameters. |
haveParameterNamed | haveParameterNamed(name: string) | Function has a parameter with the name. |
haveReturnType | haveReturnType(type: string) | Function has the given return type. |
haveRestParameter | haveRestParameter() | Function has a ...args rest parameter. |
haveOptionalParameter | haveOptionalParameter() | Function has an optional or default-valued parameter. |
haveParameterOfType | haveParameterOfType(i: number, m: TypeMatcher) | Parameter at index i matches the TypeMatcher. |
haveParameterNameMatching | haveParameterNameMatching(re: RegExp) | Function has a parameter name matching regex. |
Type Predicates
| Export | Signature | Description |
|---|---|---|
areInterfaces | areInterfaces | Type is an interface. |
areTypeAliases | areTypeAliases | Type is a type alias. |
haveProperty | haveProperty(name: string) | Type has a property with the name. |
havePropertyOfType | havePropertyOfType(name: string, re: RegExp) | Property exists with type matching regex. |
extendType | extendType(name: string) | Interface extends the named type. |
Call Predicates
| Export | Signature | Description |
|---|---|---|
onObject | onObject(name: string) | Call is on the named object (e.g., app). Supports nested: router.route. |
withMethod | withMethod(nameOrRegex: string | RegExp) | Call method matches exact name or regex pattern. |
withArgMatching | withArgMatching(index: number, pattern: string | RegExp) | Argument at index matches regex or exact string. |
withStringArg | withStringArg(index: number, glob: string) | String literal argument at index matches glob pattern. |
CallRuleBuilder identity enrichment
| Method | Signature | Description |
|---|---|---|
identifiedByArg | identifiedByArg(index: number) | Opt-in: fold the indexed string-literal argument into the violation element and message so individual registrations can be .excluding()-targeted. See docs/calls.md for details and the Identity scope footgun. |
JSX Predicates
| Export | Signature | Description |
|---|---|---|
areHtmlElements | areHtmlElements(...tags: string[]) | Matches HTML intrinsic elements with the given tag names. |
areComponents | areComponents(...names?: string[]) | Matches component elements. No args = all components. |
jsxWithAttribute | withAttribute(name: string) | Filter to elements that have the named attribute. |
jsxWithAttributeMatching | withAttributeMatching(name: string, value: string | RegExp) | Filter to elements where attribute matches value. |
JSX Conditions
| Export | Signature | Description |
|---|---|---|
jsxNotExist | notExist() | Filtered JSX element set must be empty. |
jsxHaveAttribute | haveAttribute(name: string) | Every matched element must have the named attribute. |
jsxNotHaveAttribute | notHaveAttribute(name: string) | No matched element may have the named attribute. |
jsxHaveAttributeMatching | haveAttributeMatching(name: string, value: string | RegExp) | Attribute must exist and match value. |
jsxNotHaveAttributeMatching | notHaveAttributeMatching(name: string, value: string | RegExp) | Attribute must not match (or be absent). |
JSX Utilities
| Export | Description |
|---|---|
STANDARD_HTML_TAGS | readonly string[] — All standard HTML tag names for use with areHtmlElements(). |
collectJsxElements | (sf: SourceFile) => ArchJsxElement[] — Collect JSX elements from a source file. |
Structural Conditions
| Export | Signature | Description |
|---|---|---|
notExist | notExist() | No elements should match the predicates. |
beExported | beExported() | All matched elements should be exported. |
conditionResideInFile | resideInFile(glob: string) | All elements should reside in matching files. |
conditionResideInFolder | resideInFolder(glob: string) | All elements should reside in matching folders. |
conditionHaveNameMatching | haveNameMatching(re: RegExp) | All elements should have names matching regex. |
Class Conditions
| Export | Signature | Description |
|---|---|---|
shouldExtend | shouldExtend(name: string) | Class must extend the named base class. |
shouldImplement | shouldImplement(name: string) | Class must implement the named interface. |
shouldHaveMethodNamed | shouldHaveMethodNamed(name: string) | Class must have a method with the name. |
shouldNotHaveMethodMatching | shouldNotHaveMethodMatching(re: RegExp) | Class must not have methods matching regex. |
classAcceptParameterOfType | acceptParameterOfType(matcher: TypeMatcher) | At least one param (ctor/method/setter) matches type. |
classNotAcceptParameterOfType | notAcceptParameterOfType(matcher: TypeMatcher) | No param (ctor/method/setter) matches type. |
Function Conditions
| Export | Signature | Description |
|---|---|---|
functionNotExist | notExist() | No functions should match. |
functionBeExported | beExported() | Function must be exported. |
functionBeAsync | beAsync() | Function must be async. |
functionHaveNameMatching | haveNameMatching(re: RegExp) | Function name must match regex. |
functionHaveReturnTypeMatching | haveReturnTypeMatching(matcher: TypeMatcher) | Return type must satisfy TypeMatcher. |
functionAcceptParameterOfType | acceptParameterOfType(matcher: TypeMatcher) | At least one parameter matches TypeMatcher. |
functionNotAcceptParameterOfType | notAcceptParameterOfType(matcher: TypeMatcher) | No parameter matches TypeMatcher. |
Dependency Conditions
| Export | Signature | Description |
|---|---|---|
onlyImportFrom | onlyImportFrom(...globs) or (globs[], options) | Module may have no edge outside the listed paths — every kind counts. Options: { ignoreTypeImports }. Reports MORE findings since v0.28.0. |
conditionNotImportFrom | notImportFrom(...globs) or (globs[], options) | Module must have no edge to the listed paths — import, export … from, import() or type X = import(…). Options: { ignoreTypeImports }. Reports MORE findings since v0.28.0. |
dependOn | dependOn(...globs) or (globs[], options) | Module must import from at least one matching path. Options: { ignoreTypeImports }. |
onlyHaveTypeImportsFrom | onlyHaveTypeImportsFrom(...globs: string[]) | Imports from matching paths must use import type. |
notHaveAliasedImports | notHaveAliasedImports() | No named import may use an alias (import { x as y }). |
Body Analysis Matchers
| Export | Signature | Description |
|---|---|---|
call | call(target: string | RegExp) | Match function/method call expressions. |
newExpr | newExpr(target: string | RegExp) | Match constructor invocations (new ...). |
access | access(target: string | RegExp) | Match property access expressions. |
property | property(name: string | RegExp, value?: boolean | number | string | RegExp) | Match property assignments by name and optional value. |
expression | expression(target: string | RegExp) | Match any expression by text. |
jsxElement | jsxElement(tag: string | RegExp) | Match JSX elements by tag name (tag-only, no attributes). |
jsxText | jsxText() | Match hardcoded JSX text content (children, incl. {"..."}). |
typeAssertion | typeAssertion(options?: { allowConst?: boolean }) | Match as Type expressions. Excludes as const by default. |
nonNullAssertion | nonNullAssertion() | Match ! non-null assertion expressions. |
comment | comment(pattern: string | RegExp) | Match comments attached to AST nodes. |
STUB_PATTERNS | RegExp | Matches common stub markers: TODO, FIXME, HACK, XXX, STUB, etc. |
Body Analysis Conditions
| Export | Signature | Description |
|---|---|---|
classContain | classContain(matcher: ExpressionMatcher) | Class methods must contain expression. |
classNotContain | classNotContain(matcher: ExpressionMatcher) | Class methods must not contain expression. |
classUseInsteadOf | classUseInsteadOf(banned, replacement) | Ban expression in class, suggest replacement. |
functionContain | functionContain(matcher: ExpressionMatcher) | Function body must contain expression. |
functionNotContain | functionNotContain(matcher: ExpressionMatcher) | Function body must not contain expression. |
functionUseInsteadOf | functionUseInsteadOf(banned, replacement) | Ban expression in function, suggest replacement. |
functionNotHaveEmptyBody | functionNotHaveEmptyBody() | Function must have at least one statement. |
classNotHaveEmptyBody | classNotHaveEmptyBody() | Class must have at least one member. |
moduleContain | moduleContain(matcher, options?) | Module must contain expression. |
moduleNotContain | moduleNotContain(matcher, options?) | Module must not contain expression. |
moduleUseInsteadOf | moduleUseInsteadOf(banned, replacement, opts?) | Ban expression in module, suggest replacement. |
Export Conditions
| Export | Signature | Description |
|---|---|---|
notHaveDefaultExport | notHaveDefaultExport() | Module must not have a default export. |
haveDefaultExport | haveDefaultExport() | Module must have a default export. |
haveMaxExports | haveMaxExports(max: number) | Module must have at most max named exports. |
Reverse Dependency Conditions
| Export | Signature | Description |
|---|---|---|
onlyBeImportedVia | onlyBeImportedVia(...globs) | All importers must match at least one glob. |
beImported | beImported() | Module must be imported by at least one other file. |
haveNoUnusedExports | haveNoUnusedExports() | Every named export must be referenced by another file. |
Property Conditions
| Export | Signature | Description |
|---|---|---|
conditionHavePropertyNamed | havePropertyNamed(...names: string[]) | All named properties must exist. |
conditionNotHavePropertyNamed | notHavePropertyNamed(...names: string[]) | None of the named properties may exist. |
conditionHavePropertyMatching | havePropertyMatching(pattern: RegExp) | At least one property name matches regex. |
conditionNotHavePropertyMatching | notHavePropertyMatching(pattern: RegExp) | No property name matches regex. |
haveOnlyReadonlyProperties | haveOnlyReadonlyProperties() | All properties must be readonly. |
maxProperties | maxProperties(max: number) | Property count must not exceed max. |
Type-Level Conditions
| Export | Signature | Description |
|---|---|---|
havePropertyType | havePropertyType(name: string, matcher: TypeMatcher) | Property must match the type matcher. |
Type Matchers
| Export | Signature | Description |
|---|---|---|
isString | isString(): TypeMatcher | Type is string. |
isNumber | isNumber(): TypeMatcher | Type is number. |
isBoolean | isBoolean(): TypeMatcher | Type is boolean. |
isUnionOfLiterals | isUnionOfLiterals(): TypeMatcher | Type is a union of literal types. |
isStringLiteral | isStringLiteral(): TypeMatcher | Type is a string literal. |
arrayOf | arrayOf(matcher: TypeMatcher): TypeMatcher | Type is an array whose element matches. |
matching | matching(re: RegExp): TypeMatcher | Type text matches regex. |
exactly | exactly(text: string): TypeMatcher | Type text matches exactly. |
Slice Conditions
| Export | Signature | Description |
|---|---|---|
beFreeOfCycles | beFreeOfCycles(options?) | No circular dependencies between slices. Ignores type-only imports by default; pass { ignoreTypeImports: false } to count type-only edges too — note that is not the pre-0.47 graph, since re-exports are counted as well since v0.48.0. |
respectLayerOrder | respectLayerOrder(...layers) or (layers, options) | Dependencies follow declared layer order. Counts type-only edges by default. |
notDependOn | notDependOn(...slices) or (slices, options) | No slice depends on the named slices. Counts type-only edges by default; one finding per dependency site. |
Call Conditions
| Export | Signature | Description |
|---|---|---|
callHaveCallbackContaining | haveCallbackContaining(matcher: ExpressionMatcher) | At least one callback argument must contain the matched expression. |
callNotHaveCallbackContaining | notHaveCallbackContaining(matcher: ExpressionMatcher) | No callback argument may contain the matched expression. |
callNotExist | notExist() | The filtered call set must be empty. |
haveArgumentWithProperty | haveArgumentWithProperty(...names: string[]) | At least one object literal arg has ALL named properties. |
notHaveArgumentWithProperty | notHaveArgumentWithProperty(...names: string[]) | No object literal arg has ANY of the named properties. |
callHaveArgumentContaining | haveArgumentContaining(matcher: ExpressionMatcher) | At least one argument subtree must contain the matched expression. |
callNotHaveArgumentContaining | notHaveArgumentContaining(matcher: ExpressionMatcher) | No argument subtree may contain the matched expression. |
See Call Rules for usage examples.
Pattern Templates
| Export | Signature | Description |
|---|---|---|
definePattern | definePattern(name: string, opts: { returnShape: Record<string, PropertyConstraint> }): ArchPattern | Define a return type shape pattern. |
followPattern | followPattern(pattern: ArchPattern): Condition<ArchFunction> | Condition: function return type must match the pattern. Unwraps Promise<T>. |
PropertyConstraint | string | TypeMatcher | string = regex on type text, 'T[]' = any array, TypeMatcher = programmatic. |
ArchPattern | type | Pattern with name and returnShape. |
See Pattern Templates for usage examples.
Smell Detectors
| Export | Signature | Description |
|---|---|---|
smells.duplicateBodies | smells.duplicateBodies(p: ArchProject): DuplicateBodiesBuilder | Detect functions with structurally similar AST bodies. |
smells.inconsistentSiblings | smells.inconsistentSiblings(p: ArchProject): InconsistentSiblingsBuilder | Detect sibling files missing a majority pattern. |
SmellBuilder | class | Base builder: inFolder, minLines, ignoreTests, ignorePaths, groupByFolder, because, warn, check. |
DuplicateBodiesBuilder | class | Extends SmellBuilder. Adds withMinSimilarity(n) and minDistinctVocabulary(n) (default 8) — a pairwise floor gating comparison before similarity is even computed. |
InconsistentSiblingsBuilder | class | Extends SmellBuilder. Adds forPattern(matcher) and inertAdvice(): string — non-empty when the rule examines a real corpus but no folder is within one edit of the 60% majority needed to ever flag anything; '' otherwise, including when the pattern matches nothing at all. diagnose() reports the same text. |
buildFingerprint | buildFingerprint(node: Node): Fingerprint | Build an AST fingerprint (kinds, calls, nodeCount, distinctVocabulary) from a body node. |
computeSimilarity | computeSimilarity(a: Fingerprint, b: Fingerprint): number | LCS-based similarity between two fingerprints, normalized to [0,1]. |
See Smell Detection for usage examples.
Cross-Layer Validation
| Export | Signature | Description |
|---|---|---|
crossLayer | crossLayer(p: ArchProject): CrossLayerBuilder | Entry point for cross-layer consistency rules. |
CrossLayerBuilder | class | Builder: .layer(name, glob) (2+ required) then .mapping(fn). |
MappedCrossLayerBuilder | class | After .mapping(): provides .forEachPair(). |
PairConditionBuilder | class | After .forEachPair(): provides .should(condition). |
PairFinalBuilder | class | Terminal: .because(), .rule(), .check(), .warn(), .severity(). |
haveMatchingCounterpart | haveMatchingCounterpart(): PairCondition | Every left-layer file must have a counterpart in the right layer. Fails on an empty left layer (v0.18.0). Takes no argument since v0.42.0 — the builder supplies its own resolved layers. An optional Layer[] is still accepted for compatibility and is ignored when the builder provides them. |
haveConsistentExports | haveConsistentExports(extractLeft, extractRight): PairCondition | Every exported symbol in left file must appear in right file. |
satisfyPairCondition | satisfyPairCondition(desc: string, fn: (pair: LayerPair) => Violation | null): PairCondition | Custom inline pair condition. |
See Cross-Layer Validation for usage examples.
Correspondence
correspondence(p) asserts that two independently-derived key sets correspond — "every X has a matching Y" (ADR-008 Rule 5 as a primitive). Compares by identity, never count; an empty side fails (non-vacuity). Call .side() twice, then an assertion.
| Method | Signature | Description |
|---|---|---|
.side() | .side(name, selection: RuleBuilder<T>, keyFn: KeyFn<T>) · .side(name, keys: readonly string[] | ReadonlySet<string>) | Add a side — a selection keyed by keyFn, or a pre-derived key set. Call twice. |
.beComplete() | .beComplete(): this | Every key of the first side has a match in the second (A ⊆ B). |
.haveNoOrphans() | .haveNoOrphans(): this | Every key of the second side has a source in the first (B ⊆ A). |
.beBijective() | .beBijective(): this | Both directions — the key sets are identical. |
.expectEmpty() | .expectEmpty(sideName?: string): this | Declare a side empty. An assertion, not a permission: it fails the day the side fills up. Replaces .allowEmpty() from the next release. |
.distinctKeysOn() | .distinctKeysOn(sideName: string): this | Fail if a side maps two distinct subjects to one key (over-normalization). |
Extends TerminalBuilder, so .rule() / .excluding() / .check() / .warn() / .violations() also apply.
| Export | Signature | Description |
|---|---|---|
byName | byName<T>(): KeyFn<T> | Key a subject by getName() (<anonymous> fallback). |
byArg | byArg<T>(index: number): KeyFn<T> | Key a call-like subject by its argument at index (string/template literals are unquoted). |
byPropertyNames | byPropertyNames<T>(): KeyFn<T> | Key a subject by each of its property names (one subject → many keys). |
setCorrespondence | setCorrespondence(aKeys, bKeys): CorrespondenceResult | Pure identity set-difference + emptiness core (also backs crossLayer's existence check). |
typescript
import { correspondence, calls, byArg } from '@nielspeter/ts-archunit'
import { ROUTE_PERMISSIONS } from '../src/config/route-permissions.js'
correspondence(p)
.side(
'routes',
calls(p)
.that()
.onObject('app')
.and()
.withMethod(/^(get|post)$/),
byArg(0),
)
.side('matrix', Object.keys(ROUTE_PERMISSIONS))
.should()
.beBijective()
.rule({ id: 'auth/route-matrix', suggestion: 'Add the route to ROUTE_PERMISSIONS.' })
.check()Extension API
| Export | Signature | Description |
|---|---|---|
definePredicate | definePredicate<T>(desc, fn, globs?): Predicate<T> | Create a custom predicate. globs declares the path globs it matches against, so doctor can report a dead one — see Custom rules. |
defineCondition | defineCondition<T>(desc, fn, globs?): Condition<T> | Create a custom condition. globs makes them visible to explain; a dead condition glob is deliberately not a finding. |
and | and(...inputs): Predicate | TypeMatcher | Combine with AND. Accepts predicates or type matchers. |
or | or(...inputs): Predicate | TypeMatcher | Combine with OR. Accepts predicates or type matchers. |
not | not(input): Predicate | TypeMatcher | Negate. Accepts a predicate or type matcher. |
Utilities
| Export | Signature | Description |
|---|---|---|
createViolation | createViolation(node, msg, ctx): ArchViolation | Create a violation from a ts-morph node. |
getElementName | getElementName(node): string | Get the name of a ts-morph node. |
getElementFile | getElementFile(node): string | Get the file path of a ts-morph node. |
getElementLine | getElementLine(node): number | Get the line number of a ts-morph node. |
remedyRepeatsMessage | remedyRepeatsMessage(v: ArchViolation): boolean | True when a violation's suggestion is its message, as a configuration finding's is. A renderer that already printed the message must not print it again as Fix:. Both fields carry the text on purpose — a tool reads suggestion, a human reads the body. |
severityFor | severityFor(v, fallback): 'error' | 'warn' | The severity a violation must be reported at. A configuration finding is always error, whatever the rule asked for — an aggregator that applies the rule's own severity would silence the one finding that may not be silenced. |
generateCodeFrame | generateCodeFrame(source, line, opts?): string | Generate a code frame snippet. |
formatViolations | formatViolations(violations, opts?): string | Format violations for terminal output. |
formatViolationsPlain | formatViolationsPlain(violations): string | Format violations as plain text. |
formatViolationsJson | formatViolationsJson(violations): string | Format violations as JSON. |
formatViolationsGitHub | formatViolationsGitHub(violations): string | Format violations as GitHub Actions annotations. |
detectFormat | detectFormat(): OutputFormat | Auto-detect output format from environment. |
isCI | isCI(): boolean | True if running in a CI environment. |
ArchRuleError | class | Error thrown by .check() on violations. |
isTypeOnlyImport | isTypeOnlyImport(decl: ImportDeclaration): boolean | Check if an import is purely type-only. |
Check Options
| Export | Signature | Description |
|---|---|---|
withBaseline | withBaseline(path: string, options?: { root?: string }): Baseline | Load a baseline file for gradual adoption. root overrides the repository root used for portable identity — rarely needed, see Baselines. |
generateBaseline | generateBaseline(violations, path): BaselineDelta | Write a baseline file from current violations, and return the delta it applied (before, after, added, removed). See enforcing the ratchet. |
collectViolations | collectViolations(...rules): ArchViolation[] | Collect violations from multiple rules. |
diffAware | diffAware(base: string): DiffFilter | Only report violations in changed files. |
Baseline | class | Baseline instance for filtering known violations. |
DiffFilter | class | Diff filter instance. |
silent | silent(pattern: string | RegExp): SilentExclusion | Wrap an exclusion pattern to suppress the "unused exclusion" warning. |
ArchFunction Model
| Export | Signature | Description |
|---|---|---|
collectFunctions | collectFunctions(sourceFiles): ArchFunction[] | Collect all functions from source files. |
fromFunctionDeclaration | fromFunctionDeclaration(node): ArchFunction | Wrap a function declaration. |
fromArrowVariableDeclaration | fromArrowVariableDeclaration(node): ArchFunction | Wrap an arrow function variable. |
fromMethodDeclaration | fromMethodDeclaration(node): ArchFunction | Wrap a class method declaration. |
Callback Extraction
| Export | Signature | Description |
|---|---|---|
extractCallbacks | extractCallbacks(calls): ExtractedCallback[] | Extract callback functions from call expressions. |
Scoped Rules
| Export | Signature | Description |
|---|---|---|
within | within(sel): ScopedContext | Create scoped rules from call selections. |
ScopedFunctionRuleBuilder | class | Builder for function rules within a scope. |
Metrics
| Export | Signature | Description |
|---|---|---|
cyclomaticComplexity | cyclomaticComplexity(body: Node | undefined): number | Calculate McCabe cyclomatic complexity for a function body. |
linesOfCode | linesOfCode(node: Node): number | Count span lines (start to end, inclusive). |
haveCyclomaticComplexity | haveCyclomaticComplexity(opts): Predicate<ClassDeclaration> | Predicate: class has a method with complexity > threshold. |
haveComplexity | haveComplexity(opts): Predicate<ArchFunction> | Predicate: function has complexity > threshold. |
haveMoreLinesThan | haveMoreLinesThan(n): Predicate<ClassDeclaration> | Predicate: class spans more than n lines. |
haveMoreFunctionLinesThan | haveMoreFunctionLinesThan(n): Predicate<ArchFunction> | Predicate: function spans more than n lines. |
haveMoreMethodsThan | haveMoreMethodsThan(n): Predicate<ClassDeclaration> | Predicate: class has more than n methods. |
CLI
| Export | Signature | Description |
|---|---|---|
defineConfig | defineConfig(config: CliConfig): CliConfig | Define CLI configuration file. |
resetProjectCache | resetProjectCache(): void | Clear the project singleton cache. Used by watch mode and tests. |
Diagnostics
Report which rules cannot enforce anything, without evaluating their conditions. It does materialize each rule's selection, because "this rule examined nothing" is a fact about the selection — so it is fast but not free, and the wording changed from "without running them" when that became true.
diagnose() itself returns findings rather than throwing — it is the surface for inspecting a rule set. Since 0.23.0 the underlying faults do fail a build when the rules actually run: a rule that asserts nothing is a configuration finding on every terminal. Use this to survey rules ahead of time — before an upgrade, or after adding rules. ts-archunit doctor is the CLI equivalent and exits non-zero when it reports anything, but it is a diagnostic you invoke rather than a gate; check is the gate.
Two hosts, one diagnosis. diagnose() runs wherever you build your rules, including inside vitest or jest. ts-archunit doctor is the CLI equivalent for rule files the CLI can load — the arch.rules.ts shape — and additionally reports a rule file that fails to load, which diagnose() cannot see because it never loads one.
| Export | Signature | Description |
|---|---|---|
diagnose | diagnose(rules: RuleBuilderLike[], project?): DiagnosticFinding[] | Report what each rule cannot enforce: dead globs, condition-less rules, rules whose project cannot be named, and projects that loaded no files. Reports identities, never totals. The project defaults to the one each rule was built against. |
DiagnosticFinding | type | { kind, rule, ruleFile?, origin?, glob?, position?, fault?, onDisk?, advice }. kind is the JSON contract and has seven values: 'dead-glob', 'no-condition', 'project-unknown', 'project-empty', 'orphan-exclusion', 'zero-subjects', 'inert' — only 'dead-glob' carries origin/glob/position/fault/onDisk. ruleFile is set by doctor, which knows which file each rule came from; diagnose() never sets it, because it is handed rules rather than files. |
DiagnosableRule | type | What diagnose can inspect. Any RuleBuilderLike qualifies. |
'inert' previews smells.inconsistentSiblings() rules that examine a non-empty corpus and still cannot fail: no folder's matching files are within one edit of the 60% majority forPattern() requires to flag anything. diagnose() reports it; check() does not yet fail on it — that flip is a separate, tracked migration (see docs/upgrading.md). A rule with a real majority, or one edit away from forming one, is unaffected: the preview is silent exactly where a finding could still fire soon.
typescript
import { project, modules, diagnose } from '@nielspeter/ts-archunit'
const p = project('tsconfig.json')
const rules = [
modules(p).that().resideInFolder('**/src/reslvers/**').should().notHaveDefaultExport(),
]
// In a test, so rules written in vitest can be measured too:
expect(diagnose(rules)).toEqual([])Or from the command line:
bash
ts-archunit doctor arch.rules.ts # exits non-zero if it reports anythingDeclaring globs on a custom predicate
A predicate that matches on a path can declare it, which is what makes it visible to diagnose. A predicate that declares nothing is simply invisible — nothing breaks.
| Export | Description |
|---|---|
DeclaredGlob | { glob, kind, polarity?, base? } — what a predicate declares. Deliberately cannot express position. |
GlobKind | 'file-path' | 'parent-dir' | 'import-target' | 'specifier' | 'literal' — names the string the matcher is applied to, not the API. |
globNode, globAnyOf | Build a declaration from one glob, or from a variadic set (any). |
combineGlobs, negateGlobs, stampGlobs | For combinators and builders. negateGlobs is a full NNF push-down. |
GlobNode, GlobSite, GlobTree, GlobPosition, GlobBase, OpaqueGlob | Supporting types. |
typescript
import { globNode, type Predicate } from '@nielspeter/ts-archunit'
import type { SourceFile } from 'ts-morph'
function inGeneratedOutput(glob: string): Predicate<SourceFile> {
return {
description: `in generated output matching "${glob}"`,
globs: globNode({ glob, kind: 'file-path' }),
test: (sf) => sf.getFilePath().includes('/generated/'),
}
}Types (TypeScript)
| Export | Kind | Description |
|---|---|---|
ArchProject | type | Loaded TypeScript project. |
Predicate | type | Predicate interface. |
Condition | type | Condition interface. |
ConditionContext | type | Context passed to condition evaluators. |
ArchViolation | type | Violation model. |
RuleMetadata | type | Rule metadata (id, because, suggestion, docs). |
RuleDescription | type | Structured rule description returned by .describeRule(). |
CheckOptions | type | Options for .check(). |
OutputFormat | type | Output format ('terminal' | 'github' | 'json'). |
FormatOptions | type | Options for formatting functions. |
CodeFrameOptions | type | Options for generateCodeFrame(). |
ExpressionMatcher | type | Matcher returned by call(), newExpr(), etc. |
TypeMatcher | type | Matcher used with havePropertyType(). |
TypeDeclaration | type | Union of interface and type alias declarations. |
ArchFunction | type | Unified function/arrow/method model. |
ArchCall | type | Model for matched call expressions. |
FunctionCollectionOptions | type | Options for functions() (includeMethods, includeObjectLiteralFunctions). |
KeyFn | type | correspondence().side() key extractor: (subject: T) => string | readonly string[]. |
KeysSource | type | A pre-derived key set: readonly string[] | ReadonlySet<string>. |
CorrespondenceResult | type | Result of setCorrespondence() (missing, orphans, aEmpty, bEmpty). |
ObjectLiteralFunction | type | A function found in an object literal (node, keyPath) from collectObjectLiteralFunctions(). |
Slice | type | A named group of source files. |
SliceDefinition | type | Input to assignedFrom(). |
Named | type | Element with a name. |
Located | type | Element with a file location. |
Exportable | type | Element that can be exported. |
BaselineEntry | type | Single entry in a baseline file. |
BaselineFile | type | Structure of the baseline JSON file. |
Layer | type | Layer definition for cross-layer validation. |
LayerPair | type | Pair of elements from two layers. |
PairCondition | type | Condition for cross-layer pairs. |
ArchPattern | type | Pattern template definition. |
PropertyConstraint | type | Property type constraint in a pattern. |
Fingerprint | type | AST fingerprint for similarity detection. |
ScopedContext | type | Context returned by within(). |
ExtractedCallback | type | Callback extracted from a call expression. |
PropertyBearingNode | type | Union of interface, type alias, and class declarations. |
ImportOptions | type | Options for import conditions/predicates ({ ignoreTypeImports }). |
CliConfig | type | CLI configuration object. |
GraphQL Extension (ts-archunit/graphql)
Requires the optional graphql peer dependency.
Entry Points
| Export | Signature | Description |
|---|---|---|
schema | schema(p: ArchProject | string, glob: string): SchemaRuleBuilder | Rule builder for .graphql schema files. |
schemaFromSDL | schemaFromSDL(sdl: string, path?): SchemaRuleBuilder | Rule builder from raw SDL string. |
resolvers | resolvers(p: ArchProject, glob: string): ResolverRuleBuilder | Rule builder for resolver TypeScript files. |
Schema Predicates
| Export | Signature | Description |
|---|---|---|
queries | queries | Select Query type fields. |
mutations | mutations | Select Mutation type fields. |
typesNamed | typesNamed(re: RegExp) | Select types matching regex. |
returnListOf | returnListOf(re: RegExp) | Select fields returning a list of matching type. |
Schema Conditions
| Export | Signature | Description |
|---|---|---|
haveFields | haveFields(...names: string[]) | Type must have the named fields. |
acceptArgs | acceptArgs(...names: string[]) | Field must accept the named arguments. |
haveMatchingResolver | haveMatchingResolver(resolverGlob: string) | Schema field has a matching resolver file. |
Resolver Predicates
| Export | Signature | Description |
|---|---|---|
resolveFieldReturning | resolveFieldReturning(re: RegExp) | Resolver resolves a field returning matching type. |
Schema Loader
| Export | Signature | Description |
|---|---|---|
loadSchemaFromGlob | loadSchemaFromGlob(root, glob): LoadedSchema | Load schema from glob pattern. |
loadSchemaFromSDL | loadSchemaFromSDL(sdl, path?): LoadedSchema | Load schema from SDL string. |
isGraphQLAvailable | isGraphQLAvailable(): boolean | Check if the graphql package is installed. |
Builders
| Export | Description |
|---|---|
SchemaRuleBuilder | Builder for schema architecture rules. |
ResolverRuleBuilder | Builder for resolver architecture rules. |
Types
| Export | Kind | Description |
|---|---|---|
SchemaElement | type | Element in a GraphQL schema. |
LoadedSchema | type | Loaded and parsed GraphQL schema. |
GraphQLSchemaLike | type | Schema interface. |
GraphQLObjectTypeLike | type | Object type interface. |
GraphQLFieldLike | type | Field interface. |
GraphQLArgumentLike | type | Argument interface. |
GraphQLTypeLike | type | Type interface. |
Presets (ts-archunit/presets)
Parameterized architecture rule bundles that generate multiple coordinated rules from a single function call.
Every preset returns RuleBuilderLike[] — spread it into a rule file (export default [...]) or run it with checkAll in a test.
| Export | Signature | Description |
|---|---|---|
recommended | recommended(p, options?): RuleBuilderLike[] | Thin universal safety floor (eval, Function ctor, …). |
agentGuardrails | agentGuardrails(p, options): RuleBuilderLike[] | Guardrails for AI-agent mistakes. |
layeredArchitecture | layeredArchitecture(p, options): RuleBuilderLike[] | Layer ordering, cycles, isolation, restricted packages. |
dataLayerIsolation | dataLayerIsolation(p, options): RuleBuilderLike[] | Base class extension and typed error enforcement. |
strictBoundaries | strictBoundaries(p, options): RuleBuilderLike[] | No cycles, no cross-boundary imports, shared isolation. |
validateOverrides | validateOverrides(overrides, knownIds): void | Warn on unrecognized override keys. |
See Architecture Presets for full configuration options.
Standard Rules (Sub-Path Imports)
ts-archunit/rules/typescript
| Export | Description |
|---|---|
noAnyProperties() | Class properties must not be typed as any. |
noTypeAssertions() | Method bodies must not contain as type assertions (allows as const). |
noNonNullAssertions() | Method bodies must not contain non-null assertions (!). |
ts-archunit/rules/security
| Export | Target | Description |
|---|---|---|
noEval() | classes | No eval() calls in class methods. |
noFunctionConstructor() | classes | No new Function() constructor. |
noConsoleLog() | classes | No console.log calls. |
noProcessEnv() | classes | No direct process.env access. |
noConsole() | classes | No console access at all (log, warn, error, etc). |
noJsonParse() | classes | No JSON.parse calls. |
functionNoEval() | functions | No eval() calls in functions. |
functionNoFunctionConstructor() | functions | No new Function() in functions. |
functionNoProcessEnv() | functions | No process.env access in functions. |
functionNoConsoleLog() | functions | No console.log in functions. |
functionNoConsole() | functions | No console access in functions. |
functionNoJsonParse() | functions | No JSON.parse in functions. |
moduleNoEval() | modules | No eval() anywhere in module. |
moduleNoProcessEnv() | modules | No process.env anywhere in module. |
moduleNoConsoleLog() | modules | No console.log anywhere in module. |
ts-archunit/rules/errors
| Export | Target | Description |
|---|---|---|
noGenericErrors() | classes | No new Error() -- use typed domain errors. |
noTypeErrors() | classes | No new TypeError(). |
functionNoGenericErrors() | functions | No new Error() in functions. |
functionNoTypeErrors() | functions | No new TypeError() in functions. |
noSilentCatch() | classes | Catch blocks must reference the caught error. |
functionNoSilentCatch() | functions | Catch blocks must reference the caught error. |
moduleNoSilentCatch() | modules | Catch blocks must reference the caught error. |
ts-archunit/rules/naming
| Export | Description |
|---|---|
mustMatchName(re: RegExp) | Class name must match regex. |
mustNotEndWith(suffix: string) | Class name must not end with suffix. |
ts-archunit/rules/dependencies
| Export | Description |
|---|---|
onlyDependOn(...globs) | Module may only import from listed paths. |
mustNotDependOn(...globs) | Module must not import from listed paths. |
typeOnlyFrom(...globs) | Imports from listed paths must use import type. |
ts-archunit/rules/architecture
| Export | Target | Description |
|---|---|---|
mustCall(pattern) | functions | Function body must contain a call matching the regex. |
classMustCall(pattern) | classes | At least one class method must contain a matching call. |
ts-archunit/rules/hygiene
| Export | Target | Description |
|---|---|---|
noDeadModules() | modules | Module must be imported by at least one other file. |
noUnusedExports() | modules | Every named export must be referenced by another file. |
noStubComments(pattern?) | functions | No TODO/FIXME/HACK/STUB comments in a function's body or its own docstring. Markers are case-sensitive and must begin a comment line. |
noEmptyBodies() | functions | Functions must have at least one statement. |
ts-archunit/rules/metrics
| Export | Description |
|---|---|
maxCyclomaticComplexity(n) | No method/constructor/getter/setter exceeds complexity n. |
maxClassLines(n) | Class spans no more than n lines. |
maxMethodLines(n) | No method/constructor/getter/setter exceeds n lines. |
maxMethods(n) | Class has no more than n methods. |
maxParameters(n) | No method/constructor has more than n parameters. |
maxFunctionComplexity(n) | Function complexity does not exceed n. |
maxFunctionLines(n) | Function spans no more than n lines. |
maxFunctionParameters(n) | Function has no more than n parameters. |