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

@gentleduck/registry-build

v0.2.1

Published

Config-driven registry and index builder CLI.

Readme

@gentleduck/registry-build

A generic, extension-driven build system for component registries.

The runner is entirely extension-driven: users define their config with defineConfig(), register extensions (custom or built-in), and the builder runs them in order. The package provides built-in extensions for UI registries via uiRegistryPreset(), or users can create their own extensions for any domain.

Source Layout

The src folder is split by responsibility so the runtime flow is easy to trace:

  • src/config resolves config files through folder modules such as loader/, merge/, and resolution/, each with its own index.ts and local support files.
  • src/pipeline owns the build context, cache, and folder-based phases where each larger phase keeps its *.types.ts and *.lib.ts beside the runner.
  • src/extensions contains folder-based extension modules such as banner/, colors/, component-index/, extension/, and validate/, with UI-specific helpers isolated under src/extensions/ui.
  • src/extensions/ui now splits ownership by concern: ui.registry.types.ts for registry items, ui.config.types.ts for UI config contracts, ui.collection.types.ts for collection adapters, and ui.schema.ts or ui.collection.ts for behavior.
  • src/commands/build and src/adapters/component-index follow the same module-folder pattern, keeping command or adapter types beside their implementation files.
  • src/config/types.ts owns the config contract, src/config/loader/loader.types.ts owns loader-specific contracts, and src/extensions/extension/extension.types.ts owns the extension runtime contract.
  • src/lib contains shared filesystem, hashing, path, and transformation utilities.

Usage

Create a registry-build.config.ts beside the app or package that consumes the generated registry:

import { defineConfig, uiRegistryPreset } from '@gentleduck/registry-build'

export default defineConfig({
  extensions: [...uiRegistryPreset()],
  output: {
    dir: '.',
  },
  sources: {
    'registry:ui': {
      path: '../../packages/registry-ui/src',
      packageName: '@example/registry-ui',
      referencePath: '/registry-ui/src',
    },
  },
  registries: {
    uis: [
      {
        name: 'button',
        root_folder: 'button',
        type: 'registry:ui',
      },
    ],
  },
})

Every registry item type is validated as registry:${string}, and defineConfig() enforces the same namespace across source keys, package mappings, target paths, schema item types, and inline registry entries.

Run the builder from that consumer directory:

registry-build build

In a workspace app, the local installed binary works the same way:

./node_modules/.bin/registry-build build

Generic Collections

For non-UI consumers, start with collections plus extensions:

import { defineConfig } from '@gentleduck/registry-build'
import { archRepositoryExtension } from './arch-repository.extension'

export default defineConfig({
  collections: {
    packages: {
      data: './data/packages.json',
      metadata: {
        repoOrder: ['core', 'extra'],
      },
      sources: {
        pkgbuilds: {
          glob: '**/PKGBUILD',
          path: './pkgbuilds',
        },
      },
    },
  },
  extensions: [
    archRepositoryExtension({
      collection: 'packages',
    }),
  ],
  output: {
    dir: './dist',
  },
})

See the runnable example in examples/arch-package-index.

Incremental Builds

The builder keeps a local cache under <output.dir>/.registry-build/ by default.

Useful CLI flags:

registry-build build --changed-only
registry-build build --changed-only --changed ../../packages/registry-ui/src/button/button.tsx
  • --changed-only keeps the incremental cache on and reuses unchanged outputs aggressively
  • --changed <paths...> narrows rebuild work to entries affected by those paths

The build summary table now reports actual rewritten files per phase, so a warm no-op build should usually show Files as -.

Extensions

Attach optional behavior explicitly:

import { defineConfig, uiRegistryPreset } from '@gentleduck/registry-build'

export default defineConfig({
  extensions: [
    ...uiRegistryPreset({
      banner: { name: 'My Registry' },
      colors: { /* colors/themes config */ },
      componentIndex: { /* component-index config */ },
    }),
  ],
  output: {
    dir: '.',
  },
})

Config Composition

Configs can extend one or more preset files:

import { defineConfig } from '@gentleduck/registry-build'

export default defineConfig({
  extends: ['./presets/theme-preset.ts', './presets/source-preset.ts'],
  output: {
    dir: './apps/docs',
  },
})

extends is path-aware:

  • source paths are resolved relative to the file that declared them
  • theme/color data files are resolved relative to the preset that declared them
  • sources, registries, target paths, package mappings, and theme maps are merged

For code-driven composition, import preset objects and merge them through the public API:

import { defineConfig, mergeRegistryBuildConfigs } from '@gentleduck/registry-build'
import { baseConfig } from './presets/base'
import { themeConfig } from './presets/theme'

export default defineConfig(mergeRegistryBuildConfigs(baseConfig, themeConfig, {
  output: {
    dir: '.',
  },
}))

Consumer-specific presets (e.g., monorepo source layouts, project-specific theme maps) should live beside the consumer config, not inside this package.

Output

The builder writes:

<output.dir>/
  .registry-build/
  __ui_registry__/index.tsx
  public/r/
    colors/
    components/
    themes/
    index.json
    themes.css