chore(packaging): every published package declares a files whitelist (#4248) - #4262
Conversation
#4248) 20 of the 49 publishable packages declared no `files` field, so npm fell back to packing the whole package directory. `npm pack --dry-run` on @objectstack/plugin-webhooks listed 21 files -- 15 under src/, three of them unit tests, plus the build-time scripts/i18n-extract.config.ts. dist/ lands on top of that at publish time rather than instead of it, so consumers were installing TypeScript sources and the test suite alongside the artifact. Each now declares ["dist", "README.md"], matching the 29 packages that already did. Nothing a consumer imports moves: every main / types / exports target in all 20 already resolved inside dist/. plugin-webhooks packs 2 files + dist/. The other half is the gate. Half the packages declaring `files` and half not was the #3786 shape -- a hand-copied convention with nothing enforcing it, where whoever forgets the line gets no signal at all. check:published-files (wired into the always-required lint job) holds every non-private workspace package to four invariants: DECLARED `files` exists and is a non-empty array of strings. SUFFICIENT it covers every entry point, so tightening a whitelist cannot ship a package that fails to resolve -- the opposite failure, and one this guard would otherwise encourage. MINIMAL nothing it admits is a test, a test-harness config or a build script. REGISTERED anything beyond dist + README.md carries a reason, reconciled in BOTH directions so a stale exemption is an error, not dead text. @objectstack/spec is the one package with registered extras: its .zod.ts sources, JSON Schemas, liveness ledgers and CHANGELOG.md are product rather than build input. MINIMAL also closes an assumption #4206 rested on. Excluding <pkg>/scripts/** from the docs-drift implementation test is sound only while no package publishes scripts/ as runtime code; that held, but it held because someone read all three offenders by hand. It is now checked on every PR. The guard runs its own --self-test first: the pattern semantics are the one part that can be wrong while every package is right, and both failure directions are silent -- an over-matching matcher turns MINIMAL into noise, an under-matching one waves a broken package through SUFFICIENT. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0132i4uupnWgjajiL8KDqzR1
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 20 package(s): 23 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Closes #4248.
问题
那 20 个包没有
files,npm 就按默认规则打包整个目录。实测@objectstack/plugin-webhooks:21 个文件,其中 15 个在src/下、3 个是单元测试,外加构建期的scripts/i18n-extract.config.ts。发布时dist/是叠在这些之上,不是取代它们 —— 消费者装到的是「制品 + 源码 + 测试套件」。改了什么
1. 补齐 20 个包的
files,统一为["dist", "README.md"],和已经声明的 29 个对齐。消费者 import 的东西一个都没挪:这 20 个包的
main/types/exports目标本来就全在dist/里面 —— 这一点不是靠人眼确认的,是下面那道闸门的 SUFFICIENT 断言验出来的。构建后实测plugin-webhooks:dist/会再叠上去)dist/**+LICENSE+package.json2. 新增
check:published-files闸门,挂在始终必跑的lintjob 上。issue 里说得对:补完这一轮不是治本 —— 一半包声明一半不声明,是 #3786 那类「手抄清单没有闸门」的形状,漏掉的人拿不到任何信号。闸门对每个非 private workspace 包断言四条:
files存在,且是非空字符串数组。scripts/。dist+README.md的条目必须在EXTRA_ENTRIES里写明理由,并且双向核对 —— 表里有、files里没有的条目同样报错,免得留下一段没人执行却仍然像政策的死文本。@objectstack/spec是唯一有登记豁免的包(.zod.ts源码、JSON Schema、liveness 账本、CHANGELOG.md—— 这些是产品本身,不是构建输入)。3. MINIMAL 顺带补上了 #4206 依赖的那个假设。
把
<包根>/scripts/**排除出 docs-drift 的实现改动判定,只有在「没有包把scripts/当运行时代码发布」时才成立。这个前提是成立的 —— 但它成立是因为有人逐个人工核对过三个包的内容,没有任何机制在守着它。现在每个 PR 都会验。验证
pnpm check:published-files—— 改前精确复现 issue 的 20 个包;改后 69 个可发布包全绿。--self-test先跑(29 个断言):模式语义是这道闸门里唯一「所有包都对、它自己却可能错」的部分,而且两个方向都是静默失败 —— 匹配过宽会把 MINIMAL 变成噪音,过窄会让 SUFFICIENT 放行一个坏包。files」「整个发布src/和scripts/」「入口指向白名单外」「spec 增删登记条目」「负向!模式」,五种都如期报错并给出可执行的修法。pnpm lint通过;check:nul-bytes、check:changeset-fixed、check:changeset-no-major通过。npm pack --dry-run验证 tarball 内容(上表)。顺带发现,已单独开 issue
#4261:
files: ["dist", "README.md"]不含CHANGELOG.md,npm 也不无条件打包它(实测@objectstack/types:70KB 的 CHANGELOG 不在 tarball 里)。而AGENTS.md写着 breaking changeset 的迁移说明「ships to consumers asCHANGELOG.mdinside the npm package」,升级者会去 grep 它 —— 69 个包里只有 spec 显式列了它,其余 68 个这条送达路径是断的。这不是本 PR 造成的回归(那 49 个早就声明
files的包一直如此),但它是一个发布产物范围的决定,不该顺手塞进这里,所以单独开。本 PR 的 canonical set 先按 issue 里写的["dist", "README.md"]落地。🤖 Generated with Claude Code
https://claude.ai/code/session_0132i4uupnWgjajiL8KDqzR1
Generated by Claude Code