🚚 Rename publint script to publish:publint and document prefix convention
publint validates the publishable artifact (dist/ vs package.json), not the source. It should run only at publish time, in the CI publish job, not on every commit. - Rename check:publint -> publish:publint - Remove from 'check' chain - Add 'publish:publint' as a step in the CI publish job, right before 'npm publish' - Introduce a 'publish:' script prefix for publish-time-only scripts - Document the prefix convention (check:, fix:, test:, publish:) in project-specs.md (as a top-level subsection under Scripts) and in the README - Add 'publint' and 'stricter' to cspell word list A new script should pick the prefix that matches its lifecycle, not invent a new one. The 'publish:' prefix has no 'npm run publish' aggregator by design (publishing is CI-only).
This commit is contained in:
1 parent
24a3880aad
commit
01d1084e1f
6 files changed
+154
-8
No files matched your search
@@ -38,6 +38,7 @@ jobs:
|
||||
registry-url: "https://registry.npmjs.org/"
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- run: npm run publish:publint
|
||||
- run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -17,6 +17,7 @@ Pattern matching for TypeScript/ESM environments (F#-style, not regex).
|
||||
- **oxlint** — Rust-based linter.
|
||||
- **oxfmt** — Rust-based formatter (Prettier-compatible). Formats JS/TS, JSON/JSONC, YAML, Markdown, MDX, and more; built-in `package.json` key sorting replaces `sort-package-json`.
|
||||
- **cspell** — spell checking.
|
||||
- **publint** — validates `package.json` for ESM publishing correctness. Runs on publish only (in CI), not as part of `npm run check`.
|
||||
- **lefthook** — git pre-commit hooks.
|
||||
|
||||
### Requirements
|
||||
@@ -32,7 +33,16 @@ Pattern matching for TypeScript/ESM environments (F#-style, not regex).
|
||||
## Workflows
|
||||
|
||||
- Version updates via `npm version`.
|
||||
- Publishing via GitHub Actions on tagged commits (see `.github/workflows/ci.yml`).
|
||||
- Publishing via GitHub Actions on tagged commits (see `.github/workflows/ci.yml`); the `publish` job runs `publish:publint` before `npm publish`.
|
||||
|
||||
## Script prefix convention
|
||||
|
||||
Script names follow a prefix convention that signals _when_ they run:
|
||||
|
||||
- `check:*` — read-only verification. Aggregated by `npm run check`. Used in pre-commit hooks and CI's build job.
|
||||
- `fix:*` — mutating counterpart of `check:*`. Run individually (no `npm run fix` aggregator by design — fixes should be intentional, not batched).
|
||||
- `test:*` — test scripts. `npm run test` runs the full suite; `test:unit` / `test:ci` are scope-specific variants.
|
||||
- `publish:*` — runs only at publish time, in the CI `publish` job (immediately before `npm publish`). There is no local `npm run publish` script — publishing is CI-only by policy.
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
|
||||
+3
-1
@@ -15,7 +15,9 @@
|
||||
"typescriptteam",
|
||||
"gitmoji",
|
||||
"dbaeumer",
|
||||
"msvc"
|
||||
"msvc",
|
||||
"publint",
|
||||
"stricter"
|
||||
],
|
||||
"ignorePaths": ["dist", "node_modules", "coverage", "*.svg", ".gitignore"]
|
||||
}
|
||||
Generated
+86
@@ -19,6 +19,7 @@
|
||||
"lefthook": "^2.1.12",
|
||||
"oxfmt": "^0.66.0",
|
||||
"oxlint": "^1.81.0",
|
||||
"publint": "^0.3.24",
|
||||
"tslib": "^2.8.1",
|
||||
"type-fest": "^5.9.0",
|
||||
"typescript": "^7.0.2"
|
||||
@@ -1400,6 +1401,22 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@publint/pack": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.7.tgz",
|
||||
"integrity": "sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyexec": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://bjornlu.com/sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node26": {
|
||||
"version": "26.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node26/-/node26-26.0.1.tgz",
|
||||
@@ -2720,6 +2737,16 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/oxfmt": {
|
||||
"version": "0.66.0",
|
||||
"resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.66.0.tgz",
|
||||
@@ -2853,6 +2880,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/package-manager-detector": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz",
|
||||
"integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -2890,6 +2924,13 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||
@@ -2903,6 +2944,28 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/publint": {
|
||||
"version": "0.3.24",
|
||||
"resolved": "https://registry.npmjs.org/publint/-/publint-0.3.24.tgz",
|
||||
"integrity": "sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@publint/pack": "^0.1.7",
|
||||
"package-manager-detector": "^1.8.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"sade": "^1.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"publint": "src/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://bjornlu.com/sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
|
||||
@@ -2913,6 +2976,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mri": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
||||
@@ -3049,6 +3125,16 @@
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
|
||||
"integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
|
||||
+4
-2
@@ -13,7 +13,7 @@
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmueller/tiny-pattern-ts.git"
|
||||
"url": "git+https://github.com/tmueller/tiny-pattern-ts.git"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"check": "npm run check:oxlint && npm run check:oxfmt && npm run check:tsc && npm run check:cspell && npm run check:outdated",
|
||||
"check": "npm run check:tsc && npm run check:oxlint && npm run check:oxfmt && npm run check:cspell && npm run check:outdated",
|
||||
"check:cspell": "cspell lint ${LEFTHOOK_FILES:-.}",
|
||||
"check:outdated": "check-outdated --ignore-pre-releases --ignore-packages @oxfmt/binding-darwin-arm64,@oxfmt/binding-darwin-x64,@oxfmt/binding-linux-arm64-gnu,@oxfmt/binding-linux-arm64-musl,@oxfmt/binding-linux-x64-gnu,@oxfmt/binding-linux-x64-musl,@oxfmt/binding-win32-x64-msvc,@oxlint/binding-darwin-arm64,@oxlint/binding-darwin-x64,@oxlint/binding-linux-arm64-gnu,@oxlint/binding-linux-arm64-musl,@oxlint/binding-linux-x64-gnu,@oxlint/binding-linux-x64-musl,@oxlint/binding-win32-x64-msvc",
|
||||
"check:oxfmt": "oxfmt --check ${LEFTHOOK_FILES:-.}",
|
||||
@@ -47,6 +47,7 @@
|
||||
"test": "npm run check:tsc && node --test --strip-types \"src/**/*.test.ts\"",
|
||||
"test:ci": "c8 --reporter=text --reporter=lcov --reporter=html node --test --strip-types \"src/**/*.test.ts\"",
|
||||
"test:unit": "node --test --strip-types \"src/**/*.test.ts\"",
|
||||
"publish:publint": "publint",
|
||||
"use:git-commit-message": "cp commit-message-template .git/COMMIT_EDITMSG || true"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -60,6 +61,7 @@
|
||||
"lefthook": "^2.1.12",
|
||||
"oxfmt": "^0.66.0",
|
||||
"oxlint": "^1.81.0",
|
||||
"publint": "^0.3.24",
|
||||
"tslib": "^2.8.1",
|
||||
"type-fest": "^5.9.0",
|
||||
"typescript": "^7.0.2"
|
||||
|
||||
+49
-4
@@ -157,14 +157,47 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
||||
|
||||
## 5. Scripts
|
||||
|
||||
### PREFIX CONVENTION
|
||||
|
||||
Script names use a prefix that signals _when_ the script is intended
|
||||
to run. A `<prefix>:<name>` script is implicitly aggregated by a
|
||||
`<prefix>` script (if one exists) and run by the corresponding
|
||||
lefthook hook or CI step. Picking the right prefix documents the
|
||||
script's intended lifecycle:
|
||||
|
||||
- `check:*` — read-only verification. Aggregated by `npm run check`
|
||||
(which runs all `check:*` scripts in order). Called from the
|
||||
lefthook pre-commit hook on staged files, and from the CI build
|
||||
job on the full project. Read-only; never modifies files.
|
||||
- `fix:*` — mutating counterpart of a `check:*` script. There is
|
||||
**no** `npm run fix` aggregator by design: fixes should be
|
||||
intentional, not batched. Run individually.
|
||||
- `test:*` — test scripts. `npm run test` is the canonical entry
|
||||
point (`check:tsc` + unit tests); `test:unit` skips the typecheck
|
||||
for fast local iteration; `test:ci` adds c8 coverage and is the
|
||||
CI variant.
|
||||
- `publish:*` — runs only at publish time, in the CI `publish` job
|
||||
(immediately before `npm publish`). There is **no** local
|
||||
`npm run publish` script — publishing is CI-only by policy (see
|
||||
§7). The `publish:` prefix still documents intent: this script
|
||||
validates the _publishable artifact_ (e.g., `dist/`) rather than
|
||||
the source.
|
||||
|
||||
A new script should pick the prefix that matches its lifecycle, not
|
||||
invent a new one. If no existing prefix fits, that is a signal the
|
||||
script does not belong in the standard pipeline.
|
||||
|
||||
### SETUP
|
||||
|
||||
- `use:git-commit-message`: Set up commit message template (if needed).
|
||||
|
||||
### TEST
|
||||
|
||||
- `test`: Run `tsc --noEmit` then `node --test --strip-types src/`.
|
||||
- `test:unit`: Run unit tests with `node --test --strip-types src/`
|
||||
- `test`: Run `check:tsc` then `node --test --strip-types "src/**/*.test.ts"`.
|
||||
The glob is required because Node 26 does not auto-discover test
|
||||
files in a bare directory argument (`node --test src/` is
|
||||
interpreted as a module path on Node 26+).
|
||||
- `test:unit`: Run unit tests with `node --test --strip-types "src/**/*.test.ts"`
|
||||
(no preceding typecheck).
|
||||
- `test:ci`: Run tests in CI mode with c8 coverage (text + lcov + html
|
||||
reporters), uploading `coverage/` as an artifact.
|
||||
@@ -216,6 +249,16 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
||||
(There is no `fix` aggregator in the scripts; run the `fix:*` scripts
|
||||
individually.)
|
||||
|
||||
### PUBLISH
|
||||
|
||||
- `publish:publint`: `publint` — runs the pack-and-lint flow against
|
||||
the current project (uses `npm pack` to validate the actual
|
||||
publishable artifact against `package.json`'s `files`, `exports`,
|
||||
`main`, etc.). Requires a fresh `build` to have populated `dist/`.
|
||||
Called by the CI `publish` job immediately before `npm publish`
|
||||
(see §6). Not part of `npm run check` and not run on commit:
|
||||
it validates publishing correctness, not source correctness.
|
||||
|
||||
### HOOKS
|
||||
|
||||
- Lefthook runs the relevant `check:*` scripts on staged files in
|
||||
@@ -235,8 +278,10 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
||||
- `publish` job: only on `refs/tags/*`, depends on `build`. Steps:
|
||||
`actions/checkout@v4`, `actions/setup-node@v4` (with
|
||||
`node-version-file: .node-version` and `registry-url`),
|
||||
`npm ci`, `npm run build`, `npm publish --access public`
|
||||
with `NODE_AUTH_TOKEN` from secrets.
|
||||
`npm ci`, `npm run build`, `npm run publish:publint` (see
|
||||
§5 for why this is `publish:` and not `check:`), and
|
||||
`npm publish --access public` with `NODE_AUTH_TOKEN` from
|
||||
secrets.
|
||||
|
||||
## 7. Versioning & Publishing
|
||||
|
||||
|
||||
Reference in new issue
Block a user