The real tarball
runs npm pack --json (with --ignore-scripts unless you pass --run-scripts), or checks an existing .tgz with --tarball. A built-in tar reader means nothing extra to install.
GitHub Packages · MIT · Node ≥ 18
Check what npm publish will actually ship, before you publish it.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/publish-gaugePublished to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.
$ npx publish-gaugepublish-gauge — my-lib@1.0.0npm pack: 9 files, 665 B packed, 676 B unpacked CRITICAL .env .env would be published [secret-file] ↳ fix: Remove it from the package (add it to .npmignore or narrow "files"), and rotate anything it contained if it was ever published.CRITICAL package.json:4 main → "dist/index.js" is not in the tarball [entry-missing] ↳ fix: Ship dist/index.js (check "files" and .npmignore). Build before packing, or add its folder to "files".HIGH bin/cli.js:1 bin/cli.js has no "#!/usr/bin/env node" line, so it will not run as a command on macOS/Linux [bin-shebang] ↳ fix: Add "#!/usr/bin/env node" as the first line.HIGH lib/load.js require("lib/load.js") failed: Error: Cannot find module './not-shipped-helper.js' [entry-load-failed] ↳ fix: A file it imports is not shipped; check "files" / .npmignore.HIGH package.json:5 types → "dist/index.d.ts" is not in the tarball [types-missing] ↳ fix: Ship dist/index.d.ts (check "files" and .npmignore). Build before packing, or add its folder to "files".... 2 critical, 5 high, 4 medium, 3 low✔ Passing (fail-on: none).
Most broken npm releases are caused by packaging, not code. main points at a dist/ folder that was never built. exports names a file that files leaves out. The .d.ts imports a sibling declaration that was not shipped, so users silently get any. The bin has no shebang. A .env file or an .npmrc with a token goes out to the whole world. publish-gauge runs “npm pack”, opens the real tarball, and checks it the way your users will consume it: every entry point must exist and load, types must resolve, and nothing that should stay private is included. Each problem comes with a file, a severity and a concrete fix.
CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. By Louis Wright.
runs npm pack --json (with --ignore-scripts unless you pass --run-scripts), or checks an existing .tgz with --tarball. A built-in tar reader means nothing extra to install.
main (with Node's .js/.json/index.js fallbacks), module, bin, types/typings, and every exports target, including nested conditions, subpaths and * patterns.
each Node-facing entry is required or imported from a sandbox copy of the tarball, using your installed node_modules. Missing shipped files and undeclared runtime dependencies are reported as errors. Declared dependencies that simply aren't installed are reported as info.
relative imports, import() types and /// <reference path> in every shipped .d.ts must point at shipped files. types must be listed before import/require/default in exports.
.env* (but not .env.example), private keys and certificates, .npmrc (flagged as containing a token when it has one), .netrc and credential files. Junk such as tests, fixtures, CI config, coverage, logs, editor files and tool configs is grouped by folder.
packed size, unpacked size and per-file size.
valid name and version, "private": true, file:/link:/workspace: dependency specs, bin shebangs and CRLF line endings, .mjs/.cjs targets under the wrong condition, source maps pointing at unshipped sources, and missing README/LICENSE.
text, JSON, SARIF 2.1.0 and Markdown; --fail-on; exit codes 0/1/2; allowlist entries with expiry dates.
# in the package directory
npx publish-gauge# let prepack/prepare build dist/ first
npx publish-gauge --run-scripts# Example
npx publish-gauge --max-size 200kb --fail-on medium# Example
npx publish-gauge --tarball my-lib-1.2.3.tgzpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
publish-gauge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- uses: MrLouisWright/publish-gauge@v1
with:
fail-on: high
sarif: publish-gauge.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: publish-gauge.sarif }
| Flag | Description |
|---|---|
| Checks | |
--tarball <file.tgz> | Check an existing tarball instead of running npm pack |
--run-scripts | Run prepack/prepare scripts while packing (default: --ignore-scripts) |
--max-size <size> | Packed size budget, e.g. 500kb, 2mb |
--max-unpacked-size <s> | Unpacked size budget |
--max-file-size <size> | Per-file budget (default 1mb) |
--no-load | Do not load-test entry points |
--allow-private | Do not report "private": true |
--ignore-files <globs> | Files to skip in content checks (comma-separated) |
| Common | |
-c, --config <file> | JSON config (default: .publish-gauge.json or "publishGauge" in package.json) |
-f, --format <fmt> | text (default) | json | sarif | markdown |
--json <file> | Also write a JSON report |
--sarif <file> | Also write SARIF 2.1.0 |
--markdown <file> | Also write Markdown (e.g. for a job summary) |
--fail-on <sev> | critical | high | medium | low | info | none (default: high) |
--list-rules | Print rules and default severities |
--no-fixes | Hide fix suggestions in text output |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.
Buy me a coffee (opens Stripe in a new tab)Secure checkout by Stripe