npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@poleski/quality-tools

v0.2.0

Published

Portable TypeScript quality checks for project structure, complexity, mutation, and test health

Downloads

880

Readme

@poleski/quality-tools

Portable TypeScript quality checks for project structure, complexity, reachability, mutation, and test health.

The tools are intentionally project-agnostic. They discover packages from pnpm-workspace.yaml or package.json#workspaces, accept explicit file/folder targets, and read project-specific behavior from quality.config.json.

Install

pnpm add -D @poleski/quality-tools
pnpm exec quality-tools init

Before publish, use a local link from a host project:

pnpm add -D link:/absolute/path/to/quality-tools

Quick Start

pnpm exec quality-tools organize .
pnpm exec quality-tools acceptance compile --spec "tests/acceptance/specs/**/*.md" --steps "tests/acceptance/steps.ts" --out "tests/playwright/generated/acceptance.spec.ts"
pnpm exec quality-tools boundaries . --strict
pnpm exec quality-tools reachability . --strict
pnpm exec quality-tools scrap ./tests
pnpm exec quality-tools crap ./src
pnpm exec quality-tools mutate .
pnpm exec quality-tools mutate ./src/parser.ts --force
pnpm exec quality-tools mutate -- --mutate ./src/parser.ts

Targets can be the repo root, a package shorthand, a package root, a directory, or a file. Package shorthand is resolved from workspace package names, not from hardcoded folder names. The starter config uses src as a conventional default, but the tools scope from the target path plus your configured include/exclude globs.

Reports

All quality-tool artifacts use one report root:

{
  "reportsDir": "reports/quality-tools"
}

Current outputs:

  • reportsDir/organize/*.json for organize baselines
  • reportsDir/scrap/*.json for scrap baselines
  • reportsDir/crap/<target>/coverage-final.json for CRAP coverage input
  • reportsDir/mutation/mutation.json and mutation.html from Stryker
  • reportsDir/mutation/<target>/mutation.json copied per mutation target

CRAP reads the coverage report path you configure. The starter Vitest command writes that coverage under reportsDir/crap/<target>/.

Config

Run quality-tools init to create a starter quality.config.json.

{
  "reportsDir": "reports/quality-tools",
  "defaults": {
    "crap": {
      "coverage": {
        "command": "pnpm",
        "args": [
          "exec",
          "vitest",
          "run",
          "--coverage",
          "--coverage.reportsDirectory",
          "{repoRoot}/{reportsDir}/crap/{reportKey}"
        ],
        "coveragePath": "{repoRoot}/{reportsDir}/crap/{reportKey}/coverage-final.json"
      },
      "exclude": ["**/*.test.ts", "**/*.test.tsx", "**/*.d.ts"]
    },
    "mutation": {
      "include": ["src/**/*.ts", "src/**/*.tsx"],
      "exclude": ["src/**/*.d.ts"]
    },
    "scrap": {
      "include": ["tests/**/*.test.ts", "tests/**/*.test.tsx"],
      "exclude": []
    },
    "boundaries": {
      "include": ["src/**/*.ts", "src/**/*.tsx"],
      "exclude": ["src/**/*.d.ts", "**/*.test.ts", "**/*.test.tsx"],
      "entrypoints": [],
      "layers": []
    },
    "organize": {
      "lowInfoNames": {
        "banned": ["utils", "helpers", "misc", "common", "shared", "_shared", "lib", "index"],
        "discouraged": ["types", "constants", "config", "base", "core"]
      }
    }
  },
  "packages": {}
}

Package-specific config lives under packages.<packageName>. The package name is the unscoped manifest name by default, so @scope/parser is configured as parser.

CRAP

CRAP calculates complexity plus coverage risk and fails when a function exceeds the threshold. The default threshold is 8.

pnpm exec quality-tools crap ./src
pnpm exec quality-tools crap parser --threshold 10

CRAP needs Istanbul coverage-final.json. Configure the command and report path for your project. Put generated coverage under {reportsDir} when possible so all artifacts stay together:

{
  "defaults": {
    "crap": {
      "coverage": {
        "command": "pnpm",
        "args": [
          "exec",
          "vitest",
          "run",
          "--coverage",
          "--coverage.reportsDirectory",
          "{repoRoot}/{reportsDir}/crap/{reportKey}"
        ],
        "coveragePath": "{repoRoot}/{reportsDir}/crap/{reportKey}/coverage-final.json"
      }
    }
  },
  "packages": {
    "parser": {
      "crap": {
        "coverage": {
          "command": "pnpm",
          "args": [
            "--filter",
            "{packageJsonName}",
            "exec",
            "vitest",
            "run",
            "--coverage",
            "--coverage.reportsDirectory",
            "{repoRoot}/{reportsDir}/crap/{reportKey}"
          ],
          "coveragePath": "{repoRoot}/{reportsDir}/crap/{reportKey}/coverage-final.json"
        }
      }
    }
  }
}

Supported template values in CRAP coverage command fields:

  • {repoRoot}
  • {packageRoot}
  • {packageName}
  • {packageJsonName}
  • {reportKey}
  • {target} or {targetPath}
  • {reportsDir}

Mutation

Mutation runs Stryker against the selected source target. The CLI does not run typecheck first and does not assume any package is special.

pnpm exec quality-tools mutate .
pnpm exec quality-tools mutate parser
pnpm exec quality-tools mutate ./src/parser.ts --force
pnpm exec quality-tools mutate -- --mutate ./src/parser.ts

mutate requires an explicit target. Use . for a repo-wide run, a package shorthand for a workspace package, or a file/folder path for a narrower run. Bare quality-tools mutate is intentionally invalid so host projects can own their own all-package wrappers. --force is passed through to Stryker when you need to ignore incremental mutation state.

The CLI passes Stryker:

  • the Stryker config path
  • scoped mutate globs from quality.config.json
  • an incremental file path under reportsDir/mutation/<target>/
  • QUALITY_TOOLS_REPORTS_DIR, so the bundled base config writes to the shared report root

Long mutation runs print a progress heartbeat once per minute. The bundled base config also accepts these environment knobs:

  • QUALITY_TOOLS_STRYKER_CONCURRENCY, default 2
  • QUALITY_TOOLS_STRYKER_MAX_TEST_RUNNER_REUSE, default 0

Use a project Stryker config when you need custom Vitest wiring:

const base = require('@poleski/quality-tools/stryker.config.cjs');

module.exports = {
  ...base,
  vitest: {
    ...base.vitest,
    configFile: 'vitest.config.ts'
  }
};

Then reference it in quality.config.json:

{
  "defaults": {
    "mutation": {
      "strykerConfig": "stryker.config.cjs",
      "include": ["src/**/*.ts", "src/**/*.tsx"],
      "exclude": ["src/**/*.d.ts"]
    }
  }
}

If strykerConfig is omitted, the tool looks for stryker.config.cjs, stryker.config.mjs, stryker.config.js, or stryker.conf.js in the repo root, then falls back to the bundled base config.

Other Tools

pnpm exec quality-tools acceptance compile --spec "tests/acceptance/specs/**/*.md" --steps "tests/acceptance/steps.ts" --out "tests/playwright/generated/acceptance.spec.ts"
pnpm exec quality-tools organize ./src
pnpm exec quality-tools boundaries parser --strict
pnpm exec quality-tools reachability parser --strict
pnpm exec quality-tools scrap ./tests --strict
  • acceptance compiles human-authored Gherkin-ish Markdown specs into executable Playwright specs that import host-owned step bindings.
  • organize checks directory size, depth, naming, barrels, and cohesion. Use --write-baseline and --compare <path> for baseline workflows.
  • boundaries checks configured layers, entrypoints, dead surfaces, and dead ends.
  • reachability reports dead surfaces and dead ends without boundary-layer failures.
  • scrap reviews test files for split pressure, repeated setup, weak assertions, validation issues, and refactor recommendations.

Development

pnpm run test
pnpm run lint
pnpm run typecheck
pnpm run build