fix(publish): apply publishConfig manifest overrides under npm publish#5338
fix(publish): apply publishConfig manifest overrides under npm publish#5338NathanFlurry wants to merge 1 commit into
Conversation
…errides under npm publish
PR ReviewThis PR fixes a real publish bug: Three findings worth addressing:
The repo's allowlist omits const PUBLISH_CONFIG_MANIFEST_FIELDS = [
"bin", "main", "module", "exports", "types", "typings",
- "browser", "esnext", "es2015", "unpkg", "umd:main", "typesVersions",
+ "browser", "esnext", "es2015", "unpkg", "umd:main", "typesVersions",
+ "type", "engines", "imports", "os", "cpu", "libc",
] as const;
Adding the index signature to the interface is necessary for (pkgJson as Record<string, unknown>)[field] = publishConfig[field];This leaves the
The rest of the dry-run path logs The core logic is correct — the |
|
🚅 Deployed to the rivet-pr-5338 environment in rivet-frontend
|
Problem
Every published version of
@rivetkit/effecthas its top-levelexports["."]pointing at./src/mod.ts(raw TS source) instead of./dist. The package'spackage.jsonintends the dist path viapublishConfig.exports, but that override never lands in the published tarball.Root cause:
publishConfigmanifest overrides likeexportsare effectively a pnpm feature. pnpm rewrites the manifest when it packs the tarball; plainnpm publishignores them. Our CI publishes withnpm publish(scripts/publish/src/lib/npm.tsspawnsnpm publishper package), so the devsrcentry ships instead of thedistswap. All published versions of@rivetkit/effectconfirm this (exports["."] = "./src/mod.ts").Impact
Runtime in Bun/Deno/tsx is fine (they execute
.tsdirectly) and bundlers don't deep-type-check deps, so most consumers never see it. It breaks consumers running a strict whole-programtsc --noEmit: undermoduleResolution: bundlerthe entry resolves to oursrc/*.ts, which then gets compiled under the consumer's tsconfig and errors.skipLibCheckdoesn't help (it skips.d.ts, not.tsreached viaexports), and the shippeddist/*.d.tsis never used.Thanks to the external consumer who reported this (their workaround was a
bun patchrewritingexportsto the dist conditional).Fix
Make the
publishConfig→ dist swap actually apply at publish time, centrally, so it works undernpm publishand covers any future@rivetkit/*package using the same pattern.scripts/publish/src/lib/version.ts—bumpPackageJsons(the publish-time, non-versionOnlyrewrite that CI runs on disk immediately beforenpm publish) now folds manifest-shapepublishConfigfields (exports,main,types,bin, ...) into the top-level manifest, mirroring pnpm's behavior. npm-native publish controls (access,registry,tag,provenance, ...) are intentionally left inpublishConfigso npm still reads them.rivetkit-typescript/packages/effect/package.json— makepublishConfig.exportsan explicit conditional with atypescondition ({ "types": "./dist/mod.d.ts", "default": "./dist/mod.js" }) so strict-tsc consumers resolve the shipped declarations.This preserves the effect package's deliberate
.ts-native dev experience (sourceexportsin the working tree,rewriteRelativeImportExtensions,@effect/language-service) while ensuring the published tarball ships the dist entry.Why not
pnpm publishor a per-packageprepack?Switching the whole publish path to
pnpm publishis a broad, riskier infra change (different workspace-dep handling). Aprepackscript would have to be added per package and mutatepackage.jsonon disk with a matchingpostpackrestore. The publish system already rewrites every manifest on disk at publish time (non-committed), so foldingpublishConfigthere is the smallest, most robust hook and generalizes to all packages.Verification
Replicated the fold against the real effect manifest. Published
exportsbecomes{ ".": { "types": "./dist/mod.d.ts", "default": "./dist/mod.js" } }andpublishConfigcorrectly retains only{ "access": "public" }. The full-mode bump runs after the TS build step inpublish.yaml, sodist/mod.js/dist/mod.d.tsexist when the manifest is finalized.