Sovra Test Decider
Sovra is a high-performance, Rust-based utility for monorepos designed to identify affected test files by analyzing code changes and their import graphs. It leverages Oxc for its underlying dependency resolution, ensuring fast execution. The library provides a Node.js API to determine which tests should be run in a given CI pipeline, significantly speeding up large repository workflows. It ships with comprehensive TypeScript support, including path aliases, and offers configurable resolver options that align with `oxc-resolver`. Currently at version 0.2.0, it is in active development, focusing on performance and accuracy for JavaScript and TypeScript projects. Its primary differentiator is its Rust-powered speed and its direct integration for test selection based on file changes.
Common errors
-
Error: Dynamic import/require not supported for analysis.
cause Attempting to use `sovra` to analyze files containing import or require statements with dynamic paths (e.g., `require(variable)` or `import(`template-string/${var}`).fixModify the source code to use only static import paths that can be resolved at build time. Sovra's Rust-based analysis requires fixed import declarations. -
TypeError: Cannot read properties of undefined (reading 'files') or (reading 'errors')
cause Incorrectly accessing properties from the result of `getAffected` without handling potential `undefined` or null results, or if the function threw an unexpected error.fixEnsure you check `affected.files` and `affected.errors` for existence before attempting to access them, as shown in the quickstart example. Always wrap calls to `getAffected` in a `try...catch` block if not awaiting a Promise directly. -
Error: Cannot find module 'glob' or 'node:child_process'
cause The `glob` package or `node:child_process` (when used with `execSync`) are used in common examples but are not direct dependencies of `sovra` itself.fixInstall `glob` as a development dependency (`npm install -D glob` or `yarn add -D glob`). `node:child_process` is a built-in Node.js module and does not require installation; ensure your Node.js version is compatible and correctly referenced.
Warnings
- gotcha Sovra cannot analyze imports that use variables or expressions, as these can only be resolved at runtime. This includes patterns like `require(process.env.SOME_VAR)` or `import(`./file.${platform}.mjs`).
- gotcha As of version 0.2.0, Sovra is in early development. While stable for its current feature set, future minor versions might introduce breaking changes to the API surface as the project matures.
Install
-
npm install sovra -
yarn add sovra -
pnpm add sovra
Imports
- getAffected
import getAffected from 'sovra'; const getAffected = require('sovra');import { getAffected } from 'sovra'; - OxcResolverOptions
import { OxcResolverOptions } from 'oxc-resolver';import { type OxcResolverOptions } from 'sovra/resolver'; - CommonJS require
const sovra = require('sovra'); const getAffected = sovra.getAffected;const { getAffected } = require('sovra');
Quickstart
import { getAffected } from "sovra";
import { execSync } from "node:child_process";
import { glob } from "glob";
async function runAffectedTests() {
const testFiles = await glob("src/**/*.spec.{ts,tsx}");
const changedFiles = execSync("git diff --name-only main", { encoding: "utf8" })
.trim()
.split("\n")
.filter(Boolean);
if (testFiles.length === 0 || changedFiles.length === 0) {
console.log("No test files or changed files found.");
return;
}
const resolverOptions = {
tsconfig: {
configFile: "tsconfig.json"
}
};
const affected = await getAffected(testFiles, changedFiles, resolverOptions);
if (affected.errors && affected.errors.length > 0) {
console.error("Errors determining affected files:", ...affected.errors);
} else if (affected.files && affected.files.length > 0) {
console.log("Affected test files:", affected.files);
// Example: Run these tests with your test runner
// execSync(`pnpm jest ${affected.files.join(' ')}`, { stdio: 'inherit' });
} else {
console.log("No tests affected by the changes.");
}
}
runAffectedTests().catch(console.error);