### Update VitePress Getting Started Guide Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0027-cli-watch-mode.md Replace the brief CLI section in the 'Getting Started' guide with a link to the dedicated CLI documentation page. ```markdown ## CLI ts-archunit also runs standalone without a test runner. See the [CLI documentation](/cli) for all commands and options. ``` -------------------------------- ### CI Integration Example Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cli.md Example GitHub Actions workflow step to run ts-archunit checks with GitHub Actions output format. ```yaml # .github/workflows/ci.yml - run: npx ts-archunit check arch.rules.ts --format github ``` -------------------------------- ### Install ts-archunit Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0043-docs-explain-recipes.md Install ts-archunit using npm or yarn. ```bash npm install ts-archunit --save-dev # or yarn add ts-archunit --dev ``` -------------------------------- ### Install Runtime and Development Dependencies Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0000-project-bootstrap.md Installs necessary runtime packages like ts-morph and picomatch, along with development dependencies for TypeScript, Vitest, ESLint, and Prettier. ```bash # Runtime dependencies npm install ts-morph@27 picomatch@4 # Dev dependencies npm install -D "typescript@~5.9.3" vitest@4 eslint@10 prettier@3 typescript-eslint@8 eslint-config-prettier@10 @eslint/js@10 @types/picomatch@4 ``` -------------------------------- ### Example ArchUnit.NET Class Rules Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0008-class-entry-point.md Demonstrates how to write architecture rules for classes using the `classes(p)` entry point. These examples show filtering by inheritance, abstract status, decorators, and location. ```typescript // All services extending BaseService must have an init() method classes(p).that().extend('BaseService').should().haveMethodNamed('init').check() // Abstract classes must reside in the domain folder classes(p).that().areAbstract().should().resideInFolder('**/domain/**').check() // Classes with @Controller decorator must be exported classes(p).that().haveDecorator('Controller').should().beExported().check() ``` ```typescript // Named selection: reuse predicate chain for multiple rules const repos = classes(p).that().extend('BaseRepository') repos.should().haveMethodNamed('findById').check() repos.should().beExported().check() ``` -------------------------------- ### Example Architecture Rule JSON Source: https://github.com/nielspeter/ts-archunit/blob/main/README.md An example of the structured JSON output for architecture rules, detailing rule ID, the rule itself, the reasoning, and a suggestion for improvement. ```json { "rules": [ { "id": "repo/typed-errors", "rule": "that extend 'BaseRepository' should not contain new 'Error'", "because": "Generic Error loses context and prevents consistent error handling", "suggestion": "Use NotFoundError, ValidationError, or DomainError instead" } ] } ``` -------------------------------- ### Install VitePress Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0023-user-guide-vitepress.md Install VitePress as a development dependency using npm. ```bash npm install -D vitepress ``` -------------------------------- ### Verification Commands Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0000-project-bootstrap.md Commands to verify the project setup, including type checking, linting, formatting, testing, and building. ```bash npm run typecheck # tsc --noEmit — zero errors npm run lint # eslint — zero errors npm run format:check # prettier — all files formatted npm run test # vitest — smoke test passes npm run build # tsc — compiles to dist/ ``` -------------------------------- ### Explain Command Usage Examples Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/explain.md Demonstrates how to use the ts-archunit explain command with different options for configuration files and output formats. ```bash npx ts-archunit explain arch.rules.ts ``` ```bash npx ts-archunit explain arch.rules.ts --markdown ``` ```bash npx ts-archunit explain arch.rules.ts --config ts-archunit.config.ts ``` -------------------------------- ### Rule File Example Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cli.md Example of a rule file exporting an array of rule builders using the ts-archunit API. ```typescript // arch.rules.ts import { project, classes, modules, call } from '@nielspeter/ts-archunit' const p = project('tsconfig.json') export default [ classes(p).that().extend('BaseRepository').should().notContain(call('parseInt')), modules(p) .that() .resideInFolder('src/domain/**') .should() .notImportFromCondition('src/repositories/**'), ] ``` -------------------------------- ### Install ts-archunit Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/getting-started.md Install the ts-archunit package as a development dependency. ```bash npm install -D @nielspeter/ts-archunit ``` -------------------------------- ### CLI Check Command Examples Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0027-cli-watch-mode.md Provides examples of how to use the `check` command in the ts-archunit CLI, including basic usage, with baseline, diff-aware, watch mode, and output formatting. ```bash # Run rules from a file npx ts-archunit check arch.rules.ts # Multiple rule files npx ts-archunit check layers.rules.ts naming.rules.ts body.rules.ts # With baseline (only new violations fail) npx ts-archunit check arch.rules.ts --baseline arch-baseline.json # Diff-aware (only report violations in changed files) npx ts-archunit check arch.rules.ts --changed --base main # Watch mode — re-run on file changes npx ts-archunit check arch.rules.ts --watch # Output format npx ts-archunit check arch.rules.ts --format github ``` -------------------------------- ### Quick Start with Layered Architecture Preset Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/getting-started.md Enforce a layered architecture pattern with a single function call using presets. This setup includes rules for layer ordering, cycle detection, isolation, and import restrictions. ```typescript import { describe, it } from 'vitest' import { project } from '@nielspeter/ts-archunit' import { layeredArchitecture } from '@nielspeter/ts-archunit/presets' const p = project('tsconfig.json') describe('Architecture', () => { it('enforces layered architecture', () => { layeredArchitecture(p, { layers: { routes: 'src/routes/**', services: 'src/services/**', repositories: 'src/repositories/**', }, shared: ['src/shared/**'], strict: true, }) }) }) ``` -------------------------------- ### Dynamic import() Examples Source: https://github.com/nielspeter/ts-archunit/blob/main/proposals/completed/004-detect-dynamic-imports.md These examples illustrate various ways dynamic `import()` can be used for lazy, conditional, or harness-based module loading. They highlight the need for static analysis to recognize these patterns. ```typescript // Lazy loading const { ResendService } = await import('./resend-service.js') // Test harness — loads modules lazily to avoid circular deps const { resetConfig } = await import('../src/config.js') // Conditional loading if (useNewImpl) { const { NewHandler } = await import('./new-handler.js') } ``` -------------------------------- ### CLI Baseline Command Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0027-cli-watch-mode.md Demonstrates how to generate a baseline JSON file using the `baseline` command, which records current violations for future filtering. ```bash # Generate baseline from current violations npx ts-archunit baseline arch.rules.ts --output arch-baseline.json ``` -------------------------------- ### MCP Server Configuration Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/0044-ai-agent-integration.md Example configuration for setting up an MCP server for ts-archunit. This JSON defines the command and arguments to launch the server. ```json { "mcpServers": { "ts-archunit": { "command": "npx", "args": ["ts-archunit", "mcp"] } } } ``` -------------------------------- ### Example Slice Definition Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0012-slice-entry-point.md Illustrates how to define layers like presentation, application, and domain using glob patterns. ```typescript const layers: SliceDefinition = { presentation: 'src/controllers **', application: 'src/services **', domain: 'src/domain **', } ``` -------------------------------- ### TypeScript Configuration File Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cli.md Example configuration file for ts-archunit, specifying project, rules, baseline, format, and watch directories. ```typescript import { defineConfig } from '@nielspeter/ts-archunit' export default defineConfig({ project: 'tsconfig.json', rules: ['arch.rules.ts'], baseline: 'arch-baseline.json', format: 'auto', watchDirs: ['src'], // directories to watch in --watch mode }) ``` -------------------------------- ### Rule File Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0027-cli-watch-mode.md Define architectural rules by exporting an array of rule builders from a rule file. This example shows rules for checking class extensions and module imports. ```typescript // arch.rules.ts import { project, classes, modules, call } from 'ts-archunit' const p = project('tsconfig.json') export default [ classes(p).that().extend('BaseRepository').should().notContain(call('parseInt')), modules(p) .that() .resideInFolder('src/domain/**') .should() .notImportFromCondition('src/repositories/**'), ] ``` -------------------------------- ### ts-archunit Explain Command Output Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0043-docs-explain-recipes.md Example JSON output from the 'ts-archunit explain' command, detailing active architecture rules, their descriptions, reasons, and suggestions. ```bash $ npx ts-archunit explain { "project": "tsconfig.json", "rules": [ { "id": "preset/layered/layer-order", "description": "that reside in layer should respect layer order", "because": "Enforces clean architecture dependency flow", "suggestion": "Move logic to the appropriate layer instead of skipping layers", "severity": "error" }, { "id": "preset/boundaries/no-cycles", "description": "that reside in boundary should be free of cycles", "because": "Cycles destroy modularity and make reasoning impossible", "severity": "error" } ], "generatedAt": "2026-03-30T12:00:00Z" } ``` -------------------------------- ### Agent-Optimized Markdown Output Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/0044-ai-agent-integration.md Example of the imperative markdown format generated by ts-archunit for AI agents. Includes sections for Layer Rules, Code Patterns, Hygiene, and Required Patterns. ```markdown ## Architecture Rules (auto-generated by ts-archunit) The following rules are enforced by CI. Violations will block your PR. ### Layer Rules - Do NOT import from `**/repositories/**` in files under `**/routes/**` — routes must go through services - Do NOT import from `**/infrastructure/**` in files under `**/domain/**` — domain must be independent ### Code Patterns - Do NOT throw `new Error()` in classes extending BaseRepository — use NotFoundError, ValidationError, or DomainError - Do NOT call `parseInt` in classes extending BaseRepository — use this.extractCount() instead - Do NOT call `eval()` anywhere in source code ### Hygiene - Do NOT leave empty function bodies — every function must have at least one statement - Do NOT leave stub comments in source code - Every exported symbol must be referenced by at least one other file ### Required Patterns - Functions in `**/services/**` MUST call a method matching /Repository/ — services must delegate to the data layer ``` -------------------------------- ### Function Rule Builder Examples Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0009-function-entry-point.md Examples demonstrating how to use the FunctionRuleBuilder to define architecture rules. These snippets show how to filter functions by various criteria and assert conditions. ```typescript import type { ArchProject } from '../core/project.js' import type { Predicate } from '../core/predicate.js' import { RuleBuilder } from '../core/rule-builder.js' import type { ArchFunction } from '../models/arch-function.js' import { collectFunctions } from '../models/arch-function.js' import { haveNameMatching as identityHaveNameMatching, haveNameStartingWith as identityHaveNameStartingWith, haveNameEndingWith as identityHaveNameEndingWith, resideInFile as identityResideInFile, resideInFolder as identityResideInFolder, areExported as identityAreExported, areNotExported as identityAreNotExported, } from '../predicates/identity.js' import { areAsync as fnAreAsync, areNotAsync as fnAreNotAsync, haveParameterCount as fnHaveParameterCount, haveParameterCountGreaterThan as fnHaveParameterCountGreaterThan, haveParameterCountLessThan as fnHaveParameterCountLessThan, haveParameterNamed as fnHaveParameterNamed, haveReturnType as fnHaveReturnType, } from '../predicates/function.js' /** * Rule builder for function-level architecture rules. * * Operates on both FunctionDeclarations and const arrow functions, * unified through the ArchFunction model. * * @example * ```typescript * // No function should have more than 5 parameters * functions(project) * .that().haveParameterCountGreaterThan(5) * .should(notExist()) * .because('functions with many parameters are hard to use') * .check() * * // All exported async functions should have names starting with a verb * functions(project) * .that().areExported().and().areAsync() * .should().haveNameMatching(/^(get|find|create|update|delete|fetch|load|save)/) * .because('async functions should use verb prefixes') * .check() * ``` */ export class FunctionRuleBuilder extends RuleBuilder { protected getElements(): ArchFunction[] { return this.project.getSourceFiles().flatMap(collectFunctions) } // --- Identity predicates (delegated to plan 0003 generics) --- haveNameMatching(pattern: RegExp | string): this { return this.addPredicate(identityHaveNameMatching(pattern)) } haveNameStartingWith(prefix: string): this { return this.addPredicate(identityHaveNameStartingWith(prefix)) } haveNameEndingWith(suffix: string): this { return this.addPredicate(identityHaveNameEndingWith(suffix)) } resideInFile(glob: string): this { return this.addPredicate(identityResideInFile(glob)) } resideInFolder(glob: string): this { return this.addPredicate(identityResideInFolder(glob)) } areExported(): this { return this.addPredicate(identityAreExported()) } areNotExported(): this { return this.addPredicate(identityAreNotExported()) } // --- Function-specific predicates --- areAsync(): this { return this.addPredicate(fnAreAsync()) } areNotAsync(): this { return this.addPredicate(fnAreNotAsync()) } haveParameterCount(n: number): this { return this.addPredicate(fnHaveParameterCount(n)) } haveParameterCountGreaterThan(n: number): this { return this.addPredicate(fnHaveParameterCountGreaterThan(n)) } haveParameterCountLessThan(n: number): this { return this.addPredicate(fnHaveParameterCountLessThan(n)) } haveParameterNamed(name: string): this { return this.addPredicate(fnHaveParameterNamed(name)) } haveReturnType(pattern: RegExp | string): this { return this.addPredicate(fnHaveReturnType(pattern)) } } /** * Entry point for function-level architecture rules. * * Scans all source files in the project for both FunctionDeclarations * and const arrow functions (VariableDeclaration with ArrowFunction initializer). * * @example * ```typescript * import { project, functions } from 'ts-archunit' * * const p = project('tsconfig.json') * * // All parseXxxOrder functions should not exist * functions(p) * .that().haveNameMatching(/^parse\w+Order$/) * .should(notExist()) * .because('use shared parseOrder() utility instead') * .check() * ``` */ export function functions(p: ArchProject): FunctionRuleBuilder { return new FunctionRuleBuilder(p) } ``` -------------------------------- ### Rule File Contract Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0020-cli-runner.md Illustrates a rule file exporting an array of rule builders for ts-archunit. This example defines rules for class extensions and module imports. ```typescript // arch.rules.ts import { project, classes, modules, call } from 'ts-archunit' const p = project('tsconfig.json') export default [ classes(p).that().extend('BaseRepository').should().notContain(call('parseInt')), modules(p).that().resideInFolder('src/domain/**').should().notImportFrom('src/repositories/**'), ] ``` -------------------------------- ### Documentation: `dependOn` in `what-to-check.md` Source: https://github.com/nielspeter/ts-archunit/blob/main/proposals/completed/005-builtin-imports-from-condition.md Presents a concise one-liner example in `what-to-check.md` for asserting that the server must depend on security middleware. ```typescript // Server must depend on security middleware modules(p).that().resideInFile('**/server.ts').should().satisfy(dependOn('**/security/**')).check() ``` -------------------------------- ### Real-World Example: Consistent Pagination Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/graphql.md Combines schema rules to enforce standard pagination envelopes on collection types and pagination arguments on list queries. ```typescript const s = schema(p, 'src/graphql/**/*.graphql') // All collection types must have the standard envelope s.that() .typesNamed(/Collection$/) .should() .haveFields('items', 'total', 'skip', 'limit') .check() // All list queries must accept pagination args s.that().queries().and().returnListOf().should().acceptArgs('skip', 'limit').check() ``` -------------------------------- ### Framework Package Structure Example Source: https://github.com/nielspeter/ts-archunit/blob/main/adr/006-framework-rules-architecture.md Framework packages, such as `@ts-archunit/fastify`, export thematic modules containing rules, predicates, and presets. ```typescript @ts-archunit/fastify rules/routes.ts — routesMustHaveSchema(), routesMustHaveAuth() rules/plugins.ts — pluginsMustBeEncapsulated() predicates/fastify.ts — isRouteHandler(), isFastifyPlugin() presets/recommended.ts index.ts ``` -------------------------------- ### Setup ts-morph Project and Fixture Class Retrieval Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0008-class-entry-point.md Initializes a ts-morph Project with a specific tsconfig and provides a helper function to retrieve ClassDeclarations by name from the project's source files. This setup is crucial for evaluating class conditions. ```typescript import { Project, type ClassDeclaration } from 'ts-morph' import path from 'node:path' const fixturesDir = path.resolve(import.meta.dirname, '../fixtures/poc') const tsconfigPath = path.join(fixturesDir, 'tsconfig.json') const project = new Project({ tsConfigFilePath: tsconfigPath }) function getClass(name: string): ClassDeclaration { for (const sf of project.getSourceFiles()) { const cls = sf.getClass(name) if (cls) return cls } throw new Error(`Class ${name} not found in fixtures`) } ``` -------------------------------- ### Example Usage of resolveByMatching Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0012-slice-entry-point.md Demonstrates how to use `resolveByMatching` to group source files into slices based on a glob pattern like 'src/features/*\/'. ```typescript resolveByMatching(project, 'src/features/*\/') // => [{ name: 'auth', files: [...] }, { name: 'billing', files: [...] }] ``` -------------------------------- ### Documentation: `dependOn` in Available Conditions Table Source: https://github.com/nielspeter/ts-archunit/blob/main/proposals/completed/005-builtin-imports-from-condition.md Illustrates how the `dependOn` condition is documented in `modules.md`, specifying its purpose and providing a usage example. ```markdown | `dependOn(...globs)` | Module must import from at least one path matching a glob | `.should().satisfy(dependOn('**/logging/**'))` | ``` -------------------------------- ### Example Usage of resolveByDefinition Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0012-slice-entry-point.md Shows how to use `resolveByDefinition` with a map of slice names to glob patterns for defining slices like 'presentation' and 'domain'. ```typescript resolveByDefinition(project, { presentation: 'src/controllers **', domain: 'src/domain **', }) ``` -------------------------------- ### Get Element Line Number Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0004-condition-engine.md Confirms that `getElementLine` correctly returns the starting line number of a code element. ```typescript // getElementLine returns start line — verify `getElementLine` returns the correct line number. ``` -------------------------------- ### Initialize Git Repository and Commit Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0000-project-bootstrap.md Initializes a new Git repository and makes the first commit with all added files. ```bash git init git add . git commit -m "chore: initial project setup" ``` -------------------------------- ### Basic Cross-Layer Validation Setup Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cross-layer.md Sets up cross-layer validation by defining layers for routes and schemas and checking for matching counterparts. Ensure ts-archunit is installed and imported. ```typescript import { project, crossLayer, haveMatchingCounterpart } from '@nielspeter/ts-archunit' const p = project('tsconfig.json') const layers = crossLayer(p).layer('routes', 'src/routes/**').layer('schemas', 'src/schemas/**') const resolved = layers.mapping( (a, b) => a.getBaseName().replace('-route.ts', '') === b.getBaseName().replace('-schema.ts', '')) resolved .forEachPair() .should(haveMatchingCounterpart(/* pass resolved layers */)) .because('every route must have a matching schema') .check() ``` -------------------------------- ### Function Predicate: Parameter Name Matching Regex Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/functions.md Use `haveParameterNameMatching(regex)` to check if any parameter name in a function matches the provided regular expression. The example checks for parameter names starting with 'ctx'. ```typescript .that().haveParameterNameMatching(/^ctx/) ``` -------------------------------- ### Initialize npm Package Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0000-project-bootstrap.md Initializes a new npm package with default settings. This is the first step in setting up a new Node.js project. ```bash npm init -y ``` -------------------------------- ### ArchCall Interface Definition Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0014-call-entry-point.md Defines the structure for a unified representation of a call expression, including methods to get its name, source file, object name, method name, arguments, underlying ts-morph node, and start line number. ```typescript import { type CallExpression, type SourceFile, type Node, Node as NodeUtils, SyntaxKind, } from 'ts-morph' /** * Unified representation of a call expression in the project. * * Wraps a ts-morph CallExpression with precomputed fields for * efficient predicate evaluation. * * Satisfies Named and Located interfaces from identity predicates. * Does NOT satisfy Exportable --- call expressions cannot be exported. */ export interface ArchCall { /** Full expression text, e.g. "app.get", "router.post", "db.query" */ getName(): string | undefined /** Source file containing this call expression. */ getSourceFile(): SourceFile /** The object the method is called on, or undefined for bare calls. */ getObjectName(): string | undefined /** The method name, or the function name for bare calls. */ getMethodName(): string | undefined /** The arguments to the call expression. */ getArguments(): Node[] /** Underlying ts-morph CallExpression node. */ getNode(): CallExpression /** Start line number in the source file. */ getStartLineNumber(): number } ``` -------------------------------- ### GraphQL Schema Predicates and Builder Chain Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0021-graphql-extension.md Demonstrates how to chain schema predicates and builder methods to select specific schema elements for architectural rules. Use `schema(p, glob)` to start, followed by predicates like `queries()` or `typesNamed(/Collection$)/` to filter, and then `should()` to apply assertions. ```typescript schema(p, glob).queries().should()... schema(p, glob).typesNamed(/Collection$/).should()... ``` -------------------------------- ### Create Project Directories Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0000-project-bootstrap.md Creates the necessary directory structure for the project's source code and tests. ```bash mkdir -p src/core src/builders src/predicates src/conditions src/helpers src/smells mkdir -p tests/fixtures tests/predicates tests/conditions tests/integration ``` -------------------------------- ### Check Options: withBaseline Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/api-reference.md Loads a baseline file for gradual adoption of architectural rules. ```APIDOC ## withBaseline ### Description Load a baseline file for gradual adoption. ### Signature `withBaseline(path: string): Baseline` ``` -------------------------------- ### Install GraphQL Peer Dependency Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/graphql.md Install the 'graphql' package as a dev dependency, which is required for ts-archunit's GraphQL module. ```bash npm install -D graphql # required for ts-archunit/graphql ``` -------------------------------- ### Logging Architecture Rules in CI Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/explain.md Demonstrates how to log the output of the explain command within a CI/CD pipeline for auditing purposes. ```yaml - name: Log architecture rules run: npx ts-archunit explain arch.rules.ts ``` -------------------------------- ### Module Body Analysis - Example: No console.log in production code Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0041-architecture-rule-primitives.md Example of using `notContain` to prevent `console.log` calls in production code directories. ```ts // No console.log in production code modules(p).that().resideInFolder('**/src/**').should().notContain(call('console.log')).check() ``` -------------------------------- ### Example Module Rule Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0007-module-entry-point.md Demonstrates how to use the `modules` entry point to define a rule that restricts imports within specific folders. This is useful for enforcing architectural boundaries. ```typescript modules(project) .that() .resideInFolder('**/domain/**') .should() .onlyImportFrom('**/domain/**', '**/shared/**') .because('domain modules must not depend on infrastructure') .check() ``` -------------------------------- ### Basic Function Rule Example Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/functions.md This snippet demonstrates how to import necessary functions from ts-archunit, initialize a project, and apply a rule to check if functions residing in '**/handlers/**' are asynchronous. Ensure you have a 'tsconfig.json' file in your project root. ```typescript import { project, functions } from '@nielspeter/ts-archunit' const p = project('tsconfig.json') functions(p).that().resideInFolder('**/handlers/**').should().beAsync().check() ``` -------------------------------- ### Module Body Analysis - Example: No eval at module scope Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0041-architecture-rule-primitives.md Example of using `notContain` to disallow the use of `eval` at module scope or anywhere within a file. ```ts // No eval at module scope or anywhere in a file modules(p).should().notContain(call('eval')).check() ``` -------------------------------- ### Module Body Analysis - Example: No process.env in domain modules Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0041-architecture-rule-primitives.md Example of using `notContain` to ensure no `process.env` is accessed anywhere within specified domain modules. ```ts // No process.env anywhere in domain modules modules(p).that().resideInFolder('**/domain/**').should().notContain(access('process.env')).check() ``` -------------------------------- ### TypeRuleBuilder Example Usage Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0010-type-entry-point.md Demonstrates how to use the TypeRuleBuilder to enforce architectural rules on types. This example filters for types with a 'sortBy' property and checks if its type is not a bare string. ```typescript import { types } from 'ts-archunit' import { not, isString } from 'ts-archunit/predicates' // Assuming 'project' is an instance of ArchProject types(project) .that().haveProperty('sortBy') .should().havePropertyType('sortBy', not(isString())) .because('sortBy must be a union of literals, not bare string') .check() ``` -------------------------------- ### Example Usage of SliceRuleBuilder Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0012-slice-entry-point.md An example demonstrating how to use the SliceRuleBuilder to check for cycles within specific code slices. This involves matching a path pattern and asserting a condition. ```typescript slices(project) .matching('src/features/*/') .should().beFreeOfCycles() .check() ``` -------------------------------- ### Run Multiple Rule Files Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cli.md Execute architecture rules from multiple specified files. ```bash npx ts-archunit check layers.rules.ts naming.rules.ts body.rules.ts ``` -------------------------------- ### Verify Schema and SDK Client Consistency Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cross-layer.md This example ensures that SDK clients are consistent with their corresponding schema definitions. It maps schemas to SDK files and verifies that all exported types from the schema are present in the client. ```typescript crossLayer(p) .layer('schemas', 'src/schemas/**') .layer('sdk', 'src/sdk/**') .mapping((schema, client) => { const name = schema.getBaseName().replace('-schema.ts', '') return client.getBaseName() === `${name}-client.ts` }) .forEachPair() .should( haveConsistentExports( (file) => file.getExportedDeclarations().keys().toArray(), (file) => file.getExportedDeclarations().keys().toArray(), ), ) .because('SDK clients must expose every type defined in the schema') .check() ``` -------------------------------- ### Piping Explain Output to AI Context Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/explain.md Example of redirecting the JSON output of the explain command to a file for use in an AI system's prompt or context. ```bash npx ts-archunit explain arch.rules.ts > .arch-rules.json # Add to CLAUDE.md or system prompt ``` -------------------------------- ### Route imports exclusion example Source: https://github.com/nielspeter/ts-archunit/blob/main/proposals/completed/per-rule-exclusions.md Enforce that route imports only include type imports from specific repositories. This example uses `.excluding()` to ignore 'internal-routes', ensuring compliance with defined architectural patterns. ```typescript modules(p) .that() .resideInFolder('**/routes/**') .should() .onlyHaveTypeImportsFrom('**/repositories/**') .excluding('internal-routes') .check() // ← now enforced ``` -------------------------------- ### Common npm Commands Source: https://github.com/nielspeter/ts-archunit/blob/main/CLAUDE.md Provides a list of common npm commands for running tests, linting, formatting, type checking, and building the project. ```bash npm run test # run vitest ``` ```bash npm run lint # eslint ``` ```bash npm run format # prettier --write ``` ```bash npm run typecheck # tsc --noEmit ``` ```bash npm run build # tsc (emit to dist/) ``` -------------------------------- ### Run Rules from a File Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cli.md Execute architecture rules defined in a specified file. ```bash npx ts-archunit check arch.rules.ts ``` -------------------------------- ### Configurable Logger Patterns Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0045-no-silent-catch.md This example shows an optional configuration for the 'noSilentCatch' rule to enforce that referenced errors are actually used in logging calls, preventing false positives where errors are referenced but not logged. ```typescript noSilentCatch({ logPatterns: [/logger\./, /Sentry\.capture/, /console\.(error|warn)/] }) ``` -------------------------------- ### CLI Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/api-reference.md Functions for configuring the Command Line Interface and managing project cache. ```APIDOC ## CLI ### `defineConfig` Define CLI configuration file. **Signature:** `defineConfig(config: CliConfig): CliConfig` ### `resetProjectCache` Clear the project singleton cache. Used by watch mode and tests. **Signature:** `resetProjectCache(): void` ``` -------------------------------- ### Nested Scoping Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0015-within-scoped-rules.md Illustrates the concept of nested scoping using the within() function. This example shows how multiple within() calls could potentially be chained to define increasingly specific rule contexts. ```typescript within(outerCalls).within(innerCalls).functions().should()... ``` -------------------------------- ### Verification Commands Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/0044-ai-agent-integration.md Commands to verify the project's build, type checking, linting, and documentation generation. ```bash npm run test npm run typecheck npm run lint npm run docs:build ``` -------------------------------- ### Predicate vs. Condition Usage Example Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0008-class-entry-point.md Illustrates the difference between using a method as a predicate (filtering after `.that()`) and as a condition (asserting after `.should()`). This example shows how to filter classes extending 'BaseService' and then assert that they are exported. ```typescript // Predicate: filter to classes extending BaseService classes(p).that().extend('BaseService').should().beExported().check() ``` -------------------------------- ### CLI explain Command Implementation Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0043-docs-explain-recipes.md Skeleton for the 'explain' subcommand implementation in the ts-archunit CLI. It loads configuration, collects descriptions, and outputs JSON. ```typescript // src/cli/explain.ts export async function explain(configPath: string): Promise { const config = await loadConfig(configPath) // Run config with explain: true on all presets // Collect descriptions from all rule builders // Output JSON to stdout } ``` -------------------------------- ### Explain Rule Metadata (JSON) Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/cli.md Output structured metadata (ID, description, suggestion) for all rules in a file in JSON format. ```bash npx ts-archunit explain arch.rules.ts ``` -------------------------------- ### Get Element Name from Function Declaration Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0004-condition-engine.md Verifies that `getElementName` correctly extracts the name from a `FunctionDeclaration` node. ```typescript // getElementName returns function name — load a `FunctionDeclaration`, verify name extraction. ``` -------------------------------- ### Get Element Name from Class Declaration Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0004-condition-engine.md Verifies that `getElementName` correctly extracts the name from a `ClassDeclaration` node. ```typescript // getElementName returns class name — load a `ClassDeclaration`, verify `getElementName` returns the class name. ``` -------------------------------- ### Import GraphQL Module Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/graphql.md Import necessary functions from the ts-archunit GraphQL module to start defining rules. ```typescript import { schema, schemaFromSDL, resolvers } from '@nielspeter/ts-archunit/graphql' ``` -------------------------------- ### Deploy GitHub Actions Workflow for Docs Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0023-user-guide-vitepress.md This workflow deploys documentation using GitHub Actions. It checks out code, sets up Node.js, installs dependencies, builds the documentation, uploads artifacts, and deploys to GitHub Pages. ```yaml # .github/workflows/docs.yml name: Deploy Docs on: push: branches: [main] paths: ['docs/**'] jobs: deploy: runs-on: ubuntu-latest permissions: pages: write id-token: write steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci - run: npm run docs:build - uses: actions/upload-pages-artifact@v3 with: path: docs/.vitepress/dist - uses: actions/deploy-pages@v4 ``` -------------------------------- ### Usage Examples for `notImportFrom` with Options Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0038-notimportfrom-type-import-awareness.md Demonstrates how to use the `notImportFrom` condition, both in its original form and with the new `ignoreTypeImports` option. ```typescript // Simple (existing, unchanged) modules(p).should().notImportFrom('**/infra/**').check() // With options (new) modules(p).should().notImportFrom(['**/infra/**'], { ignoreTypeImports: true }).check() ``` -------------------------------- ### CLI Commands for ts-archunit Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0020-cli-runner.md Demonstrates various commands for running ts-archunit from the CLI, including checking rules, generating baselines, and specifying output formats. ```bash # Run rules from a file npx ts-archunit check arch.rules.ts # Watch mode — re-run on file changes npx ts-archunit check --watch # Generate baseline npx ts-archunit baseline --output arch-baseline.json # Check with baseline (only new violations fail) npx ts-archunit check --baseline arch-baseline.json # Diff-aware (only report violations in changed files) npx ts-archunit check --changed --base main # Output format npx ts-archunit check --format github ``` -------------------------------- ### ArchUnit.ts End-to-End: Custom Predicates Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0014-call-entry-point.md End-to-end test example showing how to define and use custom predicates with the CallRuleBuilder. ```typescript describe('custom predicates via .satisfy()') - definePredicate works with CallRuleBuilder ``` -------------------------------- ### Building a Rule Chain Source: https://github.com/nielspeter/ts-archunit/blob/main/docs/core-concepts.md Provides an example of constructing a rule using ts-archunit's fluent API, illustrating the entry point, predicate, condition, and execution phases. ```typescript classes(p) // 1. entry point: class declarations .that() // 2. start filtering .extend('BaseService') // 2. predicate: only classes extending BaseService .should() // 3. start asserting .notContain(call('parseInt')) // 3. condition: must not call parseInt .check() // 4. execute ``` -------------------------------- ### Vitest Smoke Test Source: https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0000-project-bootstrap.md A basic smoke test to verify that the project setup, including ts-morph, is working correctly. ```typescript import { describe, it, expect } from 'vitest' import { Project } from 'ts-morph' describe('smoke test', () => { it('ts-morph loads a project', () => { const project = new Project({ useInMemoryFileSystem: true }) const sourceFile = project.createSourceFile('test.ts', 'export class Foo extends Bar {}') const classes = sourceFile.getClasses() expect(classes).toHaveLength(1) expect(classes[0].getName()).toBe('Foo') expect(classes[0].getExtends()?.getText()).toBe('Bar') }) }) ``` -------------------------------- ### Cross-Entry Point Composition Example Source: https://github.com/nielspeter/ts-archunit/blob/main/proposals/completed/010-jsx-element-rules.md Demonstrates composing JSX element rules with other entry points, such as checking for the absence of specific JSX elements within functions. ```typescript Prominently feature cross-entry-point composition (`functions().should().notContain(jsxElement('div'))`) ```