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

@raegen/tscheck

v0.1.7

Published

Lightweight type-checker for TypeScript monorepos with project references - no emit required

Downloads

801

Readme

tscheck

Lightweight type-checker for TypeScript monorepos with project references - no emit required.

Why?

TypeScript's tsc --build requires emitting files (at minimum --emitDeclarationOnly). This means:

  • You need outDir configured
  • Files are written to disk
  • CI pipelines need to handle generated artifacts

tscheck solves this by building .d.ts declarations in-memory only, allowing you to type-check monorepos with project references without producing any output files.

Warning

Bear in mind what this actually means. Because it's by no means magic. What tscheck does is a tradeoff:

+ no emitted artifacts to manage and a slight speed improvement

- noticably higher memory usage

So, if you're looking for a plug&play typechecking in a monorepo and you can afford the memory usage, this might be worth a try. Otherwise, just stick to stock.

Installation

npm install -D @raegen/tscheck typescript

CLI Usage

# Check the default tsconfig.json
tscheck

# Check a specific project
tscheck packages/app/tsconfig.json

# Only care about errors in THIS project, not its dependencies
tscheck packages/app/tsconfig.json --own

# Solution-style config: check each referenced project and report all errors
tscheck tsconfig.json

# Verbose: show which project is being checked and a summary at the end
tscheck tsconfig.json -v

# Skip references pointing outside the tsconfig's own directory
tscheck tsconfig.json --ignore-external-references

Options

| Flag | Description | |------|-------------| | -p, --project <path> | Path to tsconfig.json (default: ./tsconfig.json) | | --own-errors-only, --own | Only report errors owned by the target project (see Ownership); implies --ignore-external-references | | -r, --include-references | Treat all projects in the graph as "owned" (report all errors regardless of origin) | | --ignore-external-references | Skip project references that resolve outside the referencing tsconfig's directory | | --show-ownership | Show which project owns each error | | -t, --timing | Show per-project timing breakdown | | --pretty / --no-pretty | Enable/disable colored output (default: enabled when stdout is a TTY) | | -v, --verbose | Print each project as it is checked and a summary at the end | | --debug | Show debug information about the project graph and diagnostic attribution |

Exit Codes

| Code | Meaning | |------|---------| | 0 | No type errors (or no owned errors when --own) | | 1 | Type errors found | | 2 | Configuration or runtime error |

Ownership

The --own flag controls which errors count toward the exit code and are displayed.

For a plain (non-solution) tsconfig, owned files are those matched by its include/files patterns. Errors in files that are only reachable via imports from dependencies — even if those dependencies are listed in references — are not owned.

For a solution-style tsconfig (files: [] with only references), tscheck recurses through the solution chain. Any leaf tsconfig reachable by following only solution-style nodes is owned. The references inside those leaf configs (e.g. a shared library added by a sync tool) are dependency references — their errors are not owned from the solution's perspective.

Example:

monorepo/tsconfig.json          ← solution (files: [])
  packages/app/tsconfig.json    ← solution (files: [])
    tsconfig.app.json           ← leaf (owned from monorepo root)
    tsconfig.node.json          ← leaf (owned from monorepo root)
      packages/shared           ← dependency reference (NOT owned from app, IS owned from monorepo root)

Running tscheck packages/app/tsconfig.json --own will not report errors in packages/shared, because shared is only a dependency of the leaf configs, not part of the app's solution chain.

Running tscheck monorepo/tsconfig.json --own will report errors in packages/shared, because shared appears as a leaf in the monorepo's solution chain (it has files and is reachable from the root).

--own implies --ignore-external-references so that tsconfig references added by tools like the NX TypeScript sync generator (which may point to workspace peers outside the package directory) are silently skipped rather than causing spurious TS6305 errors.

Programmatic API

Simple API

import { check } from '@raegen/tscheck';

const result = check('./tsconfig.json');

if (!result.success) {
  console.log(result.formattedDiagnostics);
  console.log(result.formattedSummary);
  process.exit(1);
}

check() accepts either a path string or an options object:

import { check } from '@raegen/tscheck';

const result = check({
  project: './tsconfig.json',
  ownErrorsOnly: true,
  includeReferences: true,
  ignoreExternalReferences: true,
  colors: true,
});

Low-level API

import { checkProjects } from '@raegen/tscheck';

const result = checkProjects({
  project: './tsconfig.json',
  ownErrorsOnly: true,
  onProjectStart: (project, index, total) => {
    console.log(`[${index + 1}/${total}] ${project.configPath}`);
  },
  onDiagnostic: (attributed) => {
    if (attributed.isOwned) process.stdout.write('!');
  },
});

if (!result.success) {
  console.log(`Found ${result.ownedErrorCount} owned errors`);
  process.exit(1);
}

CheckOptions

interface CheckOptions {
  /** Path to root tsconfig.json */
  project: string;
  /** Only count/report errors owned by the target project (see Ownership section) */
  ownErrorsOnly?: boolean;
  /** Treat all projects in the graph as "owned" */
  includeReferences?: boolean;
  /**
   * Skip project references that resolve outside the referencing tsconfig's directory.
   * Implied when ownErrorsOnly is true.
   */
  ignoreExternalReferences?: boolean;
  /** Callback for each diagnostic as it's found */
  onDiagnostic?: (diagnostic: AttributedDiagnostic) => void;
  /** Callback when starting to check a project (only fires for projects with files) */
  onProjectStart?: (project: ProjectNode, index: number, total: number) => void;
  /** Callback when finished checking a project (only fires for projects with files) */
  onProjectEnd?: (project: ProjectNode, diagnostics: AttributedDiagnostic[], durationMs?: number) => void;
}

CheckResult

interface CheckResult {
  /** Whether type-checking passed with no errors */
  success: boolean;
  /** All diagnostics (errors and warnings) */
  diagnostics: ts.Diagnostic[];
  /** Diagnostics with project attribution */
  attributedDiagnostics: AttributedDiagnostic[];
  /** Number of projects checked (solution-style umbrella configs excluded) */
  projectCount: number;
  /** Total number of files checked */
  fileCount: number;
  /** Total number of errors */
  errorCount: number;
  /** Number of warnings */
  warningCount: number;
  /** Number of errors owned by the checked project(s) */
  ownedErrorCount: number;
  /** Number of errors from dependencies */
  dependencyErrorCount: number;
  /** Per-project timing information */
  projectTimings: ProjectTiming[];
  /** Total checking duration in milliseconds */
  totalDurationMs: number;
}

AttributedDiagnostic

interface AttributedDiagnostic {
  /** The underlying TypeScript diagnostic */
  diagnostic: ts.Diagnostic;
  /** The project that was being checked when this diagnostic was found */
  sourceProject: ProjectNode;
  /** The project that owns the file where the error is */
  owningProject: ProjectNode | null;
  /** Whether this error is owned by the target project (see Ownership section) */
  isOwned: boolean;
}

How It Works

  1. Parse project graph — Resolves tsconfig.json and all project references into a dependency graph, topologically sorted so dependencies come first.
  2. Virtual file system — Creates an in-memory compiler host that captures .d.ts output from each project.
  3. Type-check — Runs TypeScript's semantic analysis on each project in order, writing declarations to memory rather than disk.
  4. Cross-project resolution — Later projects resolve imports from earlier ones via the in-memory .d.ts files, exactly as tsc --build would, but without touching the filesystem.
  5. Diagnostic attribution — Every diagnostic is tagged with its owning project, enabling --own and --show-ownership to work correctly across the graph.

License

MIT