### Install Dependencies Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Install the necessary Node.js dependencies for the project. ```bash npm install ``` -------------------------------- ### Install ArchUnit dependency Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Install the package as a development dependency using npm. ```bash npm install archunit --save-dev ``` -------------------------------- ### Basic Pattern Matching for Files and Metrics Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Examples of using projectFiles and metrics modules to enforce basic architectural rules based on file names, folders, and paths. ```typescript import { projectFiles, metrics } from 'archunit'; // Files module - Test architectural rules projectFiles().withName('*.service.ts').should().beInFolder('**/services/**'); // Metrics module - Test only service classes metrics().withName('*.service.ts').lcom().lcom96b().shouldBeBelow(0.7); // Files module - Test classes in specific folders projectFiles() .inFolder('**/controllers/**') .shouldNot() .dependOnFiles() .inFolder('**/database/**'); // Metrics module - Test classes in specific folders metrics().inFolder('**/controllers/**').count().methodCount().shouldBeBelow(20); // Files module - Test classes matching full path patterns projectFiles().inPath('src/domain/**/*.ts').should().haveNoCycles(); // Metrics module - Test classes matching full path patterns metrics().inPath('src/domain/**/*.ts').lcom().lcom96a().shouldBeBelow(0.8); ``` -------------------------------- ### Sample Debug Output Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Example of the detailed analysis logs generated when debug logging is enabled. ```text [2025-06-02T12:08:26.355Z] [INFO] Starting architecture rule check: Dependency check: patterns [(^|.*/)src/database/.*] [2025-06-02T12:08:26.445Z] [DEBUG] Analyzing 12 files in 'src/presentation' folder [2025-06-02T12:08:26.456Z] [DEBUG] Found file: src/presentation/controllers/UserController.ts [2025-06-02T12:08:26.467Z] [DEBUG] Found file: src/presentation/views/UserView.tsx [2025-06-02T12:08:26.478Z] [DEBUG] Checking dependencies against 'src/database' pattern [2025-06-02T12:08:26.489Z] [DEBUG] Violation detected: src/presentation/controllers/UserController.ts depends on src/database/UserRepository.ts [2025-06-02T12:08:26.772Z] [WARN] Completed architecture rule check: Dependency check: patterns [(^|.*/)src/database/.*] (1 violations) ``` -------------------------------- ### CI Pipeline Integration for Logs Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Configuration examples for uploading log artifacts in GitHub Actions and GitLab CI. ```yaml # GitHub Actions example - name: Run Architecture Tests run: npm test - name: Upload Test Logs if: always() uses: actions/upload-artifact@v3 with: name: architecture-test-logs path: logs/ ``` ```yaml # GitLab CI example test: script: - npm test artifacts: when: always paths: - logs/ expire_in: 1 week ``` -------------------------------- ### Conventional Commit Examples Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Examples of commit messages following the Conventional Commits specification for automated versioning and changelog generation. ```markdown fix: handle empty tsconfig in graph extraction feat: add support for JS file analysis feat!: remove deprecated matchFilename API ``` -------------------------------- ### Configure Case Sensitivity Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Examples of case-sensitive versus case-insensitive regex matching. ```typescript // Case sensitive regex (default) .withName(/^.*service\.ts$/) // Matches service.ts ``` ```typescript // Case insensitive regex .withName(/^.*service\.ts$/i) // Matches Service.ts, service.ts, SERVICE.ts ``` -------------------------------- ### Vitest Example with LCOM Metrics Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md Demonstrates using ArchUnitTS with Vitest to check the LCOM (Lack of Cohesion in Methods) metric for folders. Note that architecture tests can be time-consuming; adjust Vitest timeouts if necessary. ```typescript import { describe, it, expect } from 'vitest'; import { metrics } from 'archunit'; describe('architecture', () => { // architecture tests can take a while to finish // Vitest default timeout is 5000ms. If needed, configure in vitest.config.ts or use test.setTimeout it('lcom should be below 0.5 in business', async () => { const rule = metrics() .inFolder('business') .lcom() .lcom96b() .shouldBeBelowOrEqual(0.5); await expect(rule).toPassAsync(); }); it('lcom should be below 0.5 ui', async () => { const rule = metrics().inFolder('ui').lcom().lcom96b().shouldBeBelow(0.5); await expect(rule).toPassAsync(); }); }); ``` -------------------------------- ### Common Glob Pattern Examples Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Collection of common glob patterns for filenames, folders, and paths. ```typescript // Filename patterns .withName('*.ts') // All TypeScript files .withName('*.{js,ts}') // All JavaScript or TypeScript files .withName('*Service.ts') // Files ending with 'Service.ts' .withName('User*.ts') // Files starting with 'User' .withName('?est.ts') // test.ts, nest.ts, etc // Folder patterns .inFolder('**/services') // Any 'services' folder at any depth .inFolder('src/services') // Exact 'src/services' folder .inFolder('**/test/**') // Any folder containing 'test' in path .inFolder('src/*') // Direct subfolders of 'src' // Path patterns .inPath('src/**/*.service.ts') // Service files anywhere under src .inPath('**/test/**/*.spec.ts') // Test files in any test folder .inPath('src/domain/*/*.ts') // TypeScript files one level under domain ``` -------------------------------- ### Enforce folder rules with RegExp Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Example of using a regular expression to define folder constraints in ArchUnitTS. ```typescript .inFolder(/.*\/components\/.*/) ``` -------------------------------- ### Automatic Framework Integration Import Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md This import triggers ArchUnitTS's automatic detection and setup for compatible testing frameworks without requiring explicit configuration. ```typescript import { ... } from 'archunit'; ``` -------------------------------- ### Mocha Architecture Test Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md This example shows how to write architecture tests using Mocha. It requires importing 'describe' and 'it' from 'mocha', 'assert' from 'assert', and 'projectFiles' from 'archunit'. Violations are checked using 'rule.check()'. ```typescript import { describe, it } from 'mocha'; import { strict as assert } from 'assert'; import { projectFiles } from 'archunit'; describe('Architecture Tests', () => { it('tests layer dependencies', async () => { const rule = projectFiles() .inFolder('src/domain') .shouldNot() .dependOnFiles() .inFolder('src/presentation'); const violations = await rule.check(); assert.equal(violations.length, 0, 'Architecture rule violated'); }); }); ``` -------------------------------- ### QUnit Architecture Test Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md An example of an architecture test written for QUnit. It imports 'projectFiles' from 'archunit' and uses QUnit's test function and assert object to verify the rule. ```typescript import { projectFiles } from 'archunit'; QUnit.test('architecture test', async (assert) => { const rule = projectFiles() .inFolder('src/domain') .shouldNot() .dependOnFiles() .inFolder('src/presentation'); const violations = await rule.check(); assert.equal(violations.length, 0, 'Architecture rule should pass'); }); ``` -------------------------------- ### Configure Jasmine for ArchUnitTS Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Setup required for Jasmine to use the asynchronous matcher. ```typescript beforeEach(() => { jasmine.addAsyncMatchers(jasmineMatcher); }); ``` ```typescript describe('architecture', () => { beforeEach(() => { jasmine.addAsyncMatchers(jasmineMatcher); }); it('business logic should not depend on the ui', async () => { const rule = projectFiles() .inFolder('business') .shouldNot() .dependOnFiles() .inFolder('ui'); await expectAsync(rule).toPassAsync(); // expectAsync, not expect !! }); ``` -------------------------------- ### AST Traversal Example Source: https://github.com/lukasniessen/archunitts/blob/main/info/TECHNICAL.md Illustrates a pseudo-code approach to visiting nodes within a TypeScript Abstract Syntax Tree (AST) to identify different code constructs like import declarations, class declarations, and call expressions. ```typescript function visitNode(node: ts.Node, sourceFile: ts.SourceFile) { switch (node.kind) { case ts.SyntaxKind.ImportDeclaration: handleImportDeclaration(node as ts.ImportDeclaration); break; case ts.SyntaxKind.ClassDeclaration: handleClassDeclaration(node as ts.ClassDeclaration); break; case ts.SyntaxKind.CallExpression: handleCallExpression(node as ts.CallExpression); break; // ... more node types } ts.forEachChild(node, (child) => visitNode(child, sourceFile)); } ``` -------------------------------- ### LCOM Metric Calculations Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Examples of measuring Lack of Cohesion of Methods using LCOM96a and LCOM96b standards. ```typescript // LCOM96a (Handerson et al.) metrics().lcom().lcom96a().shouldBeBelow(0.8); // LCOM96b (Handerson et al.) metrics().lcom().lcom96b().shouldBeBelow(0.7); ``` -------------------------------- ### Serve Documentation Locally Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Serve the generated documentation locally for previewing. ```bash npm run docs:serve ``` -------------------------------- ### Build the Project Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Build the project for production or distribution. ```bash npm run build ``` -------------------------------- ### ArchUnitTS Initialization: Selecting Project Files Source: https://github.com/lukasniessen/archunitts/blob/main/info/TECHNICAL.md Demonstrates how to initialize ArchUnitTS by selecting project files within a specific folder. This is the first step in defining architectural rules. ```typescript // User calls projectFiles() const files = projectFiles().inFolder('src'); ``` -------------------------------- ### Generate Documentation Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Generate project documentation using TypeDoc. ```bash npm run docs ``` -------------------------------- ### projectFiles() Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Initializes a fluent builder for testing architectural rules on project files, such as dependency constraints and naming conventions. ```APIDOC ## projectFiles() ### Description Entry point for defining architectural rules based on file patterns, folder locations, and dependency relationships. ### Methods - **withName(pattern)**: Filters files by name (string or regex). - **inFolder(pattern)**: Filters files by folder path. - **inPath(pattern)**: Filters files by full path. - **should()**: Starts the assertion chain for positive rules. - **shouldNot()**: Starts the assertion chain for negative rules. - **check()**: Executes the defined rules. ``` -------------------------------- ### Simplified Auto-Detection Logic Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md Illustrates the internal logic for auto-detecting and setting up testing frameworks. Each adapter function checks for framework availability before extending matchers. ```typescript // Auto-detect and setup all available testing frameworks // Each adapter function has built-in checks for framework availability extendJestMatchers(); // Only sets up if Jest is available extendVitestMatchers(); // Only sets up if Vitest is available ``` -------------------------------- ### Run Tests Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Execute the project's test suite to ensure code quality. ```bash npm test ``` -------------------------------- ### metrics() Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Initializes a fluent builder for analyzing code metrics, including cohesion, complexity, and structural counts. ```APIDOC ## metrics() ### Description Entry point for defining rules based on code metrics like LCOM, cyclomatic complexity, and class member counts. ### Methods - **lcom()**: Accesses Lack of Cohesion of Methods metrics (lcom96a, lcom96b). - **count()**: Accesses count metrics (methodCount, fieldCount, linesOfCode). - **distance()**: Accesses architectural distance metrics (abstractness, instability, distanceFromMainSequence). - **customMetric(name, description, callback)**: Defines a custom metric calculation. ``` -------------------------------- ### Define configuration options object Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Configuration object structure for logging and test behavior settings. ```javascript { logging: { enabled: true, // show logs level: 'debug', // show lots of logs logFile: true // write logs to file inside ./logs folder. You can specify a custom path too. }, // if your rule 'passes' because it 'passed' zero files, the test normally fails. You can turn this off by setting this true allowEmptyTests: true, clearCache: true // reading nodes, imports etc is normally cached, } ``` -------------------------------- ### Watch Documentation Changes Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Enable watch mode for automatic documentation regeneration during local development. ```bash npm run docs:watch ``` -------------------------------- ### Format Code Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Automatically format code to adhere to project style guidelines. ```bash npm run format ``` -------------------------------- ### ArchUnitTS: Ensuring All Project Files are Included Source: https://github.com/lukasniessen/archunitts/blob/main/info/TECHNICAL.md Explains and demonstrates ArchUnitTS's mechanism for including all project files in the dependency graph, even those without explicit imports, by adding self-referencing edges. This ensures comprehensive analysis. ```typescript // Even if utils.ts doesn't import anything from your project, // it will still appear in the graph with a self-edge: utils.ts -> utils.ts // This ensures files like these are always analyzed: // - Configuration files // - Standalone utilities // - Entry points // - Constants files // - Type definition files ``` -------------------------------- ### Clone the Repository Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Clone the ArchUnitTS repository to your local machine. ```bash git clone https://github.com/LukasNiessen/ArchUnitTS.git ``` -------------------------------- ### Define Pattern Matching Rules Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Demonstrates usage of string glob patterns and regular expressions for targeting files and classes. ```typescript // String patterns with glob support (case sensitive) .withName('*.service.ts') // All files ending with .service.ts .inFolder('**/services') // All files in any services folder .inPath('src/api/**/*.ts') // All TypeScript files under src/api // Regular expressions (case sensitive - use when you need exact case matching) .withName(/^.*Service\.ts$/) // Same as *.service.ts but case-sensitive .inFolder(/services$/) // Folders ending with 'services' (case-sensitive) // For metrics module: Class name matching with regex .forClassesMatching(/.*Service$/) // Classes ending with 'Service' .forClassesMatching(/^User.*/) // Classes starting with 'User' ``` -------------------------------- ### Generate dependency graph reports Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Export project dependency graphs in HTML or Mermaid format. ```typescript it('should generate dependency graph reports', async () => { await projectGraph() .collapseToFolderDepth(2) .exportAsHTML('reports/dependency-graph.html'); await projectGraph().exportAsMermaid('reports/dependency-graph.mmd'); expect(0).toBe(0); }); ``` -------------------------------- ### Execute rules with check Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Universal execution method for frameworks like Mocha. ```typescript // Mocha example const violations = await rule.check(); expect(violations).to.have.length(0); ``` ```typescript // With configuration options, the same ones as mentioned above const violations = await rule.check(options); ... ``` -------------------------------- ### check() Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Executes an architecture rule and returns a list of violations, suitable for testing frameworks without custom matcher support. ```APIDOC ## check(options?) ### Description Executes an architecture rule and returns an array of violations. This method is universal and intended for use with frameworks like Mocha or Node.js assert where custom matchers are not available. ### Parameters - **options** (CheckOptions) - Optional - Configuration object for logging, caching, and empty test handling. ### Returns - **violations** (Array) - An array containing rule violations found during execution. ### Usage Example ```typescript const violations = await rule.check(); expect(violations).to.have.length(0); ``` ``` -------------------------------- ### String Patterns with Glob Support Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Use string patterns with glob support to match filenames, folders, or paths. ```typescript .withName('*.service.ts') // All files ending with .service.ts .inFolder('**/services') // All files in any services folder .inPath('src/api/**/*.ts') // All TypeScript files under src/api ``` -------------------------------- ### Configure File Logging Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Enable file-based logging to save test results as artifacts, useful for CI/CD environments. ```typescript // Write logs to a specific file const options = { logging: { enabled: true, level: 'debug', logFile: true, }, }; await expect(rule).toPassAsync(options); ``` ```typescript // Automatically generates timestamped log files in ./logs/ const options = { logging: { enabled: true, level: 'info', logFile: true, // Creates logs/archunit-YYYY-MM-DD_HH-MM-SS.log }, }; await expect(rule).toPassAsync(options); ``` -------------------------------- ### AVA Architecture Test Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md This snippet demonstrates an architecture test using AVA. It imports 'test' from 'ava' and 'projectFiles' from 'archunit'. Violations need to be checked manually or with a helper function. ```typescript import test from 'ava'; import { projectFiles } from 'archunit'; test('architecture test', async (t) => { const rule = projectFiles() .inFolder('src/domain') .shouldNot() .dependOnFiles() .inFolder('src/presentation'); // AVA requires checking violations manually or using the checkArchRule helper const violations = await rule.check(); t.is(violations.length, 0, 'Architecture rule should pass'); }); ``` -------------------------------- ### Configure Vitest Globals Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Ensure globals are enabled in the Vitest configuration file. ```ts import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { globals: true, // This line matters !! ... }, }); ``` -------------------------------- ### Configure Vitest for ArchUnitTS Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Required configuration for Vitest to support ArchUnitTS globals. ```typescript import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { globals: true, // This line matters !! environment: 'node', coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], }, include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'], }, }); ``` -------------------------------- ### TypeDoc Configuration for Custom CSS Source: https://github.com/lukasniessen/archunitts/blob/main/docs-assets/README.md This JSON configuration snippet shows how to specify a custom CSS file for TypeDoc to use when generating documentation. Ensure the path to your CSS file is correct. ```json { "customCss": "./docs-assets/custom.css" } ``` -------------------------------- ### Generate Dependency Graph Reports Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Export dependency graphs in various formats like Mermaid or HTML, with options to focus on specific paths or collapse folder depth. ```typescript import { projectGraph } from 'archunit'; it('should export dependency graph reports', async () => { const graph = projectGraph().titled('Application Architecture'); await graph.collapseToFolderDepth(2).exportAsMermaid('reports/dependencies.mmd'); await graph.focusOn('src/**', 1).exportAsHTML('reports/source-dependencies.html'); expect(0).toBe(0); }); ``` -------------------------------- ### Check Controller Class Complexity with Mocha Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Uses Mocha and Chai to verify that controller classes within the '**/controllers' folder have a method count below 15, helping to manage complexity. Requires the 'archunit' package. ```typescript import { metrics } from 'archunit'; import { expect } from 'chai'; describe('Code Metrics', () => { it('controller classes should not be too complex', async () => { const violations = await metrics() .inFolder('**/controllers') .count() .methodCount() .shouldBeBelow(15) .check(); expect(violations).to.have.length(0); }); }); ``` -------------------------------- ### Advanced Pattern Matching Techniques Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Demonstrates combining multiple pattern matching methods and mixing file patterns with dependency or class name rules. ```typescript // Files module - Combine folder and filename patterns projectFiles() .inFolder('**/services/**') .withName('*.service.ts') .should() .haveNoCycles(); // Metrics module - Combine folder and filename patterns metrics().inFolder('**/services/**').withName('*.service.ts').lcom().lcom96b(); // Files module - Mix pattern matching with dependency rules projectFiles().inPath('src/api/**').shouldNot().dependOnFiles().inPath('src/database/**'); // Metrics module - Mix pattern matching with class name matching metrics() .inPath('src/api/**') .forClassesMatching(/.*Controller/) .count() .methodCount() .shouldBeBelow(15); ``` -------------------------------- ### Pattern Matching Methods Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Methods used to target files or classes based on specific patterns. ```APIDOC ## Pattern Matching Methods ### Description Methods to filter files or classes based on string patterns (glob) or regular expressions. ### Methods - **withName(pattern)**: Matches against the filename. - **inPath(pattern)**: Matches against the full relative path. - **inFolder(pattern)**: Matches against the path without the filename. - **forClassesMatching(pattern)**: Matches against class names (specific to metrics module). ### Parameters - **pattern** (string|RegExp) - Required - The pattern to match against. - **options** (object) - Optional - Configuration object containing an `except` property for exclusions. ``` -------------------------------- ### Generate HTML metric reports Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Export metric data as HTML files for use as CI artifacts. ```typescript it('should generate HTML reports', async () => { const countMetrics = metrics().count(); const lcomMetrics = metrics().lcom(); // Saves HTML report files to /reports await countMetrics.exportAsHTML(); await lcomMetrics.exportAsHTML(); // So we get no warning about an empty test expect(0).toBe(0); }); ``` -------------------------------- ### Execute rules with toPassAsync Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Standard usage of toPassAsync for Jest and Vitest environments. ```typescript // Jest/Vitest await expect(rule).toPassAsync(); // With configuration options await expect(rule).toPassAsync(options); ``` -------------------------------- ### ArchUnitTS Rule Execution with Jest Source: https://github.com/lukasniessen/archunitts/blob/main/info/TECHNICAL.md Illustrates how to execute a defined ArchUnitTS rule using the custom `toPassAsync` Jest matcher. This step validates the architectural constraints against the codebase. ```typescript // User executes rule await expect(rule).toPassAsync(); ``` -------------------------------- ### Configure GitLab CI artifacts Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Configure the CI pipeline to persist generated reports as artifacts. ```yml test: script: - npm test artifacts: when: always paths: - reports ``` -------------------------------- ### Regular Expression Patterns Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Use regular expressions for more complex pattern matching on filenames and folders. ```typescript .withName(/^.*Service\.ts$/) // Same as *.service.ts but as regex .inFolder(/services$/) // Folders ending with 'services' ``` -------------------------------- ### Combine Folder and Filename Patterns Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Combines folder and filename patterns to target specific service files and checks their LCOM metric (LCOM96b). ```typescript await metrics() .inFolder('**/services') .withName('*.service.ts') .lcom() .lcom96b() .shouldBeBelow(0.6) .check(); ``` -------------------------------- ### Integrate Metrics with Architecture Rules for Core Domain Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Applies LCOM metric (LCOM96b) to core domain classes and asserts high cohesion. ```typescript it('core domain has high cohesion', async () => { const violations = await metrics() .forClasses(/^Core\..*/) .lcom() .lcom96b() .shouldHaveCohesionAbove(0.6) .check(); expect(violations).toHaveLength(0); }); ``` -------------------------------- ### Complex Pattern Matching Scenarios Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Advanced use cases combining path, name, and dependency constraints for feature-based architectural enforcement. ```typescript // Ensure all TypeScript files in feature folders follow naming conventions projectFiles() .inPath('src/features/**/*.ts') .withName(/^[A-Z][a-zA-Z]*\.(service|controller|model)\.ts$/) .should() .haveNoCycles(); // Test that utility files have low complexity metrics() .inFolder('**/utils/**') .withName('*.util.ts') .complexity() .cyclomaticComplexity() .shouldBeBelow(5); // Ensure test files don't depend on implementation details projectFiles() .withName('*.spec.ts') .shouldNot() .dependOnFiles() .inPath('src/**/internal/**'); // Check cohesion of domain entities metrics() .inPath('src/domain/entities/**/*.ts') .withName(/^[A-Z][a-zA-Z]*Entity\.ts$/) .lcom() .lcom96a() .shouldBeBelow(0.6); ``` -------------------------------- ### toPassAsync() Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Executes an architecture rule within supported testing frameworks (Jest, Vitest, Jasmine) to provide enhanced error reporting and integration. ```APIDOC ## toPassAsync(options?) ### Description Executes an architecture rule and asserts success within Jest, Vitest, or Jasmine environments. This method is recommended for these frameworks as it provides better error messages and framework-specific integration. ### Parameters - **options** (CheckOptions) - Optional - Configuration object for logging, caching, and empty test handling. ### Usage Example ```typescript await expect(rule).toPassAsync(); // With options await expect(rule).toPassAsync({ logging: { enabled: true, level: 'debug' }, allowEmptyTests: true, clearCache: true }); ``` ``` -------------------------------- ### Test Classes by Full Path Pattern and LCOM Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Checks if TypeScript files under 'src/domain' meet a specific LCOM metric (LCOM96a). ```typescript await metrics().inPath('src/domain/**/*.ts').lcom().lcom96a().shouldBeBelow(0.8).check(); ``` -------------------------------- ### Mix Path Pattern, Class Name Matching, and Method Count Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Targets API files, filters for classes matching a regex, and checks their method count. ```typescript await metrics() .inPath('src/api/**') .forClassesMatching(/.*Controller/) .count() .methodCount() .shouldBeBelow(15) .check(); ``` -------------------------------- ### Exclude Matches in Rules Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Shows how to use the except option to exclude specific files or folders from a rule. ```typescript it('should consume orders only through its public API', async () => { const rule = projectFiles() .inPath('src/app/**/*.ts', { except: { inPath: 'src/app/orders/**' }, }) .shouldNot() .dependOnFiles() .inFolder('src/app/orders/**', { except: ['index.ts', 'public-api.ts'], }); await expect(rule).toPassAsync(); }); ``` ```typescript projectFiles() .inPath('src/app/**/*.ts', { except: { inPath: 'src/app/generated/**', inFolder: 'src/app/testing/**', withName: '*.spec.ts', }, }) .should() .beInPath('src/app/**'); metrics() .forClassesMatching('*Service', { except: { forClassesMatching: '*Legacy*' }, }) .lcom() .lcom96b() .shouldBeBelow(0.8); ``` -------------------------------- ### Validate Nx project naming and categorization Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Enforces naming conventions and dependency rules based on project types. ```typescript it('should follow Nx project naming patterns', async () => { // Feature projects should follow naming convention const rule = nxProjectSlices() .matching(/^feature-/) .should() .containSlices() .matching(/^feature-[a-z-]+$/); await expect(rule).toPassAsync(); }); it('should enforce shared library dependencies', async () => { // Shared libs should not depend on feature libs const rule = nxProjectSlices() .matching('shared-*') .shouldNot() .dependOnSlices() .matching('feature-*'); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Validate Nx project architecture Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Initializes slices based on Nx projects and checks for circular dependencies. ```typescript import { nxProjectSlices } from 'archunit'; import * as path from 'path'; it('should adhere to Nx project architecture', async () => { const rule = nxProjectSlices().should().haveNoCycles(); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Define Custom Rules Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Implements custom logic to validate file content or structure. ```typescript const ruleDesc = 'TypeScript files should export functionality'; const myCustomRule = (file: FileInfo) => { // TypeScript files should contain export statements return file.content.includes('export'); }; const violations = await projectFiles() .withName('*.ts') // all ts files .should() .adhereTo(myCustomRule, ruleDesc) .check(); expect(violations).toStrictEqual([]); ``` -------------------------------- ### Lint Code Source: https://github.com/lukasniessen/archunitts/blob/main/CONTRIBUTING.md Check code style and identify potential issues using the linter. ```bash npm run lint ``` -------------------------------- ### Custom Metric Definition and Check Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Defines and checks a custom metric named 'complexityRatio' based on the ratio of methods to fields. ```typescript await metrics() .customMetric( 'complexityRatio', 'Ratio of methods to fields', (classInfo) => classInfo.methods.length / Math.max(classInfo.fields.length, 1) ) .shouldBeBelow(3.0) .check(); ``` -------------------------------- ### Validate Nx architecture with UML diagrams Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Checks project structure against external PlantUML files or inline diagram strings. ```typescript it('should adhere to Nx architecture diagram', async () => { const diagramLocation = path.resolve('docs', 'components.puml'); const rule = nxProjectSlices() .ignoringExternalDependencies() .should() .adhereToDiagramInFile(diagramLocation); await expect(rule).toPassAsync(); }); it('should follow inline diagram', async () => { const diagram = ` @startuml component [shared-ui] as UI component [feature-auth] as Auth component [feature-dashboard] as Dashboard component [shared-data-access] as Data Auth --> UI Dashboard --> UI Auth --> Data Dashboard --> Data @enduml`; const rule = nxProjectSlices().should().adhereToDiagram(diagram); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Enable Debug Logging in ArchUnitTS Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Configure logging options within the test expectation to troubleshoot architecture rule failures. ```typescript it('should respect layered architecture', async () => { const rule = projectFiles() .inFolder('src/presentation/**') .shouldNot() .dependOnFiles() .inFolder('src/database'); const options = { logging: { enabled: true, level: 'debug', // 'error' | 'warn' | 'info' | 'debug' }, }; await expect(rule).toPassAsync(options); }); ``` -------------------------------- ### Define CheckOptions interface Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Interface definition for rule execution configuration options. ```typescript interface CheckOptions { // default undefined, which is treated as no logging logging?: { enabled: boolean; level: 'debug' | 'info' | 'warn' | 'error'; }; allowEmptyTests?: boolean; // Default: false clearCache?: boolean; // Default: false } ``` -------------------------------- ### Test Service Classes by Filename Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Checks if service classes adhere to a specific LCOM metric (LCOM96b) below a threshold. ```typescript import { metrics } from 'archunit'; // Test only service classes await metrics().withName('*.service.ts').lcom().lcom96b().shouldBeBelow(0.7).check(); ``` -------------------------------- ### Enforce Naming Conventions Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Validates that file names match specific patterns or casing requirements. ```typescript it('should follow naming patterns', async () => { const rule = projectFiles() .inFolder('src/services/**') .should() .haveName('*-service.ts'); // my-service.ts for example await expect(rule).toPassAsync(); }); it('components should be PascalCase', async () => { const rule = projectFiles() .inFolder('src/components/**') .should() .haveName(/^[A-Z][a-zA-Z]*Commponent\.ts$/); // MyComponent.ts for example await expect(rule).toPassAsync(); }); ``` -------------------------------- ### ArchUnitTS Rule Definition: Checking for Cycles Source: https://github.com/lukasniessen/archunitts/blob/main/info/TECHNICAL.md Shows how to define a simple architectural rule using ArchUnitTS's fluent API, specifically checking if a set of files should have no cycles. This is part of the Analysis Phase. ```typescript // User defines rule const rule = files.should().haveNoCycles(); ``` -------------------------------- ### Define Custom Metrics Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Creates user-defined metrics to evaluate class or file properties. ```typescript it('should have a nice method field ratio', async () => { const rule = metrics() .customMetric( 'methodFieldRatio', 'Ratio of methods to fields', (classInfo) => classInfo.methods.length / Math.max(classInfo.fields.length, 1) ) .shouldBeBelowOrEqual(10); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Jest/Jasmine Architecture Test Source: https://github.com/lukasniessen/archunitts/blob/main/src/testing/README.md Use this snippet with Jest or Jasmine to define and assert architecture rules. It requires importing 'describe', 'it', and 'expect' from the testing framework and 'projectFiles' from 'archunit'. ```typescript import { describe, it, expect } from 'jest'; // or jasmine setup import { projectFiles } from 'archunit'; describe('Architecture Tests', () => { it('tests layer dependencies', async () => { const rule = projectFiles() .inFolder('src/domain') .shouldNot() .dependOnFiles() .inFolder('src/presentation'); await expect(rule).toPassAsync(); }); }); ``` -------------------------------- ### Maintain Reasonable File Sizes Source: https://github.com/lukasniessen/archunitts/blob/main/examples/layered-architecture/fastify-uml/README.md Sets an upper limit on the lines of code for any file within the 'src' directory to promote maintainability and readability. This rule helps prevent excessively large files. ```typescript it('should maintain reasonable file sizes', async () => { const rule = metrics().inFolder('src/**').count().linesOfCode().shouldBeBelow(1200); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Configure Jasmine Async Matchers Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Register the async matcher in the Jasmine test environment. ```typescript beforeEach(() => { jasmine.addAsyncMatchers(jasmineMatcher); }); ``` -------------------------------- ### Enforce Layered Architecture with Package Diagrams Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Validates package-level dependencies using a UML package diagram. ```typescript it('should follow layered architecture diagram', async () => { const diagram = ` @startuml package "Presentation Layer" { [Controllers] [ViewModels] } package "Business Layer" { [Services] [Domain Models] } package "Data Layer" { [Repositories] [Entities] } [Controllers] --> [Services] [Services] --> [Repositories] [ViewModels] --> [Domain Models] @enduml`; const rule = projectSlices().definedBy('src/**/(**)').should().adhereToDiagram(diagram); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Architectural Distance Metrics Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Measuring abstractness, instability, and distance from the main sequence. ```typescript // Abstractness metrics().distance().abstractness().shouldBeAbove(0.3); // Instability metrics().distance().instability().shouldBeBelow(0.8); // Distance from main sequence metrics().distance().distanceFromMainSequence().shouldBeBelow(0.5); ``` -------------------------------- ### Enforce Architecture Slices Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Use projectSlices to define architectural boundaries and verify adherence to UML diagrams or dependency constraints. ```typescript it('should adhere to UML diagram', async () => { const diagram = ` @startuml component [controllers] component [services] [controllers] --> [services] @enduml`; const rule = projectSlices().definedBy('src/(**)/').should().adhereToDiagram(diagram); await expect(rule).toPassAsync(); }); it('should not contain forbidden dependencies', async () => { const rule = projectSlices() .definedBy('src/(**)/') .shouldNot() .containDependency('services', 'controllers'); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Export Metrics Reports Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Generate HTML reports for code metrics such as counts, LCOM cohesion, and distance, or export a comprehensive dashboard. ```typescript // Export count metrics report await metrics().count().exportAsHTML('reports/count-metrics.html', { title: 'Count Metrics Dashboard', includeTimestamp: true, }); // Export LCOM cohesion metrics report await metrics().lcom().exportAsHTML('reports/lcom-metrics.html', { title: 'Code Cohesion Analysis', includeTimestamp: false, }); // Export distance metrics report await metrics().distance().exportAsHTML('reports/distance-metrics.html'); ``` ```typescript // Export comprehensive report with all metrics import { MetricsExporter } from 'archunitts'; await MetricsExporter.exportComprehensiveAsHTML(undefined, { outputPath: 'reports/comprehensive-metrics.html', title: 'Complete Architecture Metrics Dashboard', customCss: '.metric-card { border-radius: 8px; }', }); ``` -------------------------------- ### Code Quality: Limit Methods Per Component Source: https://github.com/lukasniessen/archunitts/blob/main/examples/angular-example/README.md Enforces a maximum of 15 methods per component class within the 'src/app/components' folder. This helps in keeping components focused and manageable. ```typescript it('should limit methods per component class', async () => { const rule = metrics() .inFolder('src/app/components/**') .withName('*.component.ts') .count() .methodCount() .shouldBeBelow(15); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Code Quality: Limit File Size Source: https://github.com/lukasniessen/archunitts/blob/main/examples/angular-example/README.md Checks for overly large files within the 'src/app' directory, enforcing a maximum limit of 1500 lines of code per file. This promotes modularity and maintainability. ```typescript it('should not contain overly large files', async () => { const rule = metrics() .inFolder('src/app/**') .count() .linesOfCode() .shouldBeBelow(1500); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Adhere to UML Layer Diagram Source: https://github.com/lukasniessen/archunitts/blob/main/examples/layered-architecture/fastify-uml/README.md Validates the project's file structure against a defined UML layer diagram. This ensures that the actual code organization matches the intended architectural blueprint. ```typescript it('should adhere to UML layer diagram', async () => { const diagram = ` @startuml component [presentation] component [business] component [persistence] [presentation] --> [business] [business] --> [persistence] @enduml`; const rule = projectSlices().definedBy('src/(**)').should().adhereToDiagram(diagram); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Enforcing Naming Conventions Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Uses regex patterns to enforce naming conventions for files and classes, finalized with the .check() method. ```typescript // Match camelCase test files projectFiles() .withName(/^[a-z][a-zA-Z]*\.spec\.ts$/) .should() .beInFolder('**/test/**') .check(); // Match interface files (starting with I) projectFiles() .withName(/^I[A-Z][a-zA-Z]*\.ts$/) .should() .beInFolder('**/interfaces/**') .check(); // Match constant files (all uppercase) projectFiles() .withName(/^[A-Z_]+\.ts$/) .should() .beInFolder('**/constants/**') .check(); // Metrics for PascalCase controllers metrics() .withName(/^[A-Z][a-zA-Z]*Controller\.ts$/) .lcom() .lcom96b() .shouldBeBelow(0.5) .check(); ``` -------------------------------- ### Execute ArchUnitTS Test in Jasmine Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Use expectAsync with the toPassAsync matcher when writing tests in Jasmine. ```typescript describe('architecture', () => { beforeEach(() => { jasmine.addAsyncMatchers(jasmineMatcher); }); it('business logic should not depend on the ui', async () => { const rule = projectFiles() .inFolder('business') .shouldNot() .dependOnFiles() .inFolder('ui'); await expectAsync(rule).toPassAsync(); // expectAsync, not expect !! }); ``` -------------------------------- ### Limit Method Count in Core Business Logic Source: https://github.com/lukasniessen/archunitts/blob/main/examples/hexagonal-architecture/express/README.md Applies a metric to ensure the core business logic remains focused by limiting the number of methods within its files. This helps prevent the core from becoming overly complex. ```typescript it('should maintain focused business logic', async () => { const rule = metrics() .inFolder('src/core/**') .count() .methodCount() .shouldBeBelow(12); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Enforce Adapter Implementation of Ports Source: https://github.com/lukasniessen/archunitts/blob/main/examples/hexagonal-architecture/express/README.md Checks that files within the adapters layer correctly depend on and implement the interfaces defined in the ports layer. This ensures adapters adhere to the contracts specified by the ports. ```typescript it('adapters should implement ports', async () => { const rule = projectFiles() .inFolder('src/adapters/**') .should() .dependOnFiles() .inFolder('src/ports/**'); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Count Metrics Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Measuring code volume and complexity by counting methods, fields, and lines of code. ```typescript // Method count metrics().count().methodCount().shouldBeBelow(20); // Field count metrics().count().fieldCount().shouldBeBelow(15).; // Lines of code metrics().count().linesOfCode().shouldBeBelow(200); ``` -------------------------------- ### Validate Microservices Boundaries Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Enforces service communication boundaries using a component diagram representing microservices. ```typescript it('should respect microservices boundaries', async () => { const diagram = ` @startuml component [UserService] as US component [OrderService] as OS component [PaymentService] as PS component [NotificationService] as NS US --> OS : getUserOrders() OS --> PS : processPayment() OS --> NS : sendNotification() note right of US : No direct dependencies between services except through defined APIs @enduml`; const rule = projectSlices() .definedBy('services/(**)/') // Group by service folders .should() .adhereToDiagram(diagram); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Define circular dependency rules Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Ensure no circular dependencies exist within the specified project folder. ```typescript import { projectFiles, metrics } from 'archunit'; it('should not have circular dependencies', async () => { const rule = projectFiles().inFolder('src/**').should().haveNoCycles(); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Enforce Nx project boundaries Source: https://github.com/lukasniessen/archunitts/blob/main/README.md Restricts dependencies between specific Nx applications or library types using matching patterns. ```typescript it('should respect Nx project boundaries', async () => { // Apps should not depend on other apps const rule = nxProjectSlices() .matching('apps/admin') .shouldNot() .dependOnSlices() .matching('apps/client'); await expect(rule).toPassAsync(); }); it('should enforce library type boundaries', async () => { // Feature libs should not depend on other feature libs const rule = nxProjectSlices() .matching('feature-*') .shouldNot() .dependOnSlices() .matching('feature-*'); await expect(rule).toPassAsync(); }); ``` -------------------------------- ### Class Name Matching for LCOM Source: https://github.com/lukasniessen/archunitts/blob/main/src/metrics/README.md Applies LCOM metric (LCOM96a) to classes matching a specific service name pattern. ```typescript await metrics() .forClassesMatching(/.*Service/) .lcom() .lcom96a() .shouldBeBelow(0.7) .check(); ``` -------------------------------- ### Enforce Shell to Micro-Frontend Dependency Rules Source: https://github.com/lukasniessen/archunitts/blob/main/examples/micro-frontends/react/README.md Ensures that the shell application does not directly depend on internal components of any micro-frontend. Use this to maintain clear separation between the shell and its constituent micro-frontends. ```typescript import { projectFiles, projectSlices, metrics } from 'archunit'; describe('React Micro Frontend Architecture Rules', () => { it('shell should not depend on micro-frontend internals', async () => { const rule = projectFiles() .inFolder('apps/shell/**') .shouldNot() .dependOnFiles() .inFolder('apps/micro-frontend-*/**/components/**'); await expect(rule).toPassAsync(); }); it('micro-frontends should not depend on each other', async () => { const rule = projectFiles() .inFolder('apps/micro-frontend-auth/**') .shouldNot() .dependOnFiles() .inFolder('apps/micro-frontend-dashboard/**'); await expect(rule).toPassAsync(); }); it('shared libraries should not be too large', async () => { const rule = metrics() .inFolder('libs/shared/**') .count() .linesOfCode() .shouldBeBelow(2000); await expect(rule).toPassAsync(); }); it('micro-frontend components should have limited complexity', async () => { const rule = metrics() .inFolder('apps/micro-frontend-*/**/components/**') .count() .methodCount() .shouldBeBelow(12); await expect(rule).toPassAsync(); }); it('should maintain good cohesion in shared utilities', async () => { const rule = metrics().inFolder('libs/shared/**').lcom().lcom96b().shouldBeBelow(0.7); await expect(rule).toPassAsync(); }); }); ```