@onezlinks/architecture-audit
v0.2.5
Published
> Config-driven architecture convention checks for TypeScript and React projects.
Readme
Architecture Audit CLI
Config-driven architecture convention checks for TypeScript and React projects.
@onezlinks/architecture-audit is a reusable command-line tool for enforcing architecture conventions that are difficult to express with TypeScript or ESLint alone. It checks how files, directories, barrels, hooks, and component exports are organized, while keeping project-specific policy in configuration instead of hardcoded rule logic.
Why use it
Large React codebases often drift in ways that normal linting does not catch:
- component directories without public barrels
- component implementations hidden inside
index.tsx - child components exported as defaults instead of named exports
- hook files that export non-hook runtime symbols
- duplicate component names inside the same owner directory
- broad
eslint-disablecomments without a clear reason
This package turns those conventions into repeatable local and CI checks.
Installation
yarn add -D @onezlinks/architecture-auditThe package requires Node.js 18 or newer.
{
"engines": {
"node": ">=18.0.0"
}
}Quick start
Add scripts to the target project:
{
"scripts": {
"audit:arch": "arch-audit",
"audit:arch:all": "arch-audit --all",
"audit:arch:changed": "arch-audit --changed"
}
}Create audit.config.json at the project root:
{
"changedBase": "origin/main",
"rulePolicy": {
"componentRoots": ["components"],
"hookRoots": ["hooks"],
"ignoredPathSegments": ["__tests__", "__mocks__", "__stories__"],
"componentExtensions": [".tsx"],
"hookExtensions": [".ts", ".tsx"],
"barrelFileNames": ["index.ts", "index.tsx"],
"indexComponentFileName": "index.tsx",
"singularTypeFileName": "type.ts",
"pluralTypeFileName": "types.ts",
"childComponentSegments": ["components"],
"mainComponentKinds": ["page-component", "component"],
"ignoredClassificationKinds": ["namespace", "test", "tooling"],
"componentFileNamePattern": "^[A-Z][A-Za-z0-9]*$",
"componentSymbolPattern": "^[A-Z][A-Za-z0-9]*$",
"hookExportPattern": "^use[A-Z0-9]",
"hookFileNamePattern": "^use[A-Z0-9].*\\.(ts|tsx)$"
},
"ignore": ["node_modules/", ".next/"],
"classify": {
"components/shared/primitives": "namespace"
},
"namespaceNames": ["primitives", "overlays", "data-display"],
"supportNames": ["state", "adapters"]
}Run the audit:
yarn audit:arch
yarn audit:arch --changed --base origin/main
yarn audit:arch components/pages/ExamplePage
yarn audit:arch --json
yarn audit:arch --markdown
yarn audit:arch --output-path reports/architecture-audit.jsonInstall the bundled Agent Skill into a supported agent directory:
yarn audit:arch install-skill
yarn audit:arch install-skill --list-targets
yarn audit:arch install-skill --target cursor --target codex
yarn audit:arch install-skill --target claude-code
yarn audit:arch install-skill --target-dir .agents/skills --forceCLI options
|Option|Description|
|---|---|
|install-skill|Install the bundled architecture-audit-configurator Agent Skill|
|--changed|Run changed-file mode and report only findings introduced since the selected git base; existing findings are informational|
|--base <ref>|Git base ref for --changed; defaults to changedBase, then origin/main|
|--root <path>|Project root directory|
|--target <agent>|Supported agent slug for install-skill; repeatable, comma-separated, or all|
|--list-targets|Print supported install targets and project paths|
|--target-dir <path>|Custom skill install directory that will contain the skill folder|
|--force|Replace an existing installed skill when using install-skill|
|--all|Run on the full codebase|
|--rule <id>|Run one or more specific rules; repeatable|
|--category <name>|Run one or more categories; repeatable|
|--format <fmt>|Print as stylish, compact, json, or markdown|
|--json|Write audit-architecture-report.json|
|--markdown|Write audit-architecture-report.md|
|--output-path <path>|Write a report to a custom path; format is inferred from extension|
|--severity <level>|Minimum severity for a non-zero exit; all new findings are reported: error, warning|
|--fix|Show suggestions only; does not modify files|
|--verbose|Include ignored findings and diagnostics|
|--help, -h|Show CLI help|
Exit codes
|Command|Exit code behavior|
|---|---|
|arch-audit|Exits 1 when findings match the selected severity|
|arch-audit --severity error|Exits 1 only when new errors are found; in --changed mode, existing findings do not affect the exit code|
|arch-audit --json|Writes a JSON report and exits 0|
|arch-audit --markdown|Writes a Markdown report and exits 0|
|arch-audit --output-path file.json|Writes a report and exits 0|
Report-generation modes are designed for CI artifact collection. Use console modes when the audit should block a build.
Rule reference
Barrel rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|BARREL-001|error|Page component directories must expose an index.ts or index.tsx barrel|Add an index barrel that re-exports the main component|
|BARREL-002|warning|Component subdirectories with two or more source files should expose a barrel|Add index.ts or index.tsx for the grouped component files|
|BARREL-003|error|Hook subdirectories must expose an index.ts barrel|Add an index.ts file that re-exports the hook module API|
|BARREL-004|warning|Re-export-only barrels should use .ts, not .tsx|Rename re-export-only index.tsx files to index.ts|
Naming rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|NAMING-001|error|A main component file should match its directory name, such as Button/Button.tsx|Rename the main component file to match the directory|
|NAMING-002|error|index.tsx should be a re-export barrel, not a component implementation|Move component code into a named component file and keep index.ts slim|
|NAMING-003|warning|Child components should use named exports instead of default exports|Replace default exports with export const or export function|
|NAMING-004|warning|Component source files should use PascalCase filenames|Rename component files to PascalCase|
|NAMING-005|warning|Type definition files should use the configured plural name, usually types.ts|Rename type.ts to types.ts|
|NAMING-006|error|Exported component runtime symbols must be unique inside the same owner directory|Rename duplicate exported component symbols|
Hook rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|HOOK-001|error|Runtime exports from hook source files must start with use|Rename the exported hook or move non-hook runtime code elsewhere|
|HOOK-002|warning|Hook files should use use*.ts or use*.tsx unless they are barrels|Rename the file or keep grouped re-exports in an index barrel only|
Structure rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|STRUCT-001|error|Page component files and directories must follow the matching directory pattern|Move the main page component into components/pages/<Name>/<Name>.tsx|
|STRUCT-002|warning|Directories with many loose component files should use a components/ subdirectory|Group child components under components/|
|STRUCT-003|warning|Shared composite components should follow the component directory pattern|Add a dedicated component directory with a main file and public barrel|
Import rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|IMPORT-001|warning|Component files should import from a barrel when the barrel exports that symbol|Import from the public or internal barrel instead of a deep path|
Export rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|EXPORT-001|warning|Main component files should export the component as default|Add a default export for the main component|
|EXPORT-002|warning|Child component files should not export components as default|Convert child component default exports into named exports|
Anti-pattern rules
|Rule|Severity|What it checks|How to fix|
|---|---|---|---|
|ANTI-001|warning|Component files should stay under the configured line limit|Split the component into smaller units|
|ANTI-002|warning|eslint-disable comments must be line-level and include a reason after --|Replace broad disables with a targeted line-level disable and reason|
|ANTI-003|warning|TSX files should not live in non-component directories|Move UI code into a component directory or update classifier config if valid|
|ANTI-004|warning|Component nesting should not exceed the configured depth|Flatten nested component ownership or extract shared pieces|
Recommended component structure
The default policy assumes a component directory shape like this:
ComponentName/
├── index.ts
├── types.ts
├── ComponentName.tsx
├── __tests__/
└── components/
├── index.ts
└── ChildComponent.tsxRecommended exports:
// ComponentName/index.ts
export { default } from "./ComponentName";
export type { ComponentNameProps } from "./types";// ComponentName/ComponentName.tsx
export default function ComponentName() {
return <div />;
}// ComponentName/components/ChildComponent.tsx
export function ChildComponent() {
return <div />;
}Configuration reference
|Key|Purpose|
|---|---|
|changedBase|Default git ref used by --changed|
|ignore|Project-level source roots to exclude from auditing|
|classify|Exact directory classification overrides|
|namespaceNames|Directory names treated as namespace/grouping directories|
|supportNames|Directory names treated as support directories|
|rulePolicy.componentRoots|Root directories that contain component source files|
|rulePolicy.hookRoots|Root directories that contain hook source files|
|rulePolicy.ignoredPathSegments|Path segments skipped by component and hook rules|
|rulePolicy.componentExtensions|Extensions considered component source files|
|rulePolicy.hookExtensions|Extensions considered hook source files|
|rulePolicy.barrelFileNames|Filenames treated as barrels|
|rulePolicy.indexComponentFileName|Filename checked for hidden component implementation|
|rulePolicy.singularTypeFileName|Disallowed singular type filename|
|rulePolicy.pluralTypeFileName|Preferred plural type filename|
|rulePolicy.childComponentSegments|Directory segments that mark child component files|
|rulePolicy.mainComponentKinds|Classifier kinds treated as main components|
|rulePolicy.ignoredClassificationKinds|Classifier kinds skipped by selected rules|
|rulePolicy.componentFileNamePattern|Regular expression for component filenames|
|rulePolicy.componentSymbolPattern|Regular expression for exported component symbols|
|rulePolicy.hookExportPattern|Regular expression for hook export names|
|rulePolicy.hookFileNamePattern|Regular expression for hook filenames|
|rulePolicy.looseComponentFileThreshold|Minimum loose component files before STRUCT-002 triggers|
|rulePolicy.compositeComponentFileThreshold|Minimum shared component files before STRUCT-003 triggers|
|rulePolicy.componentLineLimit|Maximum line count before ANTI-001 triggers|
|rulePolicy.componentNestingLimit|Maximum components/ nesting depth before ANTI-004 triggers|
Directory classification
The classifier helps rules understand whether a directory is a component, page component, namespace, support folder, test folder, or tooling folder.
Use classify for explicit overrides when a valid architecture pattern should not be treated as debt:
{
"classify": {
"components/shared/primitives": "namespace",
"hooks/state": "support"
}
}Use .auditignore only for known debt or temporary suppressions, not for valid architecture patterns.
Audit ignore file
Create .auditignore only when a finding needs to be suppressed temporarily. Each entry should have a preceding reason comment.
# Legacy component tree scheduled for migration in Q3
components/legacy/OldDashboardBroad wildcard ignores are intentionally discouraged so that important architecture drift remains visible.
CI examples
Block pull requests on newly introduced changed-file errors only. Existing findings from the git base are reported as informational and do not block:
yarn audit:arch --changed --severity errorGenerate a JSON artifact without blocking the build:
yarn audit:arch --json --severity warningGenerate a Markdown report for review comments or CI summaries:
yarn audit:arch --markdown --severity warningDesign principles
- Keep rule logic reusable and project-neutral.
- Put project-specific structure in
audit.config.json. - Prefer classifier/configuration changes for valid patterns.
- Use
.auditignoreonly for documented suppressions. - Keep
--fixsuggestion-only so the audit never rewrites source code unexpectedly. - Report parse failures as diagnostics instead of crashing the full audit.
Checklist
- [x] Install the package from the npm scope
- [x] Add audit scripts to the target project
- [x] Configure project conventions in
audit.config.json - [x] Run local architecture checks
- [x] Run changed-file checks in CI
- [x] Generate JSON or Markdown reports when artifacts are needed
