Skip to content

ngrok/gen-x

Repository files navigation

@ngrok/gen-x

Generate package.json#exports from src

Automatically generate package.json exports from your source files with support for TypeScript, JavaScript, JSX/TSX, and CSS. Features include index file flattening, custom transform modes, and monorepo live types support.

Installation

Install @ngrok/gen-x to your devDependencies with your preferred package manager:

package manager command
npm npm install -DE @ngrok/gen-x
yarn yarn add -DE @ngrok/gen-x
pnpm pnpm add -DE @ngrok/gen-x
bun bun add -DE @ngrok/gen-x

Quick Start

# Generate exports from src/ to dist/
npx gen-x

# Preview changes without writing
npx gen-x --dry-run

This will scan your src/ directory and generate package.json exports like:

{
	"exports": {
		"./package.json": "./package.json",
		".": {
			"import": "./dist/index.js",
			"types": "./dist/index.d.ts"
		},
		"./utils": {
			"import": "./dist/utils.js",
			"types": "./dist/utils.d.ts"
		}
	}
}

Features

  • Index file flattening - index.ts".", lib/index.ts"./lib"
  • CSS support - Preserves extensions for assets, no types field
  • Multiple file types - .ts, .tsx, .js, .jsx, .mjs, .cjs, .css
  • Transform modes - camelCase, kebab-case, PascalCase, snake_case
  • Replace patterns - Rename exports with regex or string patterns
  • Custom conditions - For monorepo live types
  • Watch mode - Automatically regenerate exports on file changes
  • Source-only mode - Emit plain source file paths without import/types conditions
  • Cross-platform - Always generates POSIX paths for package.json
  • Collision detection - Errors on duplicate export keys

Configuration

gen-x is configured entirely through CLI flags.

CLI Options

Options:
  -V, --version                      output the version number
  --dry-run, --dryRun               Preview changes to stdout
  --exclude <exclude...>            Globs to exclude (default: ["**/*.d.ts","**/*.test.*","**/*.spec.*","**/__tests__/**"])
  --include <include...>            Globs to include (default: ["**/*.{ts,tsx,cts,mts,js,jsx,mjs,cjs,css}"])
  -i, --input <input>               Input directory (default: "src")
  -m, --mode <mode>                 Transform mode: passthrough|camelCase|kebab-case|PascalCase|snake_case (default: "passthrough")
  --customCondition <condition>     Custom condition for monorepo live types
  -o, --output <output>             Output directory (default: "dist")
  -p, --package <package>           Path to package.json (default: "package.json")
  -r, --replace <pattern:=>replacement...>  Replace export keys
  --sourceOnly                      Only emit plain source file paths in exports
  -w, --watch                       Watch the input directory for changes and regenerate exports
  -h, --help                        display help for command

Examples

Basic Usage

npx gen-x

Transform Export Names

# Transform to camelCase
npx gen-x --mode camelCase
# hello-world.ts → ./helloWorld

# Transform to kebab-case
npx gen-x --mode kebab-case
# HelloWorld.ts → ./hello-world

Replace Patterns

# Remove _pb suffix from protobuf files
npx gen-x --replace "/_pb/:=>"
# user_pb.ts → ./user (but still imports from user_pb.js)

# Multiple replacements
npx gen-x --replace "/_pb/:=>" --replace "/^api-/:=>api/"

Monorepo Live Types

For consuming packages in the same monorepo to use source TypeScript files:

npx gen-x --customCondition "@my-org/my-package/source"

Generates:

{
	"exports": {
		".": {
			"@my-org/my-package/source": "./src/index.ts",
			"import": "./dist/index.js",
			"types": "./dist/index.d.ts"
		}
	}
}

See Live Types in TypeScript Monorepo for more info.

CSS and Asset Files

CSS files automatically preserve their extensions:

# src/styles/theme.css → ./styles/theme.css
npx gen-x

Output:

{
	"./styles/theme.css": {
		"import": "./dist/styles/theme.css"
	}
}

Custom Include/Exclude

# Include only TypeScript files
npx gen-x --include "**/*.ts" --include "**/*.tsx"

# Exclude internal files
npx gen-x --exclude "**/internal/**" --exclude "**/*.test.*"

Watch Mode

Automatically regenerate exports when files are added, deleted, or renamed:

npx gen-x --watch

Writes are skipped when exports haven't changed, avoiding unnecessary downstream rebuilds in tools like Turborepo.

API Usage

import { generateExports } from "@ngrok/gen-x";

const exports = await generateExports({
	input: "src",
	output: "dist",
	mode: "camelCase",
});

console.log(exports);

Development

We use mise to manage the toolchain (Node.js and pnpm). Versions are pinned in .nvmrc (Node) and package.json#packageManager (pnpm), and locked in the committed mise.lock.

First, install mise (or run ./scripts/install-mise to install the pinned version):

OS command
macOS brew install mise
other curl https://mise.run | sh

For all other install methods, see the mise installation guide.

Don't forget to activate mise in your shell.

Next, clone the repo and move into the directory:

git clone https://github.com/ngrok/gen-x.git
cd gen-x

Next, run:

mise trust
mise install
mise run setup

This installs the pinned node and pnpm versions and then runs pnpm install --frozen-lockfile to install all node_modules.

Useful mise tasks:

  • mise run doctor — verify the active tools match the committed pins
  • mise run relock — refresh mise.lock after bumping .nvmrc or package.json#packageManager (then run mise install and commit both)
Prefer to install and manage the tooling yourself?
  1. Install the node version pinned in .nvmrc with your node version manager of choice.
  2. Enable pnpm with corepack: corepack enable pnpm
  3. Install pnpm with corepack: corepack install
  4. Install project dependencies with pnpm: pnpm install

About

Generate package.json#exports from src

Resources

Code of conduct

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages