@liiift-studio/vf-clamp-cli 0.1.0 → 0.2.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 +6 -0
- package/dist/commands/clamp.js +68 -2
- package/package.json +53 -42
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
A command-line interface for [`@liiift-studio/vf-clamp`](https://www.npmjs.com/package/@liiift-studio/vf-clamp) — restrict variable font axis ranges from the terminal without writing any JavaScript.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Try it live
|
|
8
|
+
|
|
9
|
+
Want to see what axis-clamping does before installing anything? **[vfclamp.com](https://vfclamp.com)** is an interactive web demo of the same axis-constraint engine that powers this CLI — upload a variable font, pick named instances or set axis ranges in the browser, and download the restricted result. The CLI runs the identical engine locally and headless for scripting and batch workflows.
|
|
10
|
+
|
|
5
11
|
## Install
|
|
6
12
|
|
|
7
13
|
Requires **Node.js 18 or newer**.
|
package/dist/commands/clamp.js
CHANGED
|
@@ -130,15 +130,81 @@ async function runClamp(fontPath, opts) {
|
|
|
130
130
|
}));
|
|
131
131
|
const written = await writeOutputs(writeInputs, outputDir, { force: opts.force !== false });
|
|
132
132
|
if (opts.json) {
|
|
133
|
-
|
|
133
|
+
// v0.2.x: include per-output structural diagnostics when --json is set
|
|
134
|
+
// so machine consumers (build pipelines, batch scripts) can read the
|
|
135
|
+
// same instance / axis / pinned / size summary the Glyphs and
|
|
136
|
+
// RoboFont plugins show in their size-estimate strip.
|
|
137
|
+
const diagnostics = writeInputs.map((result, i) => {
|
|
138
|
+
const req = outputRequests[i];
|
|
139
|
+
return {
|
|
140
|
+
path: written[i],
|
|
141
|
+
...summariseRequest(req, result.buffer),
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
process.stdout.write(JSON.stringify({ written, diagnostics }) + '\n');
|
|
134
145
|
}
|
|
135
146
|
else {
|
|
136
|
-
for (
|
|
147
|
+
for (let i = 0; i < written.length; i++) {
|
|
148
|
+
const filePath = written[i];
|
|
137
149
|
// Bare paths on stdout so consumers can `... | xargs` them.
|
|
138
150
|
process.stdout.write(`${filePath}\n`);
|
|
151
|
+
if (opts.verbose) {
|
|
152
|
+
// Diagnostic line mirrors the Glyphs+RoboFont dialog's size
|
|
153
|
+
// estimate: `~38 KB · 5 instances · 3 ax · 1 pinned`. Goes to
|
|
154
|
+
// stderr so it doesn't pollute the xargs-friendly stdout.
|
|
155
|
+
const req = outputRequests[i];
|
|
156
|
+
const result = writeInputs[i];
|
|
157
|
+
if (req && result) {
|
|
158
|
+
process.stderr.write(` ${formatSummary(summariseRequest(req, result.buffer))}\n`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
139
161
|
}
|
|
140
162
|
}
|
|
141
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Compute the structural-summary fields shown in --verbose / --json output.
|
|
166
|
+
* Mirrors the Glyphs and RoboFont dialogs' `_count_structural` helper:
|
|
167
|
+
* number of licensed instances, total axes the user touched, number of
|
|
168
|
+
* those axes pinned to a single value, and the resulting file size.
|
|
169
|
+
*
|
|
170
|
+
* Master count is intentionally omitted — the engine doesn't expose
|
|
171
|
+
* post-instancer master geometry through its public API, and computing
|
|
172
|
+
* it from the OUTPUT buffer would require re-parsing TTFont in JS.
|
|
173
|
+
*/
|
|
174
|
+
function summariseRequest(req, buffer) {
|
|
175
|
+
const instances = req?.instances?.length ?? 0;
|
|
176
|
+
const axesObj = req?.axes ?? {};
|
|
177
|
+
const axes = Object.keys(axesObj).length;
|
|
178
|
+
let pinned = 0;
|
|
179
|
+
for (const value of Object.values(axesObj)) {
|
|
180
|
+
// Numbers in the axes map mean "pin to this value"; objects with
|
|
181
|
+
// equal min/max are also effectively pinned.
|
|
182
|
+
if (typeof value === 'number') {
|
|
183
|
+
pinned++;
|
|
184
|
+
}
|
|
185
|
+
else if (value && typeof value === 'object'
|
|
186
|
+
&& 'min' in value && 'max' in value
|
|
187
|
+
&& value.min
|
|
188
|
+
=== value.max) {
|
|
189
|
+
pinned++;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { sizeBytes: buffer.byteLength, instances, axes, pinned };
|
|
193
|
+
}
|
|
194
|
+
/** Human-readable form of summariseRequest's output. */
|
|
195
|
+
function formatSummary(s) {
|
|
196
|
+
const parts = [];
|
|
197
|
+
const sizeKB = Math.round(s.sizeBytes / 1024);
|
|
198
|
+
parts.push(`~${sizeKB.toLocaleString()} KB`);
|
|
199
|
+
if (s.instances > 0) {
|
|
200
|
+
parts.push(`${s.instances} instance${s.instances !== 1 ? 's' : ''}`);
|
|
201
|
+
}
|
|
202
|
+
if (s.axes > 0) {
|
|
203
|
+
const pinnedSuffix = s.pinned > 0 ? ` · ${s.pinned} pinned` : '';
|
|
204
|
+
parts.push(`${s.axes} ax${pinnedSuffix}`);
|
|
205
|
+
}
|
|
206
|
+
return parts.join(' · ');
|
|
207
|
+
}
|
|
142
208
|
/** Commander value collector — appends each repeated flag value into an array. */
|
|
143
209
|
function collect(value, previous) {
|
|
144
210
|
return [...previous, value];
|
package/package.json
CHANGED
|
@@ -1,44 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
2
|
+
"name": "@liiift-studio/vf-clamp-cli",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "CLI for @liiift-studio/vf-clamp — restrict variable font axis ranges from the command line",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vf-clamp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.build.json && chmod +x dist/index.js",
|
|
19
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:coverage": "vitest run --coverage",
|
|
23
|
+
"prepublishOnly": "npm test && npm run build"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@liiift-studio/vf-clamp": "^2.0.1",
|
|
27
|
+
"commander": "^12.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"@vitest/coverage-v8": "^3.2.0",
|
|
32
|
+
"typescript": "^5.0.0",
|
|
33
|
+
"vitest": "^3.2.0"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/Liiift-Studio/vf-clamp-cli.git"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"variable-font",
|
|
47
|
+
"font",
|
|
48
|
+
"cli",
|
|
49
|
+
"vf-clamp",
|
|
50
|
+
"typography",
|
|
51
|
+
"fonttools"
|
|
52
|
+
],
|
|
53
|
+
"author": "Quinn Keaveney <quinn@liiift.studio>",
|
|
54
|
+
"license": "MIT"
|
|
44
55
|
}
|