@keyline-icons/cli 0.2.0 → 0.3.0
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/icons.json +1 -1
- package/package.json +1 -1
- package/src/index.mjs +50 -17
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -22,9 +22,11 @@ import { existsSync } from "node:fs"
|
|
|
22
22
|
const data = JSON.parse(
|
|
23
23
|
await readFile(new URL("../icons.json", import.meta.url), "utf8")
|
|
24
24
|
)
|
|
25
|
-
|
|
25
|
+
// `corners` defaults for a bundle generated before the axis existed, so an
|
|
26
|
+
// older icons.json still runs rather than reading `undefined.includes`.
|
|
27
|
+
const { icons, styles, corners = ["regular"], keywords = {} } = data
|
|
26
28
|
const NAMES = Object.keys(icons)
|
|
27
|
-
const VERSION = "0.
|
|
29
|
+
const VERSION = "0.3.0"
|
|
28
30
|
|
|
29
31
|
/** Colour only when a human is looking. Piped output stays clean. */
|
|
30
32
|
const tty = process.stdout.isTTY
|
|
@@ -49,7 +51,7 @@ function die(msg) {
|
|
|
49
51
|
* positional, which is what lets `add a b c --style fill` work in any order.
|
|
50
52
|
*/
|
|
51
53
|
function parseArgs(argv) {
|
|
52
|
-
const alias = { s: "style", o: "out", l: "limit", h: "help", v: "version" }
|
|
54
|
+
const alias = { s: "style", c: "corners", o: "out", l: "limit", h: "help", v: "version" }
|
|
53
55
|
const flags = {}
|
|
54
56
|
const positional = []
|
|
55
57
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -89,10 +91,25 @@ const styleOf = (flags) => {
|
|
|
89
91
|
return s
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
const cornersOf = (flags) => {
|
|
95
|
+
const k = flags.corners ?? "regular"
|
|
96
|
+
if (!corners.includes(k))
|
|
97
|
+
die(`unknown corner treatment \`${k}\`. One of: ${corners.join(", ")}`)
|
|
98
|
+
return k
|
|
99
|
+
}
|
|
100
|
+
|
|
92
101
|
/* ------------------------------------------------------------------ icons */
|
|
93
102
|
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
/**
|
|
104
|
+
* A drawing in one style and one treatment.
|
|
105
|
+
*
|
|
106
|
+
* The rounded half sits at `icons[name][style]` where it has always sat, and
|
|
107
|
+
* the sharp half one level down under `sharp`, so a project pinned to an older
|
|
108
|
+
* bundle reads the same shape it read before.
|
|
109
|
+
*/
|
|
110
|
+
function svgFor(name, style, corners = "regular") {
|
|
111
|
+
const art =
|
|
112
|
+
corners === "sharp" ? icons[name]?.sharp?.[style] : icons[name]?.[style]
|
|
96
113
|
if (!art) return null
|
|
97
114
|
const attrs = Object.entries(art.root)
|
|
98
115
|
.map(([k, v]) => ` ${k}="${v}"`)
|
|
@@ -181,14 +198,14 @@ const wholeWord = (hay, needle) => {
|
|
|
181
198
|
return false
|
|
182
199
|
}
|
|
183
200
|
|
|
184
|
-
function search(query, style, limit) {
|
|
201
|
+
function search(query, style, limit, corners = "regular") {
|
|
185
202
|
const q = query.toLowerCase().trim()
|
|
186
203
|
const words = wordsOf(query)
|
|
187
204
|
if (!words.length) return []
|
|
188
205
|
|
|
189
206
|
return NAMES.filter(
|
|
190
207
|
(n) =>
|
|
191
|
-
(!style ||
|
|
208
|
+
(!style || svgFor(n, style, corners)) &&
|
|
192
209
|
(foreign[q] === n ||
|
|
193
210
|
wholeWord(n, q) ||
|
|
194
211
|
words.every((w) => wholeWord(n, w)) ||
|
|
@@ -255,17 +272,20 @@ function nearest(name) {
|
|
|
255
272
|
.map((h) => h.n)
|
|
256
273
|
}
|
|
257
274
|
|
|
258
|
-
function resolveOrDie(name, style) {
|
|
275
|
+
function resolveOrDie(name, style, corners = "regular") {
|
|
259
276
|
if (!icons[name]) {
|
|
260
277
|
const near = nearest(name)
|
|
261
278
|
die(
|
|
262
279
|
`no icon named \`${name}\`.${near.length ? ` Did you mean: ${near.join(", ")}?` : ""}`
|
|
263
280
|
)
|
|
264
281
|
}
|
|
265
|
-
const svg = svgFor(name, style)
|
|
282
|
+
const svg = svgFor(name, style, corners)
|
|
266
283
|
if (!svg) {
|
|
284
|
+
// `Object.keys` would list `sharp` here as though it were a fourth style,
|
|
285
|
+
// which is the axis leaking into a sentence about coverage.
|
|
286
|
+
const has = styles.filter((s) => svgFor(name, s, corners))
|
|
267
287
|
die(
|
|
268
|
-
`\`${name}\` has no ${style} style. It has: ${
|
|
288
|
+
`\`${name}\` has no ${style} style. It has: ${has.join(", ")}.\n` +
|
|
269
289
|
` Duotone and fill need a fillable region, and this glyph has none.`
|
|
270
290
|
)
|
|
271
291
|
}
|
|
@@ -288,13 +308,14 @@ ${bold("COMMANDS")}
|
|
|
288
308
|
|
|
289
309
|
${bold("OPTIONS")}
|
|
290
310
|
-s, --style <s> ${styles.join(" | ")} (default: stroke)
|
|
311
|
+
-c, --corners <c> ${corners.join(" | ")} (default: regular)
|
|
291
312
|
-o, --out <dir> Where \`add\` writes (default: ./icons)
|
|
292
313
|
-l, --limit <n> Max results for \`search\` (default: 25)
|
|
293
314
|
|
|
294
315
|
${bold("EXAMPLES")}
|
|
295
316
|
keyline-icons search arrow
|
|
296
317
|
keyline-icons search mail --style fill
|
|
297
|
-
keyline-icons show check > check.svg
|
|
318
|
+
keyline-icons show check --corners sharp > check-sharp.svg
|
|
298
319
|
keyline-icons add circle-arrow-down bell --out src/icons
|
|
299
320
|
keyline-icons list | grep chart
|
|
300
321
|
|
|
@@ -309,7 +330,8 @@ const commands = {
|
|
|
309
330
|
|
|
310
331
|
list({ flags }) {
|
|
311
332
|
const style = flags.style ? styleOf(flags) : null
|
|
312
|
-
const
|
|
333
|
+
const treatment = cornersOf(flags)
|
|
334
|
+
const names = style ? NAMES.filter((n) => svgFor(n, style, treatment)) : NAMES
|
|
313
335
|
for (const n of names) out(n)
|
|
314
336
|
note(dim(`\n${names.length} icons${style ? ` in ${style}` : ""}`))
|
|
315
337
|
},
|
|
@@ -329,7 +351,7 @@ const commands = {
|
|
|
329
351
|
is not a number at all earns the default. */
|
|
330
352
|
const raw = parseInt(flags.limit ?? "25", 10)
|
|
331
353
|
const limit = Math.min(Math.max(Number.isNaN(raw) ? 25 : raw, 1), 200)
|
|
332
|
-
const hits = search(query, style, limit)
|
|
354
|
+
const hits = search(query, style, limit, cornersOf(flags))
|
|
333
355
|
if (!hits.length) {
|
|
334
356
|
const near = nearest(query)
|
|
335
357
|
die(
|
|
@@ -337,7 +359,7 @@ const commands = {
|
|
|
337
359
|
)
|
|
338
360
|
}
|
|
339
361
|
for (const n of hits) {
|
|
340
|
-
const has = styles.filter((s) =>
|
|
362
|
+
const has = styles.filter((s) => svgFor(n, s, cornersOf(flags)))
|
|
341
363
|
out(`${n}${tty ? " " + dim(`[${has.join(", ")}]`) : ""}`)
|
|
342
364
|
}
|
|
343
365
|
note(dim(`\n${hits.length} shown${style ? ` in ${style}` : ""}`))
|
|
@@ -346,17 +368,27 @@ const commands = {
|
|
|
346
368
|
show({ flags, positional }) {
|
|
347
369
|
const [name] = positional
|
|
348
370
|
if (!name) die("show needs an icon name. Try: keyline-icons show check")
|
|
349
|
-
process.stdout.write(resolveOrDie(name, styleOf(flags)))
|
|
371
|
+
process.stdout.write(resolveOrDie(name, styleOf(flags), cornersOf(flags)))
|
|
350
372
|
},
|
|
351
373
|
|
|
352
374
|
async add({ flags, positional }) {
|
|
353
375
|
if (!positional.length) die("add needs at least one icon name.")
|
|
354
376
|
const style = styleOf(flags)
|
|
377
|
+
const treatment = cornersOf(flags)
|
|
355
378
|
const dir = resolve(flags.out === true || !flags.out ? "icons" : flags.out)
|
|
356
379
|
|
|
380
|
+
// The treatment rides in the filename, for the reason the style does not:
|
|
381
|
+
// two treatments of one drawing are the same name and the same style, so
|
|
382
|
+
// adding both would otherwise write one file twice and leave whichever ran
|
|
383
|
+
// last. Rounded keeps the bare name, which is what everyone means by it.
|
|
384
|
+
const suffix = treatment === "regular" ? "" : `-${treatment}`
|
|
385
|
+
|
|
357
386
|
// Every name is resolved before anything is written, so a typo in the
|
|
358
387
|
// third of five does not leave two files on disk and an error.
|
|
359
|
-
const files = positional.map((name) => [
|
|
388
|
+
const files = positional.map((name) => [
|
|
389
|
+
`${name}${suffix}`,
|
|
390
|
+
resolveOrDie(name, style, treatment),
|
|
391
|
+
])
|
|
360
392
|
|
|
361
393
|
await mkdir(dir, { recursive: true })
|
|
362
394
|
for (const [name, svg] of files) {
|
|
@@ -369,7 +401,8 @@ const commands = {
|
|
|
369
401
|
}
|
|
370
402
|
note(
|
|
371
403
|
dim(
|
|
372
|
-
`\n${files.length} ${
|
|
404
|
+
`\n${files.length} ${treatment === "sharp" ? "sharp " : ""}${style} ` +
|
|
405
|
+
`icon${files.length === 1 ? "" : "s"} to ${dir}`
|
|
373
406
|
)
|
|
374
407
|
)
|
|
375
408
|
},
|