Skip to content

Fix broken "types" entries across the three build-info packages - #565

Merged
iainmerrick merged 2 commits into
mainfrom
claude/build-info-types-mismatch-d9pwhg
Aug 20, 2026
Merged

iainmerrick merged 2 commits into
mainfrom
claude/build-info-types-mismatch-d9pwhg

Conversation

@more-please-oliver

@more-please-oliver more-please-oliver commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

All three build-info packages ship a "types" entry that fails for consumers. Each one is broken differently; none of them can be fixed by republishing alone.

Verified empirically throughout: packing each tarball, installing it into a scratch project, and typechecking real imports under both moduleResolution: "bundler" and "nodenext".

1. @moreplease/build-info"types" points at a file that doesn't exist

"types" was build/index.d.ts, but tsdown builds the build-info.ts entry into build/build-info.mjs + build/build-info.d.mts. The published 0.0.1 tarball contains exactly:

package/build/build-info.mjs
package/build/build-info.d.mts   <- the real declarations

So consumers get no types and have to work around it:

// The published @moreplease/build-info package's "types" field points at a
// file it doesn't ship (build/index.d.ts vs the actual build-info.d.mts),
// so declare the module locally.
declare module "@moreplease/build-info" { ... }

The stale path looks copied from the two plugin packages, where build/index.d.ts is a hand-written, git-tracked type entry (which is also why they set "clean": false). This package has no such file.

Fixed: "types"build/build-info.d.mts, pairing with the already-correct "main".

Also annotated the exported constants explicitly. The published build-info.d.mts declares branch and commit as any because the dts pass resolved without node types; annotating makes the emitted declarations deterministic regardless of publish environment, and matches what the workaround above assumed (string).

2. @moreplease/esbuild-plugin-build-info — declarations don't compile, and are incomplete

export { BuildInfo } from "./build-info";

build-info.d.ts is the ambient declaration for the virtual "build-info" module — a global script, not a module. So this errors for every consumer: TS2306 (not a module) under bundler, TS2834 (needs a file extension) under nodenext.

Because that file was imported rather than referenced, its declare module never entered the program either. Consumers importing the module the plugin injects got:

error TS2307: Cannot find module 'build-info' or its corresponding type declarations.

The declarations were also missing branch, commit and buildInfo, which the plugin has always exported at runtime — importing any of them was a TS2724/TS2614 error.

Fixed: replaced with a triple-slash reference directive plus the full export surface, mirroring esbuild-plugin-build-info.ts and matching what the rollup plugin already exposes:

/// <reference path="build-info.d.ts" />
import type { Plugin } from "esbuild";

export interface BuildInfo {
  timestamp: Date;
  branch: string;
  commit: string;
}

export function branch(): Promise<BuildInfo["branch"]>;
export function commit(): Promise<BuildInfo["commit"]>;
export function buildInfo(): Promise<BuildInfo>;

export default function buildInfoPlugin(): Plugin;

Bonus: the esbuild peer range excludes every current esbuild

"peerDependencies": { "esbuild": "^0.20.0" }

esbuild is 0.x, so a caret pins the minor — ^0.20.0 means >=0.20.0 <0.21.0. That excludes everything from 0.21 up, including the ^0.28.2 this package develops against. Installing the plugin next to a current esbuild fails outright:

npm error Could not resolve dependency:
npm error peer esbuild@"^0.20.0" from @moreplease/esbuild-plugin-build-info@0.0.3

Fixed: widened to >=0.20.0. The install now resolves without --legacy-peer-deps.

3. @moreplease/rollup-plugin-build-info — declaration entry imports a declaration file

export { ... } from "./rollup-plugin-build-info.d.mts";

Importing a .d.mts directly is TS2846 (A declaration file cannot be imported without 'import type'), under both resolution modes. The specifier has to name the implementation.

Fixed: point it at ./rollup-plugin-build-info.mjs and let TypeScript find the sibling .d.mts.

Verification

Consumer typecheck matrix, against packed tarballs — default export, every named export, and the injected virtual module:

package bundler nodenext
esbuild-plugin-build-info pass pass
rollup-plugin-build-info pass pass
build-info pass pass

All four plugin combinations failed before this change. pnpm test (tsc) passes in all three packages.

Worth noting the pattern behind all three bugs: pnpm test is tsc over each package's own sources, so it never typechecks the build/*.d.ts files consumers actually receive. A small smoke-test consumer package in CI would have caught every one of these. Happy to add one as a follow-up if that seems worthwhile.

Versions

Bumped build-info 0.0.1 → 0.0.2, esbuild-plugin-build-info 0.0.3 → 0.0.4, rollup-plugin-build-info 0.0.13 → 0.0.14, since these fixes only reach consumers via a republish.

Notes, not addressed here

  • The build-info README documents a default export and a BuildInfo type (import buildInfo from "build-info"), which the plugins' virtual modules provide but the runtime placeholder does not — it only has the three named exports. That's a public-API change rather than a packaging fix.
  • rollup-plugin-build-info/build/rollup-plugin-build-info.{mjs,d.mts} are git-tracked even though .gitignore lists build and tsdown regenerates them on prepare. Only the two hand-written .d.ts files need tracking. I checked and they're not currently stale, so this is cosmetic — left alone to keep the diff focused.

"types" was "build/index.d.ts", a file this package never produces and
never shipped — tsdown emits "build/build-info.d.mts" from the
"build-info.ts" entry. The published 0.0.1 tarball contains only
build-info.mjs and build-info.d.mts, so consumers got no types at all and
had to "declare module" locally.

The stale path was copied from the two plugin packages, where
build/index.d.ts is a hand-written, git-tracked type entry (which is also
why they set "clean": false). This package has no such file, so point
"types" at the emitted declaration instead.

Also annotate the exported constants explicitly. The published
build-info.d.mts declares "branch" and "commit" as "any" because the dts
pass resolved without node types; annotating makes the emitted types
deterministic regardless of the publish environment.

Verified by packing the tarball and typechecking a consumer that imports
branch/commit/timestamp under moduleResolution: nodenext, with no
declare-module shim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLKGKWWvgHcF8JR64yZj6P
Both plugins ship a hand-written build/index.d.ts as their "types" entry,
and both fail to typecheck in a consumer under either moduleResolution:
"bundler" or "nodenext".

esbuild-plugin-build-info:

  export { BuildInfo } from "./build-info";

build-info.d.ts is the ambient declaration for the virtual "build-info"
module — a global script, not a module — so this errors with TS2306
("not a module") under bundler and TS2834 (needs a file extension) under
nodenext. Because the file was imported rather than referenced, its
"declare module" never entered the program either, so consumers importing
the injected virtual module got TS2307 "Cannot find module 'build-info'".
The declarations were also missing branch, commit and buildInfo, which
the plugin has always exported at runtime.

Replaced with a /// <reference path> plus the full export surface,
mirroring esbuild-plugin-build-info.ts and matching what the rollup
plugin already exposes.

rollup-plugin-build-info:

  export { ... } from "./rollup-plugin-build-info.d.mts";

Importing a declaration file directly is TS2846; the specifier has to
name the implementation, so point it at ./rollup-plugin-build-info.mjs
and let TypeScript find the sibling .d.mts.

Also widen the esbuild peer range from "^0.20.0" to ">=0.20.0". esbuild
is 0.x, so a caret pins the minor: "^0.20.0" excludes everything from
0.21 up, including the 0.28.2 this package develops against. Installing
it alongside a current esbuild fails ERESOLVE on npm.

Verified by packing both tarballs and typechecking consumers that import
the default export, every named export, and the injected virtual module,
under both bundler and nodenext resolution — all four combinations pass,
and the install now resolves without --legacy-peer-deps.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLKGKWWvgHcF8JR64yZj6P
@more-please-oliver more-please-oliver changed the title build-info: point "types" at the file tsdown actually emits Fix broken "types" entries across the three build-info packages Aug 20, 2026
@iainmerrick
iainmerrick merged commit 633dd7e into main Aug 20, 2026
4 checks passed
@iainmerrick
iainmerrick deleted the claude/build-info-types-mismatch-d9pwhg branch August 20, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants