mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-07-30 19:36:01 +00:00
fix: guard publish against a half-built dist
The build is `tsup && tsc`. tsup runs with clean:true, so it wipes dist and writes JavaScript; if tsc then fails, dist holds .js with no declarations. The build exits non-zero, but packages/core declared no prepublishOnly, so nothing rebuilt at publish time and a later npm publish would ship it. Reproduced: remove the declarations from a copy of dist and npm pack --dry-run still succeeds, with all 41 exports subpaths pointing at files absent from the tarball. npm pack was never the guard. Adds prepublishOnly (build then verify) and scripts/verify-dist.mjs, which asserts every exports target exists. CI runs verify-dist as well, so the guard is exercised on every push rather than only on the rare publish.
This commit is contained in:
parent
6f1f8a4462
commit
0ec9ea93cb
3 changed files with 60 additions and 1 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
|
@ -35,6 +35,9 @@ jobs:
|
|||
- name: Build core
|
||||
run: npm run build --workspace=@codeburn/core
|
||||
|
||||
- name: Verify every export target exists
|
||||
run: npm run verify-dist --workspace=@codeburn/core
|
||||
|
||||
- name: Verify package contents
|
||||
run: npm pack --workspace=@codeburn/core --dry-run
|
||||
|
||||
|
|
|
|||
|
|
@ -181,7 +181,9 @@
|
|||
"build": "tsup && tsc -p tsconfig.build.json",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"emit-schemas": "tsx scripts/emit-schemas.mts"
|
||||
"emit-schemas": "tsx scripts/emit-schemas.mts",
|
||||
"verify-dist": "node scripts/verify-dist.mjs",
|
||||
"prepublishOnly": "npm run build && npm run verify-dist"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.13.0"
|
||||
|
|
|
|||
54
packages/core/scripts/verify-dist.mjs
Normal file
54
packages/core/scripts/verify-dist.mjs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env node
|
||||
// Refuses to let a half-built dist reach npm.
|
||||
//
|
||||
// `npm run build` is `tsup && tsc`. tsup runs with clean:true, so it wipes dist
|
||||
// and writes JavaScript; if tsc then fails, dist holds .js with no declarations.
|
||||
// The build exits non-zero, but nothing rebuilds at publish time, so a later
|
||||
// `npm publish` would ship a package whose every `types` target 404s. Verified:
|
||||
// removing the declarations and running `npm pack --dry-run` succeeds with
|
||||
// 41/41 exports subpaths pointing at files that are not in the tarball.
|
||||
//
|
||||
// This script is the assertion that closes that gap. It runs from
|
||||
// `prepublishOnly` (after the build) and in CI, so it is exercised on every
|
||||
// push rather than only on the rare publish.
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname, join } from 'node:path'
|
||||
|
||||
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const pkg = JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8'))
|
||||
|
||||
const problems = []
|
||||
let checked = 0
|
||||
|
||||
for (const [subpath, entry] of Object.entries(pkg.exports ?? {})) {
|
||||
for (const condition of ['import', 'types']) {
|
||||
const relative = entry?.[condition]
|
||||
if (!relative) {
|
||||
problems.push(`exports["${subpath}"] declares no "${condition}" target`)
|
||||
continue
|
||||
}
|
||||
checked++
|
||||
if (!existsSync(join(pkgRoot, relative))) {
|
||||
problems.push(`exports["${subpath}"].${condition} -> ${relative} does not exist`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `files` decides what actually ships; a target outside it is unreachable to a
|
||||
// consumer even when it exists on disk here.
|
||||
const shipped = pkg.files ?? []
|
||||
if (!shipped.includes('dist')) {
|
||||
problems.push('package.json#files does not include "dist", so no export target would ship')
|
||||
}
|
||||
|
||||
if (problems.length > 0) {
|
||||
console.error(`dist verification failed (${problems.length} problem(s)):`)
|
||||
for (const problem of problems) console.error(` - ${problem}`)
|
||||
console.error('\nRun `npm run build --workspace=@codeburn/core` and re-check.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(
|
||||
`dist verified: ${checked} export targets across ${Object.keys(pkg.exports ?? {}).length} subpaths all present`,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue