{"id":13454704,"url":"https://github.com/privatenumber/pkgroll","last_synced_at":"2026-02-10T18:11:18.831Z","repository":{"id":40236193,"uuid":"476460891","full_name":"privatenumber/pkgroll","owner":"privatenumber","description":"📦  Zero-config package bundler for Node.js + TypeScript","archived":false,"fork":false,"pushed_at":"2025-04-24T17:23:00.000Z","size":2447,"stargazers_count":1398,"open_issues_count":25,"forks_count":39,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-29T13:56:04.237Z","etag":null,"topics":["bundler","cjs","commonjs","declaration","dts","esm","exports","javascript","noconfig","nodejs","rollup","typescript","zero-config"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/privatenumber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"privatenumber"}},"created_at":"2022-03-31T20:05:51.000Z","updated_at":"2025-04-28T02:21:25.000Z","dependencies_parsed_at":"2023-12-19T18:46:58.244Z","dependency_job_id":"a39d573a-a2b7-4ceb-9893-50649883c791","html_url":"https://github.com/privatenumber/pkgroll","commit_stats":{"total_commits":98,"total_committers":4,"mean_commits":24.5,"dds":0.0714285714285714,"last_synced_commit":"fd398e4a7a8ac5791bdd2dacb7d253fc168a0b34"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpkgroll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpkgroll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpkgroll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpkgroll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/privatenumber","download_url":"https://codeload.github.com/privatenumber/pkgroll/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254020616,"owners_count":22000755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bundler","cjs","commonjs","declaration","dts","esm","exports","javascript","noconfig","nodejs","rollup","typescript","zero-config"],"created_at":"2024-07-31T08:00:57.102Z","updated_at":"2026-02-10T18:11:18.824Z","avatar_url":"https://github.com/privatenumber.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n\t\u003cimg width=\"130\" src=\".github/logo.webp\"\u003e\n\u003c/p\u003e\n\u003ch1 align=\"center\"\u003e\n\t\u003csup\u003epkgroll\u003c/sup\u003e\n\t\u003cbr\u003e\n\t\u003ca href=\"https://npm.im/pkgroll\"\u003e\u003cimg src=\"https://badgen.net/npm/v/pkgroll\"\u003e\u003c/a\u003e \u003ca href=\"https://npm.im/pkgroll\"\u003e\u003cimg src=\"https://badgen.net/npm/dm/pkgroll\"\u003e\u003c/a\u003e\n\u003c/h1\u003e\n\n_pkgroll_ is a JavaScript package bundler powered by Rollup that automatically builds your package from entry-points defined in `package.json`. No config necessary!\n\nWrite your code in TypeScript/ESM and run `pkgroll` to get ESM/CommonJS/.d.ts outputs!\n\n### Features\n- ✅ `package.json#exports` to define entry-points\n- ✅ Dependency externalization\n- ✅ Minification\n- ✅ TypeScript support + `.d.ts` bundling\n- ✅ Watch mode\n- ✅ CLI outputs (auto hashbang insertion)\n\n\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://github.com/sponsors/privatenumber/sponsorships?tier_id=398771\"\u003e\u003cimg width=\"412\" src=\"https://raw.githubusercontent.com/privatenumber/sponsors/master/banners/assets/donate.webp\"\u003e\u003c/a\u003e\n\t\u003ca href=\"https://github.com/sponsors/privatenumber/sponsorships?tier_id=397608\"\u003e\u003cimg width=\"412\" src=\"https://raw.githubusercontent.com/privatenumber/sponsors/master/banners/assets/sponsor.webp\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003csup\u003e\u003ci\u003eAlready a sponsor?\u003c/i\u003e Join the discussion in the \u003ca href=\"https://github.com/pvtnbr/pkgroll\"\u003eDevelopment repo\u003c/a\u003e!\u003c/sup\u003e\u003c/p\u003e\n\n## Install\n```sh\nnpm install --save-dev pkgroll\n```\n\n## Quick setup\n1. Setup your project with source files in `src` and output in `dist` (configurable).\n\n2. Define package entry-files in `package.json`.\n\n    [These configurations](https://nodejs.org/api/packages.html#package-entry-points) are for Node.js to determine how to import the package.\n\n    Pkgroll leverages the same configuration to determine how to build the package.\n\n\t```json5\n\t{\n\t    \"name\": \"my-package\",\n\n\t    // Set \"module\" or \"commonjs\" (https://nodejs.org/api/packages.html#type)\n\t    // \"type\": \"module\",\n\n\t    // Define the output files\n\t    \"main\": \"./dist/index.cjs\",\n\t    \"module\": \"./dist/index.mjs\",\n\t    \"types\": \"./dist/index.d.cts\",\n\n\t    // Define output files for Node.js export maps (https://nodejs.org/api/packages.html#exports)\n\t    \"exports\": {\n\t        \"require\": {\n\t            \"types\": \"./dist/index.d.cts\",\n\t            \"default\": \"./dist/index.cjs\",\n\t        },\n\t        \"import\": {\n\t            \"types\": \"./dist/index.d.mts\",\n\t            \"default\": \"./dist/index.mjs\",\n\t        },\n\t    },\n\n\t    // bin files will be compiled to be executable with the Node.js hashbang\n\t    \"bin\": \"./dist/cli.js\",\n\n\t    // (Optional) Add a build script referencing `pkgroll`\n\t    \"scripts\": {\n\t        \"build\": \"pkgroll\",\n\t    },\n\n\t    // ...\n\t}\n\t```\n\n\tPaths that start with `./dist/` are automatically mapped to files in the `./src/` directory.\n\n\u003e [!TIP]\n\u003e In addition to `package.json`, pkgroll also supports [pnpm's `package.yaml`](https://pnpm.io/package_json#:~:text=In%20addition%20to%20the%20traditional%20package.json%20format%2C%20pnpm%20also%20supports%20package.json5%20(via%20json5)%20and%20package.yaml%20(via%20js%2Dyaml).).\n\n3. Package roll!\n\t```sh\n\tnpm run build # or npx pkgroll\n\t```\n\n## Usage\n\n### Entry-points\n_Pkgroll_ parses package entry-points from `package.json` by reading properties `main`, `module`, `types`, and `exports`.\n\nThe paths in `./dist` are mapped to paths in `./src` (configurable with the `--srcdist` flag) to determine bundle entry-points.\n\n#### Wildcard exports\n\n_Pkgroll_ supports [wildcard patterns](https://nodejs.org/api/packages.html#subpath-patterns) in `package.json#exports` for exporting multiple modules with a single pattern:\n\n```json5\n{\n    \"exports\": {\n        \"./utils/*\": \"./dist/utils/*.mjs\",\n        \"./components/*/index\": \"./dist/components/*/index.mjs\",\n    },\n}\n```\n\nThis automatically bundles all matching source files. For example:\n- `src/utils/format.ts` → `dist/utils/format.mjs`\n- `src/components/button/index.ts` → `dist/components/button/index.mjs`\n\n\u003e [!IMPORTANT]\n\u003e Wildcard patterns must include a file extension (e.g., `.mjs`, `.cjs`)\n\n#### Subpath Imports\n\n_Pkgroll_ supports building entry-points defined in [`package.json#imports` (Node.js subpath imports)](https://nodejs.org/api/packages.html#subpath-imports), including conditional imports:\n\n```json\n{\n    \"imports\": {\n        \"#my-pkg\": \"./dist/index.js\",\n        \"#env\": {\n            \"node\": \"./dist/env.node.js\",\n            \"default\": \"./dist/env.browser.js\"\n        }\n    }\n}\n```\n\n### Output formats\n_Pkgroll_ detects the format for each entry-point based on the file extension or the `package.json` property it's placed in, using the [same lookup logic as Node.js](https://nodejs.org/api/packages.html#determining-module-system).\n\n| `package.json` property | Output format |\n| - | - |\n| `main` | Auto-detect |\n| `module` | ESM\u003cbr\u003e\u003csub\u003eNote: This [unofficial property](https://stackoverflow.com/a/42817320/911407) is not supported by Node.js and is mainly used by bundlers.\u003c/sub\u003e |\n| `types` | TypeScript declaration |\n| `exports` | Auto-detect |\n| `exports.require` | CommonJS |\n| `exports.import` | Auto-detect |\n| `exports.types` | TypeScript declaration |\n| `bin` | Auto-detect\u003cbr\u003eAlso patched to be executable with the Node.js hashbang. |\n\n_Auto-detect_ infers the type by extension or `package.json#type`:\n\n| Extension | Output format |\n| - | - |\n| `.cjs` | [CommonJS](https://nodejs.org/api/packages.html#:~:text=Files%20ending%20with%20.cjs%20are%20always%20loaded%20as%20CommonJS%20regardless%20of%20the%20nearest%20parent%20package.json) |\n| `.mjs` | [ECMAScript Modules](https://nodejs.org/api/modules.html#the-mjs-extension) |\n| `.js` | Determined by `package.json#type`, defaulting to CommonJS |\n\n\n### Dependency bundling \u0026 externalization\n\nPackages to externalize are detected by reading dependency types in `package.json`. Only dependencies listed in `devDependencies` are bundled in.\n\nWhen generating type declarations (`.d.ts` files), this also bundles and tree-shakes type dependencies declared in `devDependencies` as well.\n\n```json5\n// package.json\n{\n    // ...\n\n    \"peerDependencies\": {\n        // Externalized\n    },\n    \"dependencies\": {\n        // Externalized\n    },\n    \"optionalDependencies\": {\n        // Externalized\n    },\n    \"devDependencies\": {\n        // Bundled\n    },\n}\n```\n\n### Aliases\n\n#### Import map\n\nYou can configure aliases using the [import map](https://nodejs.org/api/packages.html#imports) in `package.json#imports`.\n\nIn Node.js, import mappings must start with `#` to indicate an internal [subpath import](https://nodejs.org/api/packages.html#subpath-imports). However, _Pkgroll_ allows defining aliases **without** the `#` prefix.\n\nExample:\n\n```json5\n{\n    \"imports\": {\n        // Alias '~utils' points to './src/utils.js'\n        \"~utils\": \"./src/utils.js\",\n\n        // Native Node.js subpath import (must use '#', can't reference './src')\n        \"#internal-package\": \"./vendors/package/index.js\",\n    },\n}\n```\n\n#### Tsconfig paths\n\nYou can also define aliases in `tsconfig.json` using `compilerOptions.paths`:\n\n```json5\n{\n    \"compilerOptions\": {\n        \"paths\": {\n            \"@foo/*\": [\n                \"./src/foo/*\",\n            ],\n            \"~bar\": [\n                \"./src/bar/index.ts\",\n            ],\n        },\n    },\n}\n```\n\n\u003e [!TIP]\n\u003e The community is shifting towards using import maps (`imports`) as the source of truth for aliases because of their wider support across tools like Node.js, TypeScript, Vite, Webpack, and esbuild.\n\n### Target\n\n_Pkgroll_ uses [esbuild](https://esbuild.github.io/) to handle TypeScript and JavaScript transformation and minification.\n\nThe target specifies the environments the output should support. Depending on how new the target is, it can generate less code using newer syntax. Read more about it in the [esbuild docs](https://esbuild.github.io/api/#target).\n\n\nBy default, the target is set to the version of Node.js used. It can be overwritten with the `--target` flag:\n\n```sh\npkgroll --target=es2020 --target=node14.18.0\n```\n\nIt will also automatically detect and include the `target` specified in `tsconfig.json#compilerOptions`.\n\n\n#### Strip `node:` protocol\nNode.js builtin modules can be prefixed with the [`node:` protocol](https://nodejs.org/api/esm.html#node-imports) for explicitness:\n\n```js\nimport fs from 'node:fs/promises'\n```\n\nThis is a new feature and may not work in older versions of Node.js. While you can opt out of using it, your dependencies may still be using it (example package using `node:`: [path-exists](https://github.com/sindresorhus/path-exists/blob/7c95f5c1f5f811c7f4dac78ab5b9e258491f03af/index.js#L1)).\n\nPass in a Node.js target that that doesn't support it to strip the `node:` protocol from imports:\n\n```sh\npkgroll --target=node12.19\n```\n\n### Custom `tsconfig.json` path\n\nBy default, _Pkgroll_ looks for `tsconfig.json` configuration file in the current working directory. You can pass in a custom `tsconfig.json` path with the `--tsconfig` flag:\n\n```sh\npkgroll --tsconfig=tsconfig.build.json\n```\n\n### Export condition\n\nSimilarly to the target, the export condition specifies which fields to read from when evaluating [export](https://nodejs.org/api/packages.html#exports) and [import](https://nodejs.org/api/packages.html#imports) maps.\n\nFor example, to simulate import resolutions in Node.js, pass in `node` as the export condition:\n```sh\npkgroll --export-condition=node\n```\n\n\n### ESM ⇄ CJS interoperability\n\nNode.js ESM offers [interoperability with CommonJS](https://nodejs.org/api/esm.html#interoperability-with-commonjs) via [static analysis](https://github.com/nodejs/cjs-module-lexer). However, not all bundlers compile ESM to CJS syntax in a way that is statically analyzable.\n\nBecause _pkgroll_ uses Rollup, it's able to produce CJS modules that are minimal and interoperable with Node.js ESM.\n\nThis means you can technically output in CommonJS to get ESM and CommonJS support.\n\n#### `require()` in ESM\nSometimes it's useful to use `require()` or `require.resolve()` in ESM. ESM code that uses `require()` can be seamlessly compiled to CommonJS, but when compiling to ESM, Node.js will error because `require` doesn't exist in the module scope.\n\nWhen compiling to ESM, _Pkgroll_ detects `require()` usages and shims it with [`createRequire(import.meta.url)`](https://nodejs.org/api/module.html#modulecreaterequirefilename).\n\n### Native modules\n\n_pkgroll_ automatically handles native Node.js addons (`.node` files) when you directly import them:\n\n```js\n// src/index.js\nimport nativeAddon from './native.node'\n```\n\nAfter bundling, the `.node` file will be copied to `dist/natives/` and the import will be automatically rewritten to load from the correct location at runtime.\n\n\u003e [!NOTE]\n\u003e - Native modules are platform and architecture-specific. Make sure to distribute the correct `.node` files for your target platforms.\n\u003e - This only works with direct `.node` imports. If you're using packages that dynamically load native modules via `bindings` or `node-pre-gyp`, you'll need to handle them separately.\n\n#### Handling dependencies with native modules\n\nIf you're using packages with native modules (like `chokidar` which depends on `fsevents`):\n\n- **If in `dependencies`/`peerDependencies`**: ✅ Works automatically - these are externalized (not bundled)\n- **If in `devDependencies`**: ⚠️ Will be bundled. If they use `bindings()` or `node-pre-gyp` patterns, move them to `dependencies` instead, or use `optionalDependencies` if they're optional.\n\nExample - if you have `chokidar` in `devDependencies` and get build errors, move it to `dependencies`:\n\n```json\n{\n    \"dependencies\": {\n        \"chokidar\": \"^3.0.0\"\n    }\n}\n```\n\nThis externalizes it (users will need to install it), avoiding the need to bundle its native modules.\n\n### Environment variables\nPass in compile-time environment variables with the `--env` flag.\n\nThis will replace all instances of `process.env.NODE_ENV` with `'production'` and remove unused code:\n```sh\npkgroll --env.NODE_ENV=production\n```\n\n### Define\nThe `--define` flag allows you to replace specific strings in your code at build time. This is useful for dead code elimination and conditional compilation.\n\n```sh\npkgroll --define.process.env.NODE_ENV='\"production\"' --define.DEBUG=false\n```\n\nNote: Unlike `--env`, values are not automatically JSON stringified, so you need to include quotes for string values.\n\n### Minification\nPass in the `--minify` flag to minify assets.\n```sh\npkgroll --minify\n```\n\n### Watch mode\nRun the bundler in watch mode during development:\n```sh\npkgroll --watch\n```\n\n### Clean dist\nClean dist directory before bundling:\n```sh\npkgroll --clean-dist\n```\n\n### License extraction\nGenerate a file containing licenses for all bundled dependencies:\n```sh\npkgroll --license\n```\n\nThis collects license information from all `devDependencies` that are bundled into your package and writes them to a LICENSE file. This helps ensure compliance with open-source license requirements.\n\n#### Custom output path\nSpecify a custom output file:\n```sh\npkgroll --license=THIRD-PARTY-LICENSES.txt\n```\n\n#### Auto-detection\nWhen no path is specified, pkgroll looks for existing LICENSE files (`LICENSE`, `LICENSE.txt`, `LICENSE.md`, `LICENCE`, `LICENCE.txt`, `LICENCE.md`) in your project root. If none exist, it creates a `LICENSE` file.\n\n#### Content placement\nIf the LICENSE file already has a `----------- BUNDLED DEPENDENCIES -----------` marker, everything from the marker to the end of the file is replaced with the updated licenses. This preserves your project's license text above the marker.\n\nIf no marker is found, the bundled licenses (with the marker) are appended to the end of the file. Subsequent runs will find the marker and replace in-place.\n\n\u003e [!NOTE]\n\u003e License extraction is skipped in watch mode since it's typically only needed for final builds.\n\n### Source maps\nPass in the `--sourcemap` flag to emit source map files for both JavaScript and TypeScript declaration outputs:\n\n```sh\npkgroll --sourcemap\n```\n\nOr to inline them in the distribution files:\n```sh\npkgroll --sourcemap=inline\n```\n\n#### Declaration source maps\n\nFor `.d.ts` files specifically, you can also enable source maps via [`declarationMap`](https://www.typescriptlang.org/tsconfig/#declarationMap) in `tsconfig.json`:\n\n```json\n{\n    \"compilerOptions\": {\n        \"declarationMap\": true\n    }\n}\n```\n\nThis enables \"Go to Definition\" in IDEs to navigate directly to the original `.ts` source files instead of the bundled `.d.ts` files.\n\nUsing `declarationMap` only generates `.d.ts.map` files (not `.js.map`), which is useful if you only need source maps for type definitions.\n\n## Dev vs Prod config\n\nIn some cases, it makes sense to use different `package.json` field values for the published environment. You can achieve this by using the [`publishConfig`](https://pnpm.io/package_json#publishconfig) field (extended by pnpm). This allows you to override specific fields during publication with a clean separation of concerns.\n\nThe following fields can be overridden using `publishConfig`:\n- `bin`\n- `main`\n- `exports`\n- `types`\n- `module`\n\n## FAQ\n\n### Why bundle with Rollup?\n[Rollup](https://rollupjs.org/) has the best tree-shaking performance, outputs simpler  code, and produces seamless CommonJS and ESM formats (minimal interop code). Notably, CJS outputs generated by Rollup supports named exports so it can be parsed by Node.js ESM. TypeScript \u0026 minification transformations are handled by [esbuild](https://esbuild.github.io/) for speed.\n\n### Why bundle Node.js packages?\n\n- **ESM and CommonJS outputs**\n\n\tAs the Node.js ecosystem migrates to [ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c), there will be both ESM and CommonJS users. A bundler helps accommodate both distribution types.\n\n- **Dependency bundling** yields smaller and faster installation.\n\n\tTree-shaking only pulls in used code from dependencies, preventing unused code and unnecessary files (eg. `README.md`, `package.json`, etc.) from getting downloaded.\n\n\tRemoving dependencies also eliminates dependency tree traversal, which is [one of the biggest bottlenecks](https://dev.to/atian25/in-depth-of-tnpm-rapid-mode-how-could-we-fast-10s-than-pnpm-3bpp#:~:text=The%20first%20optimization%20comes%20from%20%27dependencies%20graph%27%3A).\n\n- **Inadvertent breaking changes**\n\n\tDependencies can introduce breaking changes due to a discrepancy in environment support criteria, by accident, or in rare circumstances, [maliciously](https://snyk.io/blog/peacenotwar-malicious-npm-node-ipc-package-vulnerability/).\n\n\tCompiling dependencies will make sure new syntax \u0026 features are downgraded to support the same environments. And also prevent any unexpected changes from sneaking in during installation.\n\n\n- **Type dependencies** must be declared in the `dependencies` object in `package.json`, instead of `devDependencies`, to be resolved by the consumer.\n\n\tThis may seem counterintuitive because types are a development enhancement. By bundling them in with your package, you remove the need for an external type dependency. Additionally, bundling only keeps the types that are actually used which helps minimize unnecessary bloat.\n\n- **Minification** strips dead-code, comments, white-space, and shortens variable names.\n\n### How does it compare to tsup?\n\nThey are similar bundlers, but I think the main differences are:\n\n- _pkgroll_ is zero-config. It reads the entry-points declared in your `package.json` to determine how to bundle the package. _tsup_ requires manual configuration.\n\n- _pkgroll_ is a thin abstraction over Rollup (just smartly configures it). Similarly, _tsup_ is a thin abstraction over esbuild. _pkgroll_ also uses esbuild for transformations \u0026 minification as a Rollup plugin, but the bundling \u0026 tree-shaking is done by Rollup (which is [known to output best/cleanest code](#why-bundle-with-rollup)). However, when _tsup_ emits type declaration, it also uses Rollup which negates the performance benefits from using esbuild.\n\n- IIRC because _tsup_ uses esbuild, the ESM to CJS compilation wasn't great compared to Rollups. As a package maintainer who wants to support both ESM and CJS exports, this was one of the biggest limitations of using _tsup_ (which compelled me to develop _pkgroll_).\n\n## Sponsors\n\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://github.com/sponsors/privatenumber\"\u003e\n\t\t\u003cimg src=\"https://cdn.jsdelivr.net/gh/privatenumber/sponsors/sponsorkit/sponsors.svg\"\u003e\n\t\u003c/a\u003e\n\u003c/p\u003e\n","funding_links":["https://github.com/sponsors/privatenumber","https://github.com/sponsors/privatenumber/sponsorships?tier_id=398771","https://github.com/sponsors/privatenumber/sponsorships?tier_id=397608"],"categories":["TypeScript","typescript","zero-config"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprivatenumber%2Fpkgroll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprivatenumber%2Fpkgroll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprivatenumber%2Fpkgroll/lists"}