A missing prepublishOnly script means a developer can run npm publish against stale or missing dist/ output — shipping a broken release to every consumer before anyone notices. ISO 25010 fault-tolerance requires that the release process itself catch this class of error. SLSA L1 requires that the build is reproducible and that the published artifact reflects the declared source version. A manual build-then-publish workflow is an invitation for version skew between source and registry.
Medium because the gap between build and publish is a procedural failure mode that produces broken releases — not a security issue, but a reliability one that affects all consumers of the broken version.
Add a prepublishOnly script that guarantees a fresh build runs before every npm publish.
{
"scripts": {
"build": "tsup",
"prepublishOnly": "npm run build"
}
}
prepublishOnly runs automatically before npm publish (not before npm install). For packages that need CI-only publishing, also add a prepack hook — it runs before both npm pack and npm publish. Confirm the build output directory in scripts.build matches the paths in main, module, and exports.
ID: sdk-package-quality.build-distribution.build-script
Severity: medium
What to look for: List all scripts in package.json. For each build-related script, check package.json scripts for a build script (or equivalent):
build script exists and invokes the detected build toolprepublishOnly script that runs the build before publishingdist/, lib/, build/) exists or would be created by the build scriptmain/module/exports paths reference the build output directory
For Python: check for a build command in pyproject.toml or Makefile. For Rust: cargo build is implicit.Pass criteria: A build script exists in package.json that invokes the build tool, AND a prepublishOnly script exists that ensures the build runs before npm publish. The output directory referenced by main/exports would be produced by the build — at least 1 build script must be configured (build, prepublishOnly, or equivalent). Report: "X build scripts configured."
Fail criteria: No build script, OR no prepublishOnly script (risk of publishing stale/missing build output), OR the build script exists but its output directory doesn't match what main/exports reference.
Skip (N/A) when: The package publishes source directly (no build step) — e.g., a pure JavaScript package with no transpilation. Also skip for Go (no build artifact for libraries) and Python packages that use only pyproject.toml with a standard build backend.
Detail on fail: "Build script runs 'tsup' but there is no prepublishOnly script. If a developer runs 'npm publish' without building first, stale or missing dist/ files will be published."
Remediation: Always ensure the build runs before publishing. The prepublishOnly script runs automatically before npm publish:
{
"scripts": {
"build": "tsup",
"prepublishOnly": "npm run build"
}
}
Some teams also add a prepack script (runs before npm pack, which npm publish calls internally). Either works.