Skip to content

feat: support multiple bin entries from package.json #255

@robertsLando

Description

@robertsLando

When package.json declares bin as a string, pkg builds a single executable. When it's an object ({ "foo": "./foo.js", "bar": "./bar.js" }), pkg silently picks only one entry — the one matching the package name, falling back to the first key:

// lib/index.ts
if (bin[inputJsonName]) {
  bin = bin[inputJsonName];
} else {
  bin = bin[Object.keys(bin)[0]]; // TODO multiple inputs to pkg them all?
}

The TODO has been in the code for years. Packages with multiple CLIs (e.g. a server + a migration tool) currently need multiple invocations with --config overrides, which defeats zero-config auto-discovery (#238).

Proposal

When bin is an object and the user hasn't narrowed it via CLI, build one executable per entry. The binary name defaults to the object key; the output path defaults to outputPath/<key> (or the current --output template, with {name} substituted per entry).

{
  "name": "my-tool",
  "bin": {
    "my-tool":       "./cli.js",
    "my-tool-admin": "./admin.js"
  },
  "pkg": { "targets": ["node22-linux-x64"], "outputPath": "dist" }
}

dist/my-tool-linux, dist/my-tool-admin-linux.

Selecting a subset

Keep the single-binary path working for users who want one entry:

  • pkg . with object-form bin → all entries (new default)
  • pkg . --bin my-tool-admin → only that one
  • A top-level bin string on the CLI still refers to one entry

Legacy behavior (picking the first/name-matching entry) becomes opt-in via explicit --bin <key>.

Scope

  • Extend config resolution in lib/index.ts to accept an array of (name, entryPath) pairs instead of a single inputBin
  • Loop the existing single-binary build pipeline per entry; share the walker/bytecode cache across entries where paths overlap to avoid re-walking node_modules N times
  • Clarify output naming: {name} in --output templates should expand to the bin key, not the package name
  • Interaction with .pkgrc (feat: support a .pkgrc configuration file #238): a pkgrc may override bin entirely (string, array, or object); bin still comes from package.json when the pkgrc doesn't declare one
  • Document migration for users who relied on the old "first key wins" behavior

Out of scope

Part of #235

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions