@casomoltd/tooling 0.22.5 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -9
- package/bin/build-report.mjs +5 -5
- package/bin/screenshot.mjs +22 -20
- package/package.json +2 -8
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Shared linting, formatting, commit config, and CLI tools for Casomo Ltd's repos.
|
|
|
25
25
|
| `pre-commit` | Husky pre-commit hook — runs `npm run check` |
|
|
26
26
|
| `commit-msg` | Husky commit-msg hook — runs commitlint |
|
|
27
27
|
| `readability` | Measure reading difficulty of page content |
|
|
28
|
-
| `screenshot` | Capture a dev server page via
|
|
28
|
+
| `screenshot` | Capture a dev server page via Playwright — an agent's visual feedback loop |
|
|
29
29
|
| `build-report` | Compile a Typst client report to PDF with the house template |
|
|
30
30
|
|
|
31
31
|
## Install
|
|
@@ -43,9 +43,31 @@ npm i -D @casomoltd/tooling
|
|
|
43
43
|
|
|
44
44
|
The package is public, so installs need no token or `.npmrc`. The `files`
|
|
45
45
|
allowlist plus the `verify-pack` leak-gate keep the published surface to
|
|
46
|
-
configs/CLI only.
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
configs/CLI only.
|
|
47
|
+
|
|
48
|
+
**Installing this package never installs a browser.** The `screenshot`
|
|
49
|
+
bin needs one and `build-report` needs the `typst` CLI, but neither is
|
|
50
|
+
declared here — see [External prerequisites](#external-prerequisites).
|
|
51
|
+
|
|
52
|
+
## External prerequisites
|
|
53
|
+
|
|
54
|
+
Two bins need something npm cannot sensibly deliver: `screenshot` needs
|
|
55
|
+
a Chromium build, `build-report` needs the `typst` CLI and the IBM Plex
|
|
56
|
+
fonts. **Neither is declared — not as a dependency, and not as an
|
|
57
|
+
optional peer.** Both load their prerequisite lazily and fail with an
|
|
58
|
+
install instruction when it is missing, so a consumer that never runs
|
|
59
|
+
those bins carries nothing for them.
|
|
60
|
+
|
|
61
|
+
The rule, for anything added later: a heavyweight prerequisite only some
|
|
62
|
+
consumers need is documented here, never declared. An optional peer
|
|
63
|
+
looks free because npm installs nothing for it, but it resolves into a
|
|
64
|
+
consumer's lockfile as soon as anything pulls it in, and npm will not
|
|
65
|
+
prune a satisfied optional peer afterwards — not on `npm install`, not
|
|
66
|
+
on `npm install --package-lock-only`, not on `npm uninstall`. That is
|
|
67
|
+
how one dev-only browser put the `extract-zip` advisory
|
|
68
|
+
(GHSA-jmr9-qjv8-65gv) into four repos that never took a screenshot, and
|
|
69
|
+
why clearing it needed a release of this package rather than a fix in
|
|
70
|
+
each of them.
|
|
49
71
|
|
|
50
72
|
## Husky hooks
|
|
51
73
|
|
|
@@ -104,7 +126,20 @@ version that ships the config.
|
|
|
104
126
|
|
|
105
127
|
## Screenshot tool
|
|
106
128
|
|
|
107
|
-
|
|
129
|
+
A visual feedback loop for coding agents. Claude captures the page it
|
|
130
|
+
just changed, reads the PNG back and checks its own work, instead of
|
|
131
|
+
waiting for someone to look and paste a screen grab. That is what the
|
|
132
|
+
bin is for, and why its output lands under `.claude/`; a person wanting
|
|
133
|
+
a screenshot already has a browser open.
|
|
134
|
+
|
|
135
|
+
It needs a Chromium build, which this package does not install (see
|
|
136
|
+
[External prerequisites](#external-prerequisites)):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm i -D playwright && npx playwright install chromium
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Then capture the running dev server:
|
|
108
143
|
|
|
109
144
|
```bash
|
|
110
145
|
npm run ss # 1280×800 desktop capture
|
|
@@ -175,10 +210,9 @@ after is the body. From a consumer repo the import goes through
|
|
|
175
210
|
`report/example.typ` is a lorem-ipsum reference report exercising every
|
|
176
211
|
feature the template styles — compile it to see the house style.
|
|
177
212
|
|
|
178
|
-
Two prerequisites
|
|
179
|
-
`
|
|
180
|
-
|
|
181
|
-
the **IBM Plex Sans / IBM Plex Mono** fonts
|
|
213
|
+
Two [external prerequisites](#external-prerequisites) apply here: the
|
|
214
|
+
**`typst` CLI** on PATH (`snap install typst` / `cargo install
|
|
215
|
+
typst-cli`) and the **IBM Plex Sans / IBM Plex Mono** fonts
|
|
182
216
|
([github.com/IBM/plex](https://github.com/IBM/plex)) — `build-report`
|
|
183
217
|
fails loud on the former and warns on the latter.
|
|
184
218
|
|
package/bin/build-report.mjs
CHANGED
|
@@ -83,11 +83,11 @@ function findRoot(from) {
|
|
|
83
83
|
}
|
|
84
84
|
const root = values.root ? path.resolve(values.root) : findRoot(input);
|
|
85
85
|
|
|
86
|
-
// typst is an external native binary (like
|
|
87
|
-
// deliberately not an npm dependency). One probe does
|
|
88
|
-
// means typst is missing; otherwise the output feeds
|
|
89
|
-
// fonts don't fail a Typst compile — it silently
|
|
90
|
-
// out off-brand — so warn loud up front.
|
|
86
|
+
// typst is an external native binary (like playwright's browsers for
|
|
87
|
+
// screenshot, it is deliberately not an npm dependency). One probe does
|
|
88
|
+
// double duty: ENOENT means typst is missing; otherwise the output feeds
|
|
89
|
+
// the font check. Missing fonts don't fail a Typst compile — it silently
|
|
90
|
+
// falls back and the PDF comes out off-brand — so warn loud up front.
|
|
91
91
|
const fonts = spawnSync("typst", ["fonts"], {encoding: "utf8"});
|
|
92
92
|
if (fonts.error) {
|
|
93
93
|
fail(
|
package/bin/screenshot.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Capture a screenshot of
|
|
4
|
+
* Capture a screenshot of a running dev server.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* npm run ss [page] [--width <n>] [--height <n>]
|
|
@@ -25,37 +25,38 @@
|
|
|
25
25
|
*
|
|
26
26
|
* Environment:
|
|
27
27
|
* SCREENSHOT_URL Base URL to capture (default: http://localhost:3000)
|
|
28
|
+
* SCREENSHOT_DIR Output directory (default: <cwd>/.claude/screenshots)
|
|
28
29
|
*
|
|
29
30
|
* Output:
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
* {dir}/{page}-{timestamp}.png Timestamped capture
|
|
32
|
+
* {dir}/latest.png Always the most recent capture
|
|
32
33
|
*/
|
|
33
34
|
|
|
34
35
|
import { parseArgs } from "node:util";
|
|
35
36
|
import path from "path";
|
|
36
37
|
import fs from "fs";
|
|
37
38
|
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
39
|
+
// playwright is an external prerequisite, not a dependency of this package.
|
|
40
|
+
// A declared optional peer resolves into every consumer's lockfile and npm
|
|
41
|
+
// will not prune it again, so a browser nobody screenshots with becomes a
|
|
42
|
+
// standing advisory. Load it lazily and fail loud when it is absent.
|
|
43
|
+
async function loadChromium() {
|
|
42
44
|
try {
|
|
43
|
-
return (await import("
|
|
45
|
+
return (await import("playwright")).chromium;
|
|
44
46
|
} catch {
|
|
45
47
|
throw new Error(
|
|
46
|
-
"
|
|
47
|
-
"(npm i -D
|
|
48
|
+
"playwright is not installed — add it as a devDependency " +
|
|
49
|
+
"(npm i -D playwright && npx playwright install chromium) " +
|
|
50
|
+
"to use the screenshot bin.",
|
|
48
51
|
);
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
const BASE_URL =
|
|
53
56
|
process.env.SCREENSHOT_URL || "http://localhost:3000";
|
|
54
|
-
const OUTPUT_DIR =
|
|
55
|
-
process.
|
|
56
|
-
".claude",
|
|
57
|
-
"screenshots",
|
|
58
|
-
);
|
|
57
|
+
const OUTPUT_DIR =
|
|
58
|
+
process.env.SCREENSHOT_DIR ||
|
|
59
|
+
path.join(process.cwd(), ".claude", "screenshots");
|
|
59
60
|
|
|
60
61
|
const PRESETS = {
|
|
61
62
|
mobile: { width: 390, height: 844 },
|
|
@@ -111,15 +112,16 @@ async function screenshot() {
|
|
|
111
112
|
|
|
112
113
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
113
114
|
|
|
114
|
-
const
|
|
115
|
-
const browser = await
|
|
115
|
+
const chromium = await loadChromium();
|
|
116
|
+
const browser = await chromium.launch({
|
|
116
117
|
headless: true,
|
|
117
118
|
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
118
119
|
});
|
|
119
120
|
|
|
120
121
|
try {
|
|
121
|
-
const page = await browser.newPage(
|
|
122
|
-
|
|
122
|
+
const page = await browser.newPage({
|
|
123
|
+
viewport: { width, height },
|
|
124
|
+
});
|
|
123
125
|
|
|
124
126
|
const url =
|
|
125
127
|
pageName === "base"
|
|
@@ -128,7 +130,7 @@ async function screenshot() {
|
|
|
128
130
|
console.log(`Navigating to ${url}...`);
|
|
129
131
|
|
|
130
132
|
await page.goto(url, {
|
|
131
|
-
waitUntil: "
|
|
133
|
+
waitUntil: "networkidle",
|
|
132
134
|
timeout: 30000,
|
|
133
135
|
});
|
|
134
136
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casomoltd/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "Casomo's shared dev config: ESLint, Prettier, commitlint, knip & jscpd presets and dev CLIs (git hooks, readability, screenshots)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -55,13 +55,7 @@
|
|
|
55
55
|
"eslint-config-next": ">=16",
|
|
56
56
|
"eslint-plugin-sonarjs": ">=3",
|
|
57
57
|
"jscpd": ">=4",
|
|
58
|
-
"knip": ">=5"
|
|
59
|
-
"puppeteer": ">=24"
|
|
60
|
-
},
|
|
61
|
-
"peerDependenciesMeta": {
|
|
62
|
-
"puppeteer": {
|
|
63
|
-
"optional": true
|
|
64
|
-
}
|
|
58
|
+
"knip": ">=5"
|
|
65
59
|
},
|
|
66
60
|
"devDependencies": {
|
|
67
61
|
"@types/node": "^26.0.0",
|