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

@kagal/build-tsdoc

v0.3.0

Published

Build-hook adapter for @microsoft/api-extractor

Readme

@kagal/build-tsdoc

Build-hook adapter for @microsoft/api-extractor. Slim wrapper with dist/<entryName>.* defaults, a stub-aware skip, and per-bundler hook factories for unbuild and obuild.

The root entry runs the extraction; a dependency-light /utils subpath holds helpers for working with the emitted manifests without pulling in api-extractor.

Usage

Each factory returns a map keyed by the bundler's own hook names, so the result spreads straight into hooks. For unbuild:

import { defineBuildConfig } from 'unbuild';
import { newUnbuildHooks } from '@kagal/build-tsdoc';

export default defineBuildConfig({
  entries: [{ input: 'src/index', name: 'index' }],
  declaration: true,
  hooks: { ...newUnbuildHooks() },
});

obuild's end hook does not carry entries, and only its entries hook sees them bundler-resolved (absolute paths, effective stub flags), so newOBuildHooks() returns a pair that captures from one and extracts from the other:

import { defineBuildConfig } from 'obuild/config';
import { newOBuildHooks } from '@kagal/build-tsdoc';

export default defineBuildConfig({
  entries: [{ type: 'bundle', input: ['./src/index.ts'] }],
  hooks: { ...newOBuildHooks() },
});

Both obuild hooks must be wired — end throws HooksNotWiredError when entries never fired.

To combine extraction with other post-build steps, keep the map in a variable and call its hooks from your own wrappers:

import { copyFileSync } from 'node:fs';

import { defineBuildConfig } from 'obuild/config';
import { newOBuildHooks } from '@kagal/build-tsdoc';

const tsdoc = newOBuildHooks();

export default defineBuildConfig({
  entries: [{ type: 'bundle', input: ['./src/index.ts'] }],
  hooks: {
    entries: tsdoc.entries,
    end(context) {
      tsdoc.end(context);
      copyFileSync('dist/index.d.mts', 'dist/index.d.ts');
    },
  },
});

unbuild entries carry their bundler-resolved name; obuild entry names derive from each input basename. Stub builds are skipped via unbuild's options.stub or obuild's per-entry stub; per-entry outDir is honoured. Entries missing that data — e.g. hand-written name lists — are rejected with InvalidBuildEntryError: only the bundler's own entries carry the data extraction detects from.

Each entry writes <projectFolder>/<outDir>/<entryName>.api.json in @microsoft/api-extractor-model's wire format. Read it back with loadPackage from @kagal/build-tsdoc/utils, or ApiPackage.loadFromJsonFile() directly. For finer control, call extractEntryManifest({ projectFolder, entryName }) yourself per entry — the hooks are a loop over it.

A runnable example — a TypeScript 6.x consumer wired through the unbuild hooks — lives in examples/playground-ts6.

Defaults

Paths derive from projectFolder, outDir, and entryName:

| Option | Default | | --- | --- | | outDir | dist, resolved against <projectFolder> | | entryFile | <outDir>/<entryName>.d.mts | | outputPath | <outDir>/<entryName>.api.json | | tsconfigPath | <projectFolder>/tsconfig.json | | packageFullPath | <projectFolder>/package.json |

Override any of them individually for non-standard layouts.

newlineKind selects the manifest's line endings ('os' | 'crlf' | 'lf', default 'os'). The default follows the host, so the file matches whatever the consuming repo normalises to; pin 'lf' or 'crlf' to override. An omitted or unexpected value falls back to the host default.

Behaviour

  • Returns undefined when entryFile is missing — stub builds emit only the JS bundle, no declarations, so the call is safe to make unconditionally.
  • Runtime dependencies are passed to api-extractor as bundledPackages: a symbol re-exported from a dependency is part of the package contract, so it is documented as a member of the package itself. Dependencies the entry never references are a no-op.
  • Analysis uses the consumer's installed typescript, not api-extractor's bundled compiler: a package built on a newer TypeScript is parsed by the engine that emitted its declarations, so no version-mismatch notice is printed. A no-op when the consumer ships no TypeScript or it already matches the bundled version.
  • Throws when api-extractor reports any error. Warnings surface in the returned warningCount.
  • The bundler hooks reject duplicate entry names with DuplicateEntryNameError, entries missing their bundler-resolved data with InvalidBuildEntryError, and contexts that match no supported bundler shape with UnrecognisedBuildContextError. The single-entry extractEntryManifest has no list-level checks — callers iterating directly own any collision logic.

@kagal/build-tsdoc/utils

Dependency-light helpers for working with manifests, importable without pulling in api-extractor — both reading a manifest back and writing one out.

loadPackage reads a *.api.json manifest into an ApiPackage, the inverse of extractEntryManifest. It depends on @microsoft/api-extractor-model alone, so a renderer or SSR consumer can load a manifest without the build tooling:

import { loadPackage } from '@kagal/build-tsdoc/utils';

const pkg = loadPackage('dist/index.api.json');

It is file-based by necessity: api-extractor-model only rehydrates a package from a path, not an in-memory object.

serialiseJSON formats a value as JSON the way api-extractor writes manifests — 2-space indent, trailing newline, and configurable line endings:

import { serialiseJSON } from '@kagal/build-tsdoc/utils';

const json = serialiseJSON(value);     // host endings
const lf = serialiseJSON(value, 'lf');  // forced LF

The endings follow the same NewlineKind ('os' | 'crlf' | 'lf', default host) as the manifest writer; resolveNewlineKind exposes that resolution on its own. The subpath exports:

  • loadPackage(file) — read a *.api.json manifest into an ApiPackage
  • serialiseJSON(value, newlineKind?) — JSON text in api-extractor's manifest format
  • resolveNewlineKind(kind?) — resolve a NewlineKind to the host's concrete 'crlf' | 'lf'
  • NewlineKind — line-ending policy ('os' | 'crlf' | 'lf')
  • ConcreteNewlineKind — a NewlineKind with 'os' resolved ('crlf' | 'lf'); the type resolveNewlineKind returns

Exports

The lists below cover the root entry; the @kagal/build-tsdoc/utils subpath is documented above.

Functions

  • extractEntryManifest — extract one entry's .api.json
  • newUnbuildHooks, newOBuildHooks — per-bundler hook-map factories
  • asUnbuildContext, asOBuildContext — narrow an unknown context to the matching bundler shape

Types

  • ExtractEntryOptions, ExtractEntryResult — the helper's options and result
  • NewlineKind — manifest line-ending policy ('os' | 'crlf' | 'lf')
  • UnbuildHooks, UnbuildBuildHookContext, UnbuildBuildHookEntry — unbuild shapes
  • OBuildHooks, OBuildBuildHookContext, OBuildBuildHookEntry — obuild shapes

Errors

  • DuplicateEntryNameError, HooksNotWiredError, InvalidBuildEntryError, UnrecognisedBuildContextError

Constant

  • VERSION — package version

Licence

MIT