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

madmod

v0.1.0

Published

Auto-generate and maintain TypeScript re-export index files

Readme

madmod

Auto-generate and maintain TypeScript re-export index files.

Install

pnpm add -D madmod

Quick Start

# Create a starter config
madmod init

# Generate index files
madmod generate

# Check for drift (CI mode)
madmod check

# Watch for changes
madmod watch

The xm alias is available for all commands:

xm generate
xm check

Config

Create madmod.config.ts in your project root:

import { defineConfig } from 'madmod'

export default defineConfig({
  rules: [
    {
      dirs: 'src/lib/**',
      modules: [{ include: './*.ts', style: 'star' }],
    },
    {
      dirs: 'src/utils',
      modules: [{ include: './*.ts', style: 'namespace' }],
    },
  ],
})

Config files are loaded with jiti, so .ts, .js, and .mjs extensions all work.

Options

| Option | Type | Default | Description | | ------------ | ----------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------- | | rules | BarrelRule[] | [] | Rules defining which dirs to manage | | extensions | 'auto' \| 'none' \| '.js' \| '.ts' | 'auto' | Import specifier extension mode | | exclude | string[] | ['*.test.*', '*.spec.*', '*.stories.*', '*.d.ts'] | Glob patterns to exclude from barrels | | barrelFile | string | 'index.ts' | Name of the generated barrel file | | formatter | 'auto' \| 'biome' \| 'dprint' \| 'prettier' \| 'oxfmt' \| false | 'auto' | Formatter to run on generated files |

Rule Options

| Option | Type | Default | Description | | -------------- | -------------------------- | ------------------------------------- | ---------------------------------------- | | dirs | string | required | Glob pattern matching directories | | modules | (string \| ModuleGlob)[] | [{ include: './*', style: 'star' }] | Module patterns to include | | defaultStyle | 'star' \| 'namespace' | 'star' | Default export style for string patterns |

Module Pattern

Each module pattern has:

| Field | Type | Description | | --------- | ----------------------- | ----------------------------------------------------- | | include | string | Glob pattern matching files in the dir | | style | 'star' \| 'namespace' | star = export *, namespace = export * as Name |

When a pattern is a plain string, it uses the rule's defaultStyle.

Generated Output

Generated files include a header marker so madmod can distinguish them from hand-written files:

// @generated by madmod — DO NOT EDIT

export * from './auth.js'
export * from './billing.js'

Namespace style:

// @generated by madmod — DO NOT EDIT

export * as Auth from './auth.js'
export * as Billing from './billing.js'

Namespace names are derived from filenames via PascalCase conversion (my-service.ts becomes MyService).

Hand-written index files without the @generated header are never overwritten. They show as conflicts in the output.

CLI Commands

generate

Generate index files from config rules.

madmod generate [--config <path>] [--dry-run] [--no-cache]
  • --dry-run previews changes without writing
  • --config / -c specifies a custom config path
  • --no-cache bypasses the scan cache

check

Check index files for drift. Exits with code 1 if any files are stale. Intended for CI.

madmod check [--config <path>]

init

Create a starter madmod.config.ts.

madmod init

watch

Watch for file changes and regenerate affected index files. Reloads config automatically when madmod.config.* changes.

madmod watch [--config <path>]

Uses @parcel/watcher for native filesystem events. Filters out self-writes to avoid infinite loops.

doctor

Diagnose setup, validate config, and lint index files.

madmod doctor [--config <path>] [--fix]

Checks:

  • Config file exists and parses
  • Rule glob patterns match directories
  • tsconfig extension mode detection
  • Formatter detection
  • Index file staleness
  • Namespace collisions
  • Unmanaged directories (suggestions)

--fix auto-regenerates stale index files.

daemon

Background daemon management. Runs watch in a detached process.

madmod daemon start [--config <path>]
madmod daemon stop
madmod daemon status

Logs to node_modules/.cache/madmod/daemon.log.

Extension Auto-Detection

When extensions is 'auto' (the default), madmod reads your tsconfig.json to determine the right import specifier style:

| moduleResolution | allowImportingTsExtensions | Result | | --------------------- | --------------------------------------- | ------ | | bundler | any | none | | node16 / nodenext | false or absent | .js | | node16 / nodenext | true + noEmit/emitDeclarationOnly | .ts | | other / missing | any | none |

Override with extensions: '.js', extensions: '.ts', or extensions: 'none' in your config.

Formatter Auto-Detection

When formatter is 'auto', madmod looks for config files in this order:

  1. biome -- biome.json, biome.jsonc
  2. dprint -- dprint.json, .dprint.json
  3. prettier -- .prettierrc, .prettierrc.json, .prettierrc.yml, prettier.config.*

Falls back to checking devDependencies in package.json. Set formatter: false to disable.

Programmatic API

import { NodeContext } from '@effect/platform-node'
import {
  defineConfig,
  execute,
  loadConfig,
  plan,
  resolveDefaults,
} from 'madmod'
import { Effect } from 'effect'

const config = resolveDefaults({
  extensions: '.js',
  rules: [{ dirs: 'src/lib/**' }],
})

const program = Effect.gen(function*() {
  const result = yield* plan(config, process.cwd())
  const written = yield* execute(result)
  console.log(`Wrote ${written.length} files`)
})

program.pipe(Effect.provide(NodeContext.layer), Effect.runPromise)

Exports:

| Export | Description | | --------------------- | ----------------------------------- | | defineConfig | Type-safe config helper | | resolveDefaults | Apply defaults to a raw config | | loadConfig | Load and validate config from disk | | plan | Scan dirs and compute actions | | execute | Write planned actions to disk | | detectExtensionMode | Detect extension mode from tsconfig |

Types: Config, ResolvedConfig, BarrelRule, ResolvedRule, ModuleGlob, ExportStyle, Action, PlanResult, ExtensionMode.

License

MIT