Local package development
Design package scripts
Give common development, test, build, lint, and release checks stable commands.
Scripts in package.json are the command vocabulary for humans and CI. If the release steps live only in your head, they will skip a step on a tired Friday.
For @acme/slugify-title, I keep a small set with stable names:
{
"scripts": {
"test": "node --test",
"build": "tsup",
"check": "npm run test && npm run build",
"prepack": "npm run check"
}
}
npm test runs the test suite. npm run build writes fresh files into dist/. npm run check chains both so CI has one entry point.
Lifecycle hooks matter
prepack runs automatically before npm pack and npm publish. Hooking check there stops you from publishing when tests fail or dist/ is stale.
You can add prepublishOnly for stricter release gates later. It runs only on publish, not on every local pack.
Prove the scripts from a clean checkout
Delete artifacts and reinstall:
rm -rf dist node_modules
npm ci
npm run check
You should see tests pass and dist/index.js appear. If check passes but dist/ is empty, your build script is a no-op or writing elsewhere.
Keep scripts portable
Avoid macOS-only shell tricks in shared scripts unless you guard them. Prefer Node tooling or cross-platform flags.
Document the order a new maintainer should run commands: clone, npm ci, npm test, npm run build. That sequence should match what CI runs on every push.
My rule: if a step is required before publish, it belongs in a script or lifecycle hook, not in a wiki page nobody reads.
Add a lint script early even if it only runs node --check on a few files today. CI can call npm run lint && npm run check so syntax errors fail before tests spend time running.
Lesson completed