ArchUnitTS Documentation

repository·main·Indexed 19 days ago

https://github.com/lukasniessen/archunitts

An architecture testing library for TypeScript and JavaScript projects (version 2.4.0) used to specify and assert architecture rules. It enables developers to enforce dependency directions, detect circular dependencies, and validate project structures against PlantUML diagrams using projectSlices(). The library also includes a metrics() API to enforce code quality standards such as lines of code, method counts, and Lack of Cohesion in Methods (LCOM).

Tokens
27.9K
Snippets
93
Records
121
Agent score
62%

What's inside ArchUnitTS

  1. Explore ArchUnitTS Core Modules

    main

    ArchUnitTS is composed of several specialized modules for different architectural testing needs:

    • Files: Used for defining rules based on file and folder structures (Stable).
    • Metrics: Used for calculating code quality metrics (Stable).
    • Slices: Used for architecture slicing (Stable).
    • Graph: Used for dependency graph reports and queries (Experimental).
    • Testing: Provides universal integration with various testing frameworks (Stable).
    • Common: Contains shared utilities (Stable).
    • Reports: Used to generate reports (Experimental).
  2. How ArchUnitTS processes architectural rules

    main

    The execution of an architectural test follows three distinct phases:

    1. Initialization Phase: Triggered by calls like projectFiles(). The engine performs workspace discovery, loads tsconfig.json settings, and applies folder/pattern filters.
    2. Analysis Phase: Triggered by rule definitions like files.should().haveNoCycles(). The engine parses files to build the dependency graph, compiles the fluent API calls into executable rules, and optimizes the analysis order.
    3. Validation Phase: Triggered by executing the rule (e.g., via a test matcher). The engine runs validation algorithms on the graph, collects violations, and formats human-readable error messages.
  3. Validate different UML diagram types

    main

    ArchUnitTS can interpret various PlantUML diagram types to enforce different architectural patterns:

    • Component Diagrams: Validate relationships between high-level components.
    • Package Diagrams: Enforce dependencies between layers (e.g., Presentation, Business, Data).
    • Class Diagrams: Validate class relationships and inheritance hierarchies.
    • Microservices: Validate boundaries and communication paths between services.
    • Custom Patterns: Supports specialized architectures like Hexagonal (Ports and Adapters).
    import { projectSlices } from 'archunit';
    
    // Example: Layered Package Diagram validation
    const diagram = `@startuml\npackage "Presentation" { [Controllers] }\npackage "Business" { [Services] }\n[Controllers] --> [Services]\n@enduml`;
    
    const rule = projectSlices()
      .definedBy('src/**/(**)')
      .should()
      .adhereToDiagram(diagram);
    
    await expect(rule).toPassAsync();
  4. How dependency graph inclusion works

    main

    To ensure comprehensive coverage, ArchUnitTS includes all project files in the dependency graph, even if they do not contain any import statements (e.g., standalone utility files, constants, or entry points).

    This is achieved by adding self-referencing edges for every file in the project. The resulting graph contains:

    • Import edges: Real dependencies between files (e.g., A imports B).
    • Self edges: Every project file references itself (e.g., utils.ts -> utils.ts), ensuring it is visible for architectural analysis.
  5. Exclude files from pattern matches

    main

    You can exclude specific files or folders from a pattern match using the except option. This is useful for allowing small public surfaces within otherwise forbidden directories.

    except can be:

    1. An array of patterns (e.g., ['index.ts', 'public-api.ts']) to exclude specific filenames.
    2. An object containing pattern methods (e.g., { inPath: '...' }) to exclude based on path, folder, or name.
    // Exclude specific files by name
    const rule = projectFiles()
      .inPath('src/app/**/*.ts', {
        except: { inPath: 'src/app/orders/**' },
      })
      .shouldNot()
      .dependOnFiles()
      .inFolder('src/app/orders/**', {
        except: ['index.ts', 'public-api.ts'],
      });
    
    // Explicit exclusions by target type
    projectFiles()
      .inPath('src/app/**/*.ts', {
        except: {
          inPath: 'src/app/generated/**',
          inFolder: 'src/app/testing/**',
          withName: '*.spec.ts',
        },
      });
  6. How the Slices API filtering works

    main
    The Slices API uses a different filtering mechanism than the files and metrics APIs. While files and metrics APIs support methods like inFolder(), the Slices API requires using its own specific filtering methods (such as matching()) to select subsets of slices for rule validation. Refer to the Nx or UML diagram examples for the correct syntax.
  7. Supported Testing Frameworks

    main

    ArchUnitTS is compatible with most TypeScript/JavaScript testing frameworks. It provides special syntax support for the following:

    • Jest
    • Jasmine
    • Vitest

    Specifically, these frameworks support the toPassAsync matcher. However, ArchUnitTS can be used with any existing framework, including Mocha.

  8. Target files using pattern matching in metrics

    main

    When using the metrics() module, you can target specific files or directories using three primary pattern matching methods. These methods support both string patterns (with glob support) and regular expressions.

    • withName(pattern): Matches only the filename (e.g., 'Service.ts' from 'src/services/Service.ts').
    • inPath(pattern): Matches against the full relative path (e.g., 'src/services/Service.ts').
    • inFolder(pattern): Matches against the path without the filename (e.g., 'src/services' from 'src/services/Service.ts').

    You can also use .forClassesMatching(regex) to filter by class names.

    import { metrics } from 'archunit';
    
    // Using glob strings
    await metrics().withName('*.service.ts').check();
    await metrics().inFolder('**/services').check();
    await metrics().inPath('src/api/**/*.ts').check();
    
    // Using regular expressions
    await metrics().withName(/^.*Service\.ts$/).check();
    await metrics().inFolder(/services$/).check();
    
    // Using class name matching
    await metrics().forClassesMatching(/.*Controller/).check();
  9. File-based vs Class-based Rules

    main

    When designing your architecture tests, choose between these two approaches based on your requirements:

    • File-based rules: Analyze import relationships between files. Use these when you want to enforce boundaries between folders or modules.
    • Class-based rules: Examine dependencies between classes and their individual members. Use these for fine-grained object-oriented architecture validation.
  10. Target files using the Pattern Matching System

    main

    ArchUnitTS provides three primary methods for targeting files across all modules using either string patterns (with glob support) or regular expressions.

    • withName(pattern): Checks the pattern against the filename (e.g., Service.ts).
    • inPath(pattern): Checks the pattern against the full relative path (e.g., src/services/Service.ts).
    • inFolder(pattern): Checks the pattern against the path without the filename (e.g., src/services).

    For the metrics module, you can also use:

    • forClassesMatching(pattern): Checks the pattern against class names, regardless of file path.
    // String patterns with glob support
    .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
    .withName(/^.*Service\.ts$/)  // Case-sensitive regex
    .inFolder(/services$/)        // Folders ending with 'services'
    
    // Metrics module: Class name matching
    .forClassesMatching(/.*Service$/)
  11. Understanding Cycle-Free Check Behavior

    main

    When performing cycle-free assertions (e.g., .should().haveNoCycles()), ArchUnitTS uses a permissive approach regarding empty sets to avoid confusing error messages.

    If you target a specific folder (e.g., .inFolder("A")) that contains files but those files don't meet other criteria, the cycle check will evaluate the unfiltered file set for emptiness rather than the filtered set. This prevents 'empty test' errors when a folder actually contains files, even if the specific subset being analyzed for cycles is empty.

  12. Compare ArchUnitTS with other libraries and linters

    main

    ArchUnitTS is a comprehensive architecture testing library that goes beyond simple dependency linting.

    Key differentiators from linters (like eslint-plugin-import):

    • Code Metrics: Provides LCOM (cohesion), cyclomatic complexity, coupling, abstractness, instability, and distance from main sequence.
    • Advanced Architecture: Supports UML diagram validation, architecture slices, and multi-layer validation.
    • Nx Support: Built-in validation for Nx monorepo project graphs and boundaries.
    • Empty Test Protection: Automatically fails tests if file patterns match zero files (preventing false positives due to typos).
    • Deep Analysis: Performs class-level analysis (methods, fields) rather than just module-level imports.

    Key differentiators from other TS architecture libraries:

    • Empty Test Protection: Fails by default if no files are found.
    • Universal Framework Support: Works with Jest, Vitest, Jasmine, Mocha, etc., with specialized async matchers.
    • Code Metrics: The only library offering comprehensive code metrics.
    • Rich Reporting: Generates HTML dashboards and multiple dependency graph formats (DOT, Mermaid, D2, CSV, JSON).