archunit
TypeScript icon, indicating that this package has built-in type declarations

2.1.22 • Public • Published

ArchUnitTS - Architecture Testing

ArchUnitTS Logo

License: MIT npm version npm downloads GitHub stars

Enforce architecture rules in TypeScript and JavaScript projects. Check for dependency directions, detect circular dependencies, enforce coding standards and much more. Integrates with every testing framework. Very simple setup and pipeline integration.

Inspired by the amazing ArchUnit library but we are not affiliated with ArchUnit.

DocumentationUse CasesFeaturesContributing

⚡ 5 min Quickstart

Installation

npm install archunit --save-dev

Add tests

Simply add tests to your existing test suites. The following is an example using Jest. First we ensure that we have no circular dependencies.

import { projectFiles, metrics } from 'archunit';

it('should not have circular dependencies', async () => {
  const rule = projectFiles().inFolder('src').should().haveNoCycles();

  // toPassAsync is syntax support we added for Jest, Vitest
  // and Jasmine, but ArchUnitTS works with any testing framework
  await expect(rule).toPassAsync();
});

Next we ensure that our layered architecture is respected.

it('presentation layer should not depend on database layer', async () => {
  const rule = projectFiles()
    .inFolder('src/presentation')
    .shouldNot()
    .dependOnFiles()
    .inFolder('src/database');

  await expect(rule).toPassAsync();
});

it('business layer should not depend on database layer', async () => {
  const rule = projectFiles()
    .inFolder('src/business')
    .shouldNot()
    .dependOnFiles()
    .inFolder('src/database');

  await expect(rule).toPassAsync();
});

// More layers ...

Lastly we ensure that some code metric rules are met.

it('should not contain too large files', () => {
  const rule = metrics().count().linesOfCode().shouldBeBelow(1000);
  await expect(rule).toPassAsync();
});

it('should only have classes with high cohesion', async () => {
  // LCOM metric (lack of cohesion of methods)
  // Low LCOM means high cohesion
  const rule = metrics().lcom().lcom96b().shouldBeBelow(0.3);
  await expect(rule).toPassAsync();
});

CI Integration

These tests will run automatically in your testing setup, for example in your CI pipeline, so that's basically it. This setup ensures that the architectural rules you have defined are always adhered to! 🌻🐣

Additionally, you can generate reports and save them as artifacts. Here's a simple example using GitLab CI. Note that reports are in beta.

it('should generate HTML reports', () => {
  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);
});

In your gitlab-ci.yml:

test:
  script:
    - npm test
  artifacts:
    when: always
    paths:
      - reports

🎬 Demo

https://github.com/user-attachments/assets/426f7b47-5157-4e92-98a3-f5ab4f7a388a

🐹 Use Cases

Many common uses cases are covered in our examples folder. Here is an overview.

Layered Architecture:

  • Express BackEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/layered-architecture/express/README.md)

  • Fastify BackEnd using a UML Diagram: click here (TODO-add-Link: subfolder of examples. Eg examples/layered-architecture/fastify-uml/README.md)

  • Angular FrontEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/layered-architecture/angular/README.md)

Domain Partitioning:

  • Express MicroServices using Nx: click here (TODO-add-Link: subfolder of examples. Eg examples/micro-services/express/README.md)

  • TODO: is this possible with ArchUnitTS, todo domain partitioning checks in Nx?

  • Modular monlith, Deno BackEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/domain-partitioning/deno/README.md)

  • React MicroFrontEnds using Nx: click here (TODO-add-Link: subfolder of examples. Eg examples/micro-frontends/react/README.md)

  • TODO: is this possible with ArchUnitTS, todo domain partitioning checks in Nx?

Clean Architecture:

  • NestJS BackEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/clean-architecture/nestjs/README.md)

  • React FrontEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/clean-architecture/react/README.md)

Hexagonal Architecture:

  • Express BackEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/hexagonal-architecture/express/README.md)

MVC:

  • Spring BackEnd: click here (TODO-add-Link: subfolder of examples. Eg examples/mvc/spring/README.md)

🐲 Example Repositories

Here are a few repositories with fully functioning examples that use ArchUnitTS to ensure architectural rules:

🐣 Features

This is an overview of you can do with ArchUnitTS.

Circular Dependencies

it('services should be free of cycles', async () => {
  const rule = projectFiles().inFolder('src/services').should().haveNoCycles();
  await expect(rule).toPassAsync();
});

Layer Dependencies

it('should respect clean architecture layers', async () => {
  const rule = projectFiles()
    .inFolder('src/presentation')
    .shouldNot()
    .dependOnFiles()
    .inFolder('src/database');
  await expect(rule).toPassAsync();
});

it('business layer should not depend on presentation', async () => {
  const rule = projectFiles()
    .inFolder('src/business')
    .shouldNot()
    .dependOnFiles()
    .inFolder('src/presentation');
  await expect(rule).toPassAsync();
});

Naming Conventions

it('should follow naming patterns', async () => {
  const rule = projectFiles().inFolder('services').should().matchFilename('*Service.ts'); // Modern glob pattern approach
  await expect(rule).toPassAsync();
});

Code Metrics

it('should not contain too large files', async () => {
  const rule = metrics().count().linesOfCode().shouldBeBelow(1000);
  await expect(rule).toPassAsync();
});

it('should have high class cohesion', async () => {
  const rule = metrics().lcom().lcom96b().shouldBeBelow(0.3);
  await expect(rule).toPassAsync();
});

it('should count methods per class', async () => {
  const rule = metrics().count().methodCount().shouldBeBelow(20);
  await expect(rule).toPassAsync();
});

it('should limit statements per file', async () => {
  const rule = metrics().count().statements().shouldBeBelowOrEqual(100);
  await expect(rule).toPassAsync();
});

it('should have 3 fields per Data class', async () => {
  const rule = metrics()
    .forClassesMatching(/.*Data.*/)
    .count()
    .fieldCount()
    .shouldBe(3);

  await expect(rule).toPassAsync();
});

Distance Metrics

it('should maintain proper coupling', async () => {
  const rule = metrics().distance().couplingFactor().shouldBeBelow(0.5);
  await expect(rule).toPassAsync();
});

it('should stay close to main sequence', async () => {
  const rule = metrics().distance().distanceFromMainSequence().shouldBeBelow(0.3);
  await expect(rule).toPassAsync();
});

Custom Rules

You can define your own custom rules.

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()
  .matchingPattern('**/*.ts')
  .should()
  .adhereTo(myCustomRule, ruleDesc)
  .check();

expect(violations).toStrictEqual([]);

Custom Metrics

You can define your own metrics as well.

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();
});

Architecture Slices

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();
});

Filtering and Targeting

it('should filter by folder pattern', async () => {
  const rule = metrics()
    .inFolder(/src\/services/)
    .count()
    .methodCount()
    .shouldBeBelow(15);
  await expect(rule).toPassAsync();
});

it('should filter by class pattern', async () => {
  const rule = metrics()
    .forClassesMatching(/.*Service$/)
    .lcom()
    .lcom96b()
    .shouldBeBelow(0.5);
  await expect(rule).toPassAsync();
});

it('should target specific files', async () => {
  const rule = metrics()
    .forFile('user-service.ts')
    .count()
    .linesOfCode()
    .shouldBeBelow(200);
  await expect(rule).toPassAsync();
});

Export & Reporting

Generate beautiful HTML reports for your metrics. Note that this features is in beta.

// 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');
// 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; }',
});

The export functionality can be customized, for example by specifying an output path and custom CSS. Thanks to this, it's also very easy to include generated reports into your deploy process of, let's say, your GitHub page or GitLab page.

🎯 File and Folder Filtering

TODO: needs to be way better. Where can you use glob pattern? Which are just name related, which entire path related? etc. which allow just string? which regex? which glob pattern? explain glob pattern!

ArchUnitTS provides powerful and flexible file filtering capabilities that allow you to precisely select files for architectural testing. The API offers multiple methods to match files based on different criteria, making it easy to enforce architectural rules.

Core Filtering Methods

1. Folder-Based Filtering

The inFolder() method is the most common way to select files within specific directories:

it('should test files in specific folders', async () => {
  // Test all files in the 'services' folder
  const rule = projectFiles().inFolder('services').should().haveNoCycles();

  await expect(rule).toPassAsync();
});

How inFolder() works:

  • Input: 'components'

    • ✅ Matches: 'src/components/component-a.ts'
    • ✅ Matches: 'src/components/component-b.ts'
    • ✅ Matches: 'src/domain/helper/components/helper-component.ts' ← notice /components/ is in the path
    • ❌ NOT matching: 'src/views/view-a.ts'
  • Input: 'src/components' (more specific path)

    • ✅ Matches: 'src/components/component-a.ts'
    • ✅ Matches: 'src/components/component-b.ts'
    • ❌ NOT matching: 'src/domain/helper/components/helper-component.ts'
    • ❌ NOT matching: 'src/views/view-a.ts'

2. Pattern-Based Filtering

Use matchingPattern() for glob-style pattern matching:

it('should match files with glob patterns', async () => {
  // Match all TypeScript files recursively
  const rule = projectFiles().matchingPattern('**/*.ts').should().haveNoCycles();

  await expect(rule).toPassAsync();
});

3. Name-Based Filtering

Use withName() for exact filename matching:

it('should match specific file names', async () => {
  const rule = projectFiles().withName('UserService.ts').should().beInFolder('services');

  await expect(rule).toPassAsync();
});

How withName() works:

  • Input: 'my-component.ts' (with extension)

    • ✅ Matches: 'src/cool-components/my-component.ts'
    • ✅ Matches: 'src/other-components/my-component.ts'
    • ❌ NOT matching: 'src/cool-components/component'
    • ❌ NOT matching: 'src/views/view-a.ts'
  • Input: 'my-component' (without extension)

    • ❌ NOT matching: 'src/cool-components/my-component.ts'
    • ❌ NOT matching: 'src/other-components/my-component.ts'
    • ✅ Matches: 'src/cool-components/my-component' (if such file exists)

Enhanced Pattern Matching API

ArchUnitTS provides four enhanced pattern matching methods for more precise file selection:

1. matchFilename() - Exact Filename Matching (Recommended)

Matches patterns against the filename only (not the full path). This is the recommended approach for most use cases.

it('should enforce service naming convention', async () => {
  const violations = await projectFiles()
    .inFolder('services')
    .should()
    .matchFilename('*Service.ts') // Glob pattern
    .check();

  // Files like 'UserService.ts', 'ProductService.ts' will match
  // Files like 'ServiceHelper.ts' will NOT match
});

Examples of matchFilename() patterns:

// Glob patterns (recommended)
.matchFilename('Service*.ts')     // ✅ Service.ts, ServiceA.ts, ServiceImpl.ts
.matchFilename('*Controller.ts')  // ✅ UserController.ts, AdminController.ts
.matchFilename('Test?.spec.ts')   // ✅ TestA.spec.ts, TestB.spec.ts (? = single char)
.matchFilename('*Util*')          // ✅ StringUtil.ts, DateUtils.ts, MathUtility.ts

// Regular expressions
.matchFilename(/^Service.*\.ts$/) // ✅ Matches files starting with "Service"
.matchFilename(/.*\.(test|spec)\.ts$/) // ✅ Matches test files

// Exact string matching
.matchFilename('UserService.ts')  // ✅ Matches exactly "UserService.ts"

2. matchPath() - Full Path Matching

Matches patterns against the complete relative file path from the project root:

it('should match specific path patterns', async () => {
  const violations = await projectFiles()
    .should()
    .matchPath('src/services/*Service.ts') // Full path pattern
    .check();
});

Examples of matchPath() patterns:

TODO: does this really work? also, why two stars?

// Glob patterns for paths
.matchPath('src/*/services/*.ts')     // ✅ Service files in any module
.matchPath('**/test/**/*.spec.ts')    // ✅ Test files in any test directory
.matchPath('src/components/**/*.tsx') // ✅ All TSX files in components

// Regular expressions for paths
.matchPath(/^src\/services\/.*Service\.ts$/) // ✅ Services in specific folder
.matchPath(/^src\/.*\/.*\.component\.ts$/)   // ✅ Component files anywhere in src

3. containInFilename() - Partial Filename Matching

Checks if the filename contains the specified pattern as a substring:

it('should find files containing specific terms', async () => {
  const violations = await projectFiles()
    .shouldNot()
    .containInFilename('Test') // Files shouldn't contain "Test" in filename
    .check();
});

Examples of containInFilename() patterns:

.containInFilename('Service')    // ✅ UserService.ts, ServiceHelper.ts, MyServiceImpl.ts
.containInFilename('test')       // ✅ user.test.ts, test-utils.ts, testing.ts
.containInFilename(/service/i)   // ✅ Case-insensitive: UserService.ts, user-service.ts

4. containInPath() - Partial Path Matching

Checks if the full file path contains the specified pattern as a substring:

it('should find files with path containing specific terms', async () => {
  const violations = await projectFiles()
    .should()
    .containInPath('components') // Files should have 'components' in their path
    .check();
});

Examples of containInPath() patterns:

.containInPath('test')        // ✅ src/test/file.ts, src/components/test/file.ts
.containInPath('services')    // ✅ src/services/file.ts, lib/services/impl/file.ts
.containInPath(/spec|test/)   // ✅ Files with 'spec' or 'test' anywhere in path

Pattern Matching

.matchFilename('*Controller.ts')  // or
.matchFilename(/.*Controller\.ts$/)

Pattern Types and Syntax

1. Glob Patterns (Recommended for Strings)

TODO: do we really allow two stars?

Glob patterns provide intuitive wildcard matching:

  • * - Matches zero or more characters (excluding path separators)
  • ? - Matches exactly one character
  • ** - Matches zero or more path segments (in path matching)
// Glob pattern examples
'Service*'; // Service.ts, ServiceA.ts, ServiceImpl.ts
'*Controller.ts'; // UserController.ts, AdminController.ts
'Test?.spec.ts'; // TestA.spec.ts, TestB.spec.ts
'*Util*'; // StringUtil.ts, DateUtils.ts, MathUtility.ts

// Path glob patterns
'src/*/services/*.ts'; // Service files in any module
'**/test/**/*.spec.ts'; // Test files in any test directory

2. Regular Expressions

Use RegExp objects for complex pattern matching:

// Regular expression examples
/^Service.*\.ts$/           // Files starting with "Service"
/.*\.(test|spec)\.ts$/     // Test files (.test.ts or .spec.ts)
/^[A-Z].*Service\.ts$/     // PascalCase service files
/service/i                 // Case-insensitive matching

3. Literal Strings

Plain strings for exact or substring matching:

// Exact matching (with matchFilename/matchPath)
'UserService.ts'; // Matches exactly "UserService.ts"
'src/services/UserService.ts'; // Matches exact path

// Substring matching (with containInFilename/containInPath)
'Service'; // Contains "Service" anywhere
'test'; // Contains "test" anywhere

Advanced Filtering Examples

Complex File Selection

it('should validate complex architectural rules', async () => {
  // Services should follow naming convention
  const serviceViolations = await projectFiles()
    .inFolder('services')
    .should()
    .matchFilename(/^[A-Z].*Service\.ts$/) // PascalCase services
    .check();

  // Components should be in components folder
  const componentViolations = await projectFiles()
    .matchFilename('*.component.tsx')
    .should()
    .beInFolder('components')
    .check();

  // Test files should not be in production code
  const testViolations = await projectFiles()
    .containInFilename(/\.(test|spec)\./)
    .shouldNot()
    .beInFolder('src/production')
    .check();

  expect(serviceViolations).toEqual([]);
  expect(componentViolations).toEqual([]);
  expect(testViolations).toEqual([]);
});

Layered Architecture Validation

it('should enforce layered architecture', async () => {
  // Controllers should follow naming convention
  const controllerViolations = await projectFiles()
    .inFolder('controllers')
    .should()
    .matchFilename('*Controller.ts')
    .check();

  // Services should follow naming convention
  const serviceViolations = await projectFiles()
    .inFolder('services')
    .should()
    .matchFilename('*Service.ts')
    .check();

  // Repositories should follow naming convention
  const repoViolations = await projectFiles()
    .inFolder('repositories')
    .should()
    .matchFilename('*Repository.ts')
    .check();

  expect(controllerViolations).toEqual([]);
  expect(serviceViolations).toEqual([]);
  expect(repoViolations).toEqual([]);
});

Test File Organization

it('should enforce test file conventions', async () => {
  // Unit tests should end with .spec.ts
  const unitTestViolations = await projectFiles()
    .inFolder('tests/unit')
    .should()
    .matchFilename('*.spec.ts')
    .check();

  // Integration tests should end with .integration.ts
  const integrationTestViolations = await projectFiles()
    .inFolder('tests/integration')
    .should()
    .matchFilename('*.integration.ts')
    .check();

  expect(unitTestViolations).toEqual([]);
  expect(integrationTestViolations).toEqual([]);
});

📊 Library Comparison

Here's how ArchUnitTS compares to other TypeScript architecture testing libraries:

Feature ArchUnitTS ts-arch arch-unit-ts ts-arch-unit
API Stability ✅ Stable ✅ Stable ⚠️ Unstable ⚠️ Unstable
Circular Dependency Detection ✅ Supported ✅ Supported ❌ Limited ❌ No
Layer Dependency Rules ✅ Advanced patterns ✅ Advanced patterns ⚠️ Limited ❌ No
File Pattern Matching ✅ Glob + Regex ✅ Glob + Regex ⚠️ Simple patterns ❌ Basic
Custom Rules ✅ Full support ❌ No ❌ No ❌ No
Code Metrics ✅ Comprehensive ❌ No ❌ No ❌ No
Empty Test Detection ✅ Fails by default (configurable) ❌ No ❌ No ❌ No
Debug Logging ✅ Optional (off by default) ❌ No ❌ No ❌ No
LCOM Cohesion Analysis ✅ Multiple algorithms ❌ No ❌ No ❌ No
Distance Metrics ✅ Coupling & abstraction ❌ No ❌ No ❌ No
UML Diagram Validation ✅ Supported ✅ Supported ❌ No ❌ No
Architecture Slicing ✅ Supported ✅ Supported ❌ No ❌ No
Testing Framework Integration ✅ Universal (Jest, Vitest, Jasmine, Mocha, etc.) ⚠️ Jest only ⚠️ Limited ⚠️ Basic
HTML Report Generation ✅ Rich dashboards ❌ No ❌ No ❌ No
TypeScript AST Analysis ✅ Deep analysis ⚠️ Basic ⚠️ Limited ⚠️ Basic
Performance Optimization ✅ Caching + parallel ⚠️ Basic ❌ No ❌ No
Error Messages ✅ Detailed + clickable ⚠️ Basic ⚠️ Basic ⚠️ Basic
Documentation ✅ Comprehensive ⚠️ Basic ⚠️ Minimal ⚠️ Minimal
Community Support ✅ Active maintenance ✅ Active maintenance ❌ Inactive ❌ Inactive

As you see in the table, there are some features that are only supported by us. Here is a brief highlight of those that we believe are the most critical of them:

  • Empty Test Protection: This one is extremely important. Let's say you define architectural boundaries that shall not be crossed - but you have a typo in the path to some folder. Your test will just pass with other libraries! They will 'check the rule' on 0 files and the test 'passes'. ArchUnitTS detects this, we call it an empty test, and your test fails. This is the default behvaior, you can customize it to allow empty tests if you want to.

  • Testing framework support: ArchUnitTS works with any testing framework, plus we have special syntax extensions for Jest, Vitest and Jasmine. Other libraries such as ts-arch only have special support for Jest, or no special support at all.

  • Logging: We have great support for logs and different log levels. This can help to understand what files are being analyzed and why tests pass/fail. Other libraries have no logging support at all.

  • Code Metrics: Metrics such as cohesion, coupling metrics, distance from main sequence, and even custom metrics provide important insights into any projects code. ArchUnitTS is the only library with code metrics support.

  • Intelligent Error Messages: Our error messages contain clickable file paths and detailed violation descriptions. Again, other libraries do not have this.

  • Custom rules: ArchUnitTS is the only library that allows you to define custom rules and custom metrics.

  • HTML Reports: We support auto generated dashboards with charts and detailed breakdowns. Other libraries do not.

🔎 Informative Error Messages

When tests fail, you get helpful, colorful output with clickable file paths.

https://github.com/user-attachments/assets/04b26afb-53e9-4507-ba24-c8308b3a7922

Click on file paths to jump directly to the issue in your IDE.

📝 Debug Logging & Configuration

We support logging to help you understand what files are being analyzed and troubleshoot test failures. Logging is disabled by default to keep test output clean.

Enabling Debug Logging

it('should respect layered architecture', async () => {
  const rule = projectFiles()
    .inFolder('src/presentation')
    .shouldNot()
    .dependOnFiles()
    .inFolder('src/database');

  // Enable debug logging
  const violations = await rule.check({
    logging: {
      enabled: true,
      level: 'debug', // 'error' | 'warn' | 'info' | 'debug'
    },
  });

  expect(violations).toEqual([]);
});

Sample Debug Output

When debug logging is enabled, you'll see detailed information about the analysis:

[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)

Logging Configuration Options

TODO: these options are false!!

const violations = await rule.check({
  logging: {
    enabled: true, // Enable/disable logging (default: false)
    level: 'info', // Log level: 'error' | 'warn' | 'info' | 'debug'
    logTiming: true, // Add timestamps to log messages (default: true)
    colorOutput: true, // Colorized console output (default: true)
  },
});

🏈 Architecture Fitness Functions

The features of ArchUnitTS can very well be used as architectural fitness functions. See here for more information about that topic.

🔲 Core Modules

ArchUnitTS has the following core modules.

Module Description Status Links
Files File and folder based rules Stable src/files/README
Metrics Code quality metrics Stable src/metrics/README
Slices Architecture slicing Stable src/slices/README
Testing Universal test framework integration Stable src/testing/README
Common Shared utilities Stable src/common/
Reports Generate reports Experimental src/metrics/fluentapi/export-utils.ts

🕵️ Technical Deep Dive

How does ArchUnitTS work under the hood? See here for a deep dive!

🦊 Contributing

We highly appreciate contributions. We use GitHub Flow, meaning that we use feature branches, similar to GitFlow, but with proper CI and CD. As soon as something is merged or pushed to main it gets deployed. See more in Contributing.

ℹ️ FAQ

Q: What TypeScript/JavaScript testing frameworks are supported?

ArchUnitTS works with Jest, Jasmine, Vitest, Mocha, and any other testing framework. We have added special syntax support for Jest, Jasmine and Vitest, namely toPassAsync but, as said, ArchUnitTS works with any existing testing framework.

Q: Can I use ArchUnitTS with JavaScript projects?

Yes! While ArchUnitTS is built for TypeScript, it works with JavaScript projects too. You'll get the most benefit with TypeScript due to better static analysis capabilities.

Q: How do I handle false positives in architecture rules?

Use the filtering and targeting capabilities to exclude specific files or patterns. You can filter by file paths, class names, or custom predicates to fine-tune your rules.

Q: What's the difference between file-based and class-based rules?

File-based rules analyze import relationships between files, while class-based rules examine dependencies between classes and their members. Choose based on your architecture validation needs.

👥 Maintainers

LukasNiessen - Creator and main maintainer

janMagnusHeimann - Maintainer

draugang - Maintainer

💟 Contributors

🤝 Community & Support

Found a bug? Want to discuss features?

If ArchUnitTS helps your project, please consider:

  • Starring the repository 💚
  • Suggesting new features 💭
  • Contributing code or documentation ⌨️

⭐ Star History

Star History Chart

📄 License

This project is under the MIT license.


Go Back to Top


TODO: add elsewhere or just in docs TODO: add doc comments to extract-graph.ts

All Files Inclusion

ArchUnitTS ensures that all project files appear in the dependency graph, even if they don't import other project files. This is achieved by adding self-referencing edges for every file in the project.

Why this matters:

  • Standalone utility files are included in architectural analysis
  • Entry point files without imports are visible in the graph
  • Complete project coverage for architectural rules
  • No files are accidentally excluded from analysis

Example:

// 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

The graph will contain:

  • Import edges: Real dependencies between files (A imports B)
  • Self edges: Every project file references itself (ensures inclusion)

This guarantees comprehensive architectural analysis across your entire codebase.

Package Sidebar

Install

npm i archunit

Weekly Downloads

1,348

Version

2.1.22

License

MIT

Unpacked Size

1.1 MB

Total Files

387

Last publish

Collaborators

  • niessen