CLICLItypeA command you run, rather than a dependency you import.See every one →What this type is →independently maintainedindependently maintainedoriginPublished by one person or a small company outside SAP. Says nothing about quality; the readings say that.See every one →

enforce-export-coverage

A static-analysis command line that checks every exported TypeScript function has a corresponding test, without running the test suite.

Parses a project's TypeScript with ts-morph, extracts the names of the exported functions, resolves the candidate test files for each source file, and records a violation when no candidate test file contains a call to the function. It runs on the file paths it is given, which is the pre-commit mode on staged files, or with --all over the whole source root, which is the CI mode. Test locations follow configured conventions (co-located, a __tests__ subdirectory, or a mirrored test root), set in enforce-export-coverage.config.json or an enforceExportCoverage key in package.json. The README contrasts it with Jest's coverageThreshold: { functions: 100 }, which it calls overlapping but not equivalent. It checks that a call exists in a test file and does not run the tests, so it says nothing about whether the call passes or what it asserts.

Facts

Latestv1.0.0ReleasedLicenceMIT

Where it sits

Lives in
Provides
TestingCAPTestingcapabilityProving the service still does what it did — against the model and the handlers, without a deployed system and without a database somebody has to keep clean.
Serves

Who is behind it

Robin van het HofRobin van het HofPersonMaintains this
In the atlas: maintains 6, contributes to 3
@qualiture
maintainer

Before choosing this

Conditions

PracticalFunctional boundary

It reads TypeScript exports only; a project written in plain JavaScript has no exported functions for it to extract. A call to the function name in a test file counts as tested, and a call through property access such as .foo( does not.

What it declares

Every artifact

Type
CLI
Origin
Independently maintained
Runtime
Node.js

As a CLI

Distributed through
does not say

Connections1

Install

npm add enforce-export-coverage

Derived from the package name. No recipe recorded; the README has the configuration.

README

quoted from enforce-export-coverage@1.0.0 under MIT, read
enforce-export-coverage@1.0.0/README.mdOn npm

enforce-export-coverage

A static analysis tool that enforces every exported TypeScript function has a corresponding test — without running your test suite.

Designed to run as a fast pre-commit hook on staged files, or as a full project scan in CI.


Why not just set Jest's coverageThreshold: { functions: 100 }?

It's a fair question. The two approaches overlap but are not equivalent:

enforce-export-coverageJest functions: 100
Requires a dedicated testYes — the function name must appear as a call in a test fileNo — executing the function as a side effect of any test counts
ScopeExported functions only (your public API)All functions, including private helpers and anonymous callbacks
SpeedSub-second static analysis, no test executionRequires running the full test suite
Pre-commit (staged files only)YesImpractical — too slow for every commit
Actionable outputNames the exact function and file missing a testReports a percentage; you must open the HTML report to find what's missing

The honest take: if you have collectCoverageFrom configured and coverageThreshold: { functions: 100 } in CI, you catch most of the same problems at the pipeline level. The two tools are complementary: this one gives you fast, intent-based, export-focused enforcement before you commit; Jest's threshold gives you a hard runtime gate afterwards. Running both is not redundant — one is proactive, one is a safety net.


Installation

npm install --save-dev enforce-export-coverage

Or install globally to use the CLI anywhere:

npm install -g enforce-export-coverage

CLI usage

Check specific files (pre-commit hook pattern)

Pass file paths as arguments. Non-.ts files and test files are automatically skipped.

enforce-export-coverage src/handlers/foo.ts src/utils/bar.ts
Scan the entire source root
enforce-export-coverage --all
All options
enforce-export-coverage [files...]

Options:
  --all                  Scan all .ts files under srcRoot instead of reading from arguments
  --project-root <path>  Project root directory (default: cwd)
  --src-root <path>      Source directory, relative to project root (overrides config)
  --test-root <path>     Test mirror directory, relative to project root (overrides config)
  --config <path>        Path to a JSON config file
  --help                 Show this message

Exit codes:
  0  All exported functions have tests
  1  One or more exported functions are untested

Configuration

Configuration can be placed in a dedicated file or inside package.json. All fields are optional and fall back to the defaults shown below.

enforce-export-coverage.config.json
{
    "srcRoot": "src",
    "testRoot": "test",
    "testSuffixes": [".test.ts", ".spec.ts"],
    "testConventions": ["co-located", "__tests__", "mirror"],
    "ignore": []
}
package.json (alternative)
{
    "enforceExportCoverage": {
        "srcRoot": "srv",
        "testRoot": "test",
        "testConventions": ["mirror"]
    }
}
Config options
OptionTypeDefaultDescription
srcRootstring"src"Source directory to scan, relative to the project root
testRootstring"test"Root for mirror-style test files, relative to the project root
testSuffixesstring[][".test.ts", ".spec.ts"]File suffixes that identify test files
testConventionsstring[]["co-located", "__tests__", "mirror"]Which test file location patterns to check (see below)
ignorestring[][]Substrings matched against project-relative paths — matching files are skipped
Test conventions

The tool checks three conventions and considers a function tested if any of them finds a matching test file:

  • co-located — test file lives next to the source file
    src/handlers/foo.ts → src/handlers/foo.test.ts

  • __tests__ — test file lives in a __tests__ subdirectory
    src/handlers/foo.ts → src/handlers/__tests__/foo.test.ts

  • mirror — test file mirrors the source tree under testRoot
    src/handlers/foo.ts → test/handlers/foo.test.ts


Integrations

Pre-commit hook with lint-staged

lint-staged passes staged file paths as arguments automatically:

{
    "lint-staged": {
        "src/**/*.ts": "enforce-export-coverage"
    }
}
Pre-commit hook with Husky
# .husky/pre-commit
npx lint-staged

Or directly, using git diff to get staged .ts files:

# .husky/pre-commit
STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.ts$' | grep -v '\.test\.ts$')
[ -z "$STAGED" ] && exit 0
enforce-export-coverage $STAGED
CI — full project scan
# GitHub Actions example
- name: Enforce export coverage
  run: enforce-export-coverage --all --src-root srv --test-root test

Programmatic API

The package exports all its internals for use in custom scripts or other tools.

import { validateFunctionCoverage } from 'enforce-export-coverage';

const result = validateFunctionCoverage(['./src/handlers/foo.ts'], {
    projectRoot: '/path/to/project',
    srcRoot: 'src',
    testRoot: 'test',
    testConventions: ['mirror'],
});

if (!result.success) {
    for (const { functionName, filePath } of result.violations) {
        console.error(`'${functionName}' in ${filePath} has no test`);
    }
}
validateFunctionCoverage(sourceFiles, options?)
ParameterTypeDescription
sourceFilesstring[]Absolute paths to source files to check
options.projectRootstringAbsolute project root (default: process.cwd())
options.configPathstringPath to a JSON config file
options.srcRootstringOverrides srcRoot from config
options.testRootstringOverrides testRoot from config
options.testSuffixesstring[]Overrides testSuffixes from config
options.testConventionsstring[]Overrides testConventions from config
options.ignorestring[]Overrides ignore from config

Returns { success: boolean, violations: Array<{ functionName: string, filePath: string }> }.

Other exports
import {
    loadConfig,           // load + merge config from project files
    resolveTestFilePaths, // resolve candidate test paths for a source file
    getExportedFunctionNames, // extract exported function names via ts-morph
    isTestedIn,           // check if a function name is called in a test file
    findFiles,            // recursively find .ts files under a directory
} from 'enforce-export-coverage';

How it works

  1. For each source file, ts-morph parses the AST and extracts the names of all exported functions (both export function foo() and export const foo = () => {}).
  2. Candidate test file paths are resolved according to the configured conventions.
  3. Each candidate test file is read, comments are stripped, and a word-boundary regex checks for a call expression matching the function name. A property access (.foo() does not count.
  4. If no candidate file contains a call to the function, a violation is recorded.

License

MIT

MIT, read

History

Latest
1downloads per week, npm
Weekly downloads for enforce-export-coverage26 complete calendar weeks from 2026-04-27 to 2026-10-25; the latest total is 1.27 Apr – 3 May: 04 May – 10 May: 011 May – 17 May: 018 May – 24 May: 025 May – 31 May: 01 Jun – 7 Jun: 08 Jun – 14 Jun: 015 Jun – 21 Jun: 12722 Jun – 28 Jun: 1229 Jun – 5 Jul: 166 Jul – 12 Jul: 1413 Jul – 19 Jul: 6620 Jul – 26 Jul: 1127 Jul – 2 Aug: 53 Aug – 9 Aug: 710 Aug – 16 Aug: 1417 Aug – 23 Aug: 1424 Aug – 30 Aug: 1331 Aug – 6 Sept: 107 Sept – 13 Sept: 1014 Sept – 20 Sept: 1721 Sept – 27 Sept: 828 Sept – 4 Oct: 105 Oct – 11 Oct: 1812 Oct – 18 Oct: 219 Oct – 25 Oct: 1
Testing
  1. com.sap.cds:cds-services-bomMaven bill of materials pinning CAP Java module versions
  2. @cap-js/cds-testTest support for CAP Node.js: server start, HTTP client, assertions
  3. cds-cucumberGherkin scenarios driving a running CAP service
  4. sapbrunoTerminal UI running Bruno collections against a CAP service
  5. cds-launchpad-pluginSandbox launchpad for all SAPUI5 apps in a CAP workspace
  6. @sap-ux/fe-mockserver-plugin-cdsFiori Elements mock server plugin taking a CDS file as metadata

Details

Sources3
npm registrydiscovered herechecked
npm registry, enforce-export-coverageversion, releases, installschecked
README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
runtimesnode
latestv1.0.0
first release
releases and cadence1
discovered vianpm registry
first seen in the atlas
ecosystem and domaingeneral, developer-experience
History in the atlas1
  1. First seen.
For your agent
Ref
artifact:qualiture-enforce-export-coverage
JSON
/artifacts/qualiture-enforce-export-coverage.json
MCP
Connecting an agent