or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-traf--core

Find truly affected packages in monorepos based on line-level code changes

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@traf/core@0.0.x

To install, run

npx @tessl/cli install tessl/npm-traf--core@0.0.0

index.mddocs/

@traf/core

@traf/core is a TypeScript library that intelligently identifies truly affected projects in monorepos by analyzing code changes at the line and semantic level, rather than using traditional file-level dependency analysis. It uses the TypeScript compiler API to find changed code elements (functions, classes, constants) and recursively tracks their references across the monorepo to determine which projects are actually impacted by changes.

Package Information

  • Package Name: @traf/core
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @traf/core

Core Imports

import { trueAffected, DEFAULT_INCLUDE_TEST_FILES } from '@traf/core';
import type { TrueAffected, TrueAffectedProject, TrueAffectedLogging } from '@traf/core';
import type { CompilerOptions } from 'ts-morph';

This package is ESM-only ("type": "module" in package.json). Note that CompilerOptions is not exported from @traf/core and must be imported from ts-morph if you need to type the compilerOptions parameter.

Basic Usage

import { trueAffected } from '@traf/core';

const affectedProjects = await trueAffected({
  cwd: process.cwd(),
  rootTsConfig: 'tsconfig.base.json',
  base: 'origin/main',
  projects: [
    {
      name: 'my-app',
      sourceRoot: 'apps/my-app/src',
      tsConfig: 'apps/my-app/tsconfig.json',
    },
    {
      name: 'shared-lib',
      sourceRoot: 'libs/shared/src',
      tsConfig: 'libs/shared/tsconfig.json',
    },
  ],
});

console.log('Affected projects:', affectedProjects);
// Output: ['my-app', 'shared-lib']

Architecture

@traf/core operates through the following key processes:

  • Git Diff Analysis: Uses git to identify all changed lines in the current branch compared to a base branch
  • Semantic Code Analysis: Uses ts-morph (TypeScript Compiler API wrapper) to parse changed lines and identify the specific code elements that changed (functions, classes, constants, etc.)
  • Reference Tracking: Recursively finds all references to changed elements across the entire monorepo using TypeScript's language service
  • Project Mapping: Maps source files to their containing projects and builds the list of affected projects
  • Non-Source File Handling: Tracks changes to non-source files (assets, configs) by finding imports/requires of those files
  • Implicit Dependencies: Includes projects that declare implicit dependencies on affected projects
  • Lockfile Analysis: Experimental feature to detect dependency changes in package-lock files and find affected files

Capabilities

True Affected Analysis

Analyzes a monorepo to find projects that are truly affected by code changes in the current branch. Uses semantic-level change detection rather than file-level to reduce false positives.

/**
 * Finds projects that are truly affected by code changes based on line-level semantic analysis
 * @param options - Configuration object
 * @returns Promise resolving to array of affected project names
 */
function trueAffected(options: TrueAffected): Promise<string[]>;

interface TrueAffected extends TrueAffectedLogging {
  /** Current working directory */
  cwd: string;

  /** Path to root tsconfig file with path mappings for all projects (optional but recommended) */
  rootTsConfig?: string;

  /** Base branch to compare against (default: 'origin/main') */
  base?: string;

  /** Array of projects to analyze */
  projects: TrueAffectedProject[];

  /** Patterns to include regardless of semantic analysis (e.g., test files). Default: test/spec files */
  include?: (string | RegExp)[];

  /** TypeScript compiler options from ts-morph */
  compilerOptions?: CompilerOptions;

  /** Paths to ignore during analysis (default: node_modules, dist, build, .git) */
  ignoredPaths?: (string | RegExp)[];

  /** Experimental feature to detect lockfile changes and find affected files */
  __experimentalLockfileCheck?: boolean;
}

interface TrueAffectedProject {
  /** Project name */
  name: string;

  /** Project source root directory path */
  sourceRoot: string;

  /** Path to project's tsconfig file (default: {sourceRoot}/tsconfig.json) */
  tsConfig?: string;

  /** Array of project names that implicitly depend on this project */
  implicitDependencies?: string[];

  /** Build targets for the project */
  targets?: string[];
}

interface TrueAffectedLogging {
  /** Console-compatible logger instance (default: console with debug controlled by DEBUG env var) */
  logger?: Console;
}

Usage Example:

import { trueAffected } from '@traf/core';

// Analyze monorepo with custom configuration
const affected = await trueAffected({
  cwd: '/path/to/monorepo',
  rootTsConfig: 'tsconfig.base.json',
  base: 'origin/develop',
  projects: [
    {
      name: 'api-service',
      sourceRoot: 'apps/api/src',
      tsConfig: 'apps/api/tsconfig.json',
      implicitDependencies: ['database-migrations'],
    },
    {
      name: 'web-app',
      sourceRoot: 'apps/web/src',
      tsConfig: 'apps/web/tsconfig.json',
    },
    {
      name: 'shared-utils',
      sourceRoot: 'libs/utils/src',
      tsConfig: 'libs/utils/tsconfig.json',
    },
    {
      name: 'database-migrations',
      sourceRoot: 'libs/migrations',
    },
  ],
  include: [
    /\.(spec|test)\.(ts|js)x?$/,  // Include test files
    /\.config\.(ts|js)$/,          // Include config files
  ],
  ignoredPaths: [
    './node_modules',
    './dist',
    './coverage',
  ],
  logger: console,
});

console.log(`Found ${affected.length} affected projects:`, affected);

Notes:

  • The rootTsConfig should include a paths property mapping all projects so ts-morph can resolve references across the monorepo
  • Each project's tsConfig should only include files for that specific project
  • If a project's tsConfig doesn't exist, the library will add all .ts and .js files from the sourceRoot
  • The algorithm tracks changes recursively, so if function A is changed and function B references A, both projects containing A and B are marked as affected
  • Setting DEBUG=true environment variable enables detailed debug logging

Default Test File Pattern

Regular expression constant for the default test file inclusion pattern.

const DEFAULT_INCLUDE_TEST_FILES: RegExp;

This constant equals /\.(spec|test)\.(ts|js)x?/ and matches files like:

  • file.spec.ts
  • file.test.js
  • component.spec.tsx
  • utils.test.jsx

Usage Example:

import { trueAffected, DEFAULT_INCLUDE_TEST_FILES } from '@traf/core';

const affected = await trueAffected({
  cwd: process.cwd(),
  rootTsConfig: 'tsconfig.base.json',
  projects: [...],
  include: [
    DEFAULT_INCLUDE_TEST_FILES,
    /\.e2e\.(ts|js)$/,  // Also include e2e test files
  ],
});

Types

CompilerOptions

Type re-exported from ts-morph for TypeScript compiler configuration.

import { CompilerOptions } from 'ts-morph';

This type is used in the TrueAffected.compilerOptions property to configure the TypeScript compiler behavior during analysis. Common options include:

const affected = await trueAffected({
  cwd: process.cwd(),
  projects: [...],
  compilerOptions: {
    allowJs: true,           // Allow JavaScript files (default: true)
    strict: false,            // Disable strict type checking for analysis
    skipLibCheck: true,       // Skip type checking of declaration files
  },
});

How It Works

The algorithm performs the following steps:

  1. Git Diff Extraction: Runs git diff against the base branch to identify all changed files and the specific line numbers that changed in each file

  2. TypeScript Project Setup: Creates a ts-morph Project instance and adds all source files from the specified projects using their tsconfig files

  3. Changed Line Analysis: For each changed line in source files:

    • Finds the AST node at that line number
    • Identifies the root code element (function, class, const, etc.) containing that line
    • Marks the containing project as affected
  4. Reference Tracking: For each changed element:

    • Uses ts-morph's findReferencesAsNodes() to find all references to that element
    • Recursively processes each reference to find its references
    • Marks all projects containing references as affected
    • Maintains a visited set to avoid infinite loops
  5. Non-Source File Handling: For changed non-source files (images, JSON, etc.):

    • Searches the codebase for imports/requires of those files
    • Treats those import statements as changed lines and applies the reference tracking algorithm
  6. Test File Inclusion: Any changed files matching the include patterns cause their containing project to be marked as affected

  7. Implicit Dependencies: Projects that declare implicit dependencies on affected projects are added to the affected set

  8. Lockfile Analysis (experimental): If enabled and package-lock.json changed:

    • Compares lockfile before and after to find changed dependencies
    • Searches for imports of changed dependencies
    • Marks projects importing changed dependencies as affected

Error Handling

The library may throw errors in the following scenarios:

  • Git errors: If the base branch doesn't exist or git commands fail, an error is thrown with message: "Unable to get diff for base: \"{base}\". are you using the correct base?"
  • File resolution errors: If a file cannot be retrieved from a git revision, an error is thrown with message: "Unable to get file \"{filePath}\" for base: \"{base}\". are you using the correct base?"

Integration with Monorepo Tools

@traf/core is designed as a reusable core library for integration with various monorepo tools. Companion packages provide tool-specific integrations:

  • @traf/nx - Integration with Nx monorepo tool
  • @traf/turbo - Integration with Turbo monorepo tool (future)

These packages handle tool-specific project configuration and provide convenient wrappers around the core functionality.