@kensio/colophon 2.2.0 → 2.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.
Files changed (350) hide show
  1. package/README.md +560 -24
  2. package/dist/cli/args.d.ts +14 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +67 -0
  5. package/dist/cli/args.js.map +1 -0
  6. package/dist/cli/config.d.ts +12 -0
  7. package/dist/cli/config.d.ts.map +1 -0
  8. package/dist/cli/config.js +43 -0
  9. package/dist/cli/config.js.map +1 -0
  10. package/dist/cli/index.d.ts +3 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +35 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli.js +34 -6
  15. package/dist/cli.js.map +1 -1
  16. package/dist/config/defaults.d.ts +64 -0
  17. package/dist/config/defaults.d.ts.map +1 -0
  18. package/dist/config/defaults.js +64 -0
  19. package/dist/config/defaults.js.map +1 -0
  20. package/dist/config/index.d.ts +22 -0
  21. package/dist/config/index.d.ts.map +1 -0
  22. package/dist/config/index.js +52 -0
  23. package/dist/config/index.js.map +1 -0
  24. package/dist/config/resolve.d.ts +24 -0
  25. package/dist/config/resolve.d.ts.map +1 -0
  26. package/dist/config/resolve.js +66 -0
  27. package/dist/config/resolve.js.map +1 -0
  28. package/dist/config/size.d.ts +20 -0
  29. package/dist/config/size.d.ts.map +1 -0
  30. package/dist/config/size.js +46 -0
  31. package/dist/config/size.js.map +1 -0
  32. package/dist/config.d.ts +27 -1
  33. package/dist/config.d.ts.map +1 -1
  34. package/dist/config.js +78 -2
  35. package/dist/config.js.map +1 -1
  36. package/dist/content/index.d.ts +5 -51
  37. package/dist/content/index.d.ts.map +1 -1
  38. package/dist/content/index.js +3 -115
  39. package/dist/content/index.js.map +1 -1
  40. package/dist/content/props.d.ts +15 -0
  41. package/dist/content/props.d.ts.map +1 -0
  42. package/dist/content/props.js +71 -0
  43. package/dist/content/props.js.map +1 -0
  44. package/dist/content/slug.d.ts +23 -0
  45. package/dist/content/slug.d.ts.map +1 -0
  46. package/dist/content/slug.js +52 -0
  47. package/dist/content/slug.js.map +1 -0
  48. package/dist/content/walk.d.ts +17 -0
  49. package/dist/content/walk.d.ts.map +1 -0
  50. package/dist/content/walk.js +57 -0
  51. package/dist/content/walk.js.map +1 -0
  52. package/dist/fonts/index.d.ts +10 -0
  53. package/dist/fonts/index.d.ts.map +1 -0
  54. package/dist/fonts/index.js +11 -0
  55. package/dist/fonts/index.js.map +1 -0
  56. package/dist/fonts/materialise.d.ts +7 -0
  57. package/dist/fonts/materialise.d.ts.map +1 -0
  58. package/dist/fonts/materialise.js +47 -0
  59. package/dist/fonts/materialise.js.map +1 -0
  60. package/dist/fonts/resolve.d.ts +7 -0
  61. package/dist/fonts/resolve.d.ts.map +1 -0
  62. package/dist/fonts/resolve.js +50 -0
  63. package/dist/fonts/resolve.js.map +1 -0
  64. package/dist/fonts.d.ts +18 -0
  65. package/dist/fonts.d.ts.map +1 -0
  66. package/dist/fonts.js +102 -0
  67. package/dist/fonts.js.map +1 -0
  68. package/dist/generate/extra.d.ts +16 -0
  69. package/dist/generate/extra.d.ts.map +1 -0
  70. package/dist/generate/extra.js +60 -0
  71. package/dist/generate/extra.js.map +1 -0
  72. package/dist/generate/index.d.ts +19 -0
  73. package/dist/generate/index.d.ts.map +1 -0
  74. package/dist/generate/index.js +46 -0
  75. package/dist/generate/index.js.map +1 -0
  76. package/dist/generate/job.d.ts +35 -0
  77. package/dist/generate/job.d.ts.map +1 -0
  78. package/dist/generate/job.js +2 -0
  79. package/dist/generate/job.js.map +1 -0
  80. package/dist/generate/options.d.ts +56 -0
  81. package/dist/generate/options.d.ts.map +1 -0
  82. package/dist/generate/options.js +17 -0
  83. package/dist/generate/options.js.map +1 -0
  84. package/dist/generate/output-path.d.ts +12 -0
  85. package/dist/generate/output-path.d.ts.map +1 -0
  86. package/dist/generate/output-path.js +18 -0
  87. package/dist/generate/output-path.js.map +1 -0
  88. package/dist/generate/outputs.d.ts +28 -0
  89. package/dist/generate/outputs.d.ts.map +1 -0
  90. package/dist/generate/outputs.js +71 -0
  91. package/dist/generate/outputs.js.map +1 -0
  92. package/dist/generate/plan.d.ts +21 -0
  93. package/dist/generate/plan.d.ts.map +1 -0
  94. package/dist/generate/plan.js +72 -0
  95. package/dist/generate/plan.js.map +1 -0
  96. package/dist/generate/render-image.d.ts +11 -0
  97. package/dist/generate/render-image.d.ts.map +1 -0
  98. package/dist/generate/render-image.js +27 -0
  99. package/dist/generate/render-image.js.map +1 -0
  100. package/dist/generate.d.ts +27 -6
  101. package/dist/generate.d.ts.map +1 -1
  102. package/dist/generate.js +69 -15
  103. package/dist/generate.js.map +1 -1
  104. package/dist/highlight/color.d.ts +9 -0
  105. package/dist/highlight/color.d.ts.map +1 -0
  106. package/dist/highlight/color.js +15 -0
  107. package/dist/highlight/color.js.map +1 -0
  108. package/dist/highlight/grid.d.ts +14 -0
  109. package/dist/highlight/grid.d.ts.map +1 -0
  110. package/dist/highlight/grid.js +36 -0
  111. package/dist/highlight/grid.js.map +1 -0
  112. package/dist/highlight/index.d.ts +12 -0
  113. package/dist/highlight/index.d.ts.map +1 -0
  114. package/dist/highlight/index.js +31 -0
  115. package/dist/highlight/index.js.map +1 -0
  116. package/dist/highlight/language.d.ts +14 -0
  117. package/dist/highlight/language.d.ts.map +1 -0
  118. package/dist/highlight/language.js +42 -0
  119. package/dist/highlight/language.js.map +1 -0
  120. package/dist/highlight/normalise.d.ts +17 -0
  121. package/dist/highlight/normalise.d.ts.map +1 -0
  122. package/dist/highlight/normalise.js +37 -0
  123. package/dist/highlight/normalise.js.map +1 -0
  124. package/dist/highlight/types.d.ts +40 -0
  125. package/dist/highlight/types.d.ts.map +1 -0
  126. package/dist/highlight/types.js +2 -0
  127. package/dist/highlight/types.js.map +1 -0
  128. package/dist/index.d.ts +15 -9
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +9 -5
  131. package/dist/index.js.map +1 -1
  132. package/dist/manifest/build.d.ts +14 -0
  133. package/dist/manifest/build.d.ts.map +1 -0
  134. package/dist/manifest/build.js +43 -0
  135. package/dist/manifest/build.js.map +1 -0
  136. package/dist/manifest/index.d.ts +27 -0
  137. package/dist/manifest/index.d.ts.map +1 -0
  138. package/dist/manifest/index.js +31 -0
  139. package/dist/manifest/index.js.map +1 -0
  140. package/dist/manifest/order.d.ts +12 -0
  141. package/dist/manifest/order.d.ts.map +1 -0
  142. package/dist/manifest/order.js +17 -0
  143. package/dist/manifest/order.js.map +1 -0
  144. package/dist/manifest/page.d.ts +24 -0
  145. package/dist/manifest/page.d.ts.map +1 -0
  146. package/dist/manifest/page.js +55 -0
  147. package/dist/manifest/page.js.map +1 -0
  148. package/dist/meta/html.d.ts +14 -0
  149. package/dist/meta/html.d.ts.map +1 -0
  150. package/dist/meta/html.js +24 -0
  151. package/dist/meta/html.js.map +1 -0
  152. package/dist/meta/index.d.ts +11 -0
  153. package/dist/meta/index.d.ts.map +1 -0
  154. package/dist/meta/index.js +11 -0
  155. package/dist/meta/index.js.map +1 -0
  156. package/dist/meta/tags.d.ts +21 -0
  157. package/dist/meta/tags.d.ts.map +1 -0
  158. package/dist/meta/tags.js +72 -0
  159. package/dist/meta/tags.js.map +1 -0
  160. package/dist/meta/url.d.ts +15 -0
  161. package/dist/meta/url.d.ts.map +1 -0
  162. package/dist/meta/url.js +34 -0
  163. package/dist/meta/url.js.map +1 -0
  164. package/dist/placement/check.d.ts +16 -0
  165. package/dist/placement/check.d.ts.map +1 -0
  166. package/dist/placement/check.js +40 -0
  167. package/dist/placement/check.js.map +1 -0
  168. package/dist/placement/index.d.ts +29 -0
  169. package/dist/placement/index.d.ts.map +1 -0
  170. package/dist/placement/index.js +61 -0
  171. package/dist/placement/index.js.map +1 -0
  172. package/dist/placement/relative.d.ts +31 -0
  173. package/dist/placement/relative.d.ts.map +1 -0
  174. package/dist/placement/relative.js +53 -0
  175. package/dist/placement/relative.js.map +1 -0
  176. package/dist/pool.d.ts +18 -0
  177. package/dist/pool.d.ts.map +1 -0
  178. package/dist/pool.js +56 -0
  179. package/dist/pool.js.map +1 -0
  180. package/dist/render/index.d.ts +10 -0
  181. package/dist/render/index.d.ts.map +1 -0
  182. package/dist/render/index.js +27 -0
  183. package/dist/render/index.js.map +1 -0
  184. package/dist/render/png.d.ts +10 -0
  185. package/dist/render/png.d.ts.map +1 -0
  186. package/dist/render/png.js +30 -0
  187. package/dist/render/png.js.map +1 -0
  188. package/dist/render/svg.d.ts +10 -0
  189. package/dist/render/svg.d.ts.map +1 -0
  190. package/dist/render/svg.js +20 -0
  191. package/dist/render/svg.js.map +1 -0
  192. package/dist/render/template.d.ts +9 -0
  193. package/dist/render/template.d.ts.map +1 -0
  194. package/dist/render/template.js +17 -0
  195. package/dist/render/template.js.map +1 -0
  196. package/dist/render.d.ts +6 -2
  197. package/dist/render.d.ts.map +1 -1
  198. package/dist/render.js +31 -10
  199. package/dist/render.js.map +1 -1
  200. package/dist/stamp/chunk.d.ts +16 -0
  201. package/dist/stamp/chunk.d.ts.map +1 -0
  202. package/dist/stamp/chunk.js +46 -0
  203. package/dist/stamp/chunk.js.map +1 -0
  204. package/dist/stamp/config-digest.d.ts +11 -0
  205. package/dist/stamp/config-digest.d.ts.map +1 -0
  206. package/dist/stamp/config-digest.js +43 -0
  207. package/dist/stamp/config-digest.js.map +1 -0
  208. package/dist/stamp/digest.d.ts +8 -0
  209. package/dist/stamp/digest.d.ts.map +1 -0
  210. package/dist/stamp/digest.js +20 -0
  211. package/dist/stamp/digest.js.map +1 -0
  212. package/dist/stamp/index.d.ts +20 -0
  213. package/dist/stamp/index.d.ts.map +1 -0
  214. package/dist/stamp/index.js +40 -0
  215. package/dist/stamp/index.js.map +1 -0
  216. package/dist/stamp/read.d.ts +7 -0
  217. package/dist/stamp/read.d.ts.map +1 -0
  218. package/dist/stamp/read.js +62 -0
  219. package/dist/stamp/read.js.map +1 -0
  220. package/dist/stamp.d.ts +29 -0
  221. package/dist/stamp.d.ts.map +1 -0
  222. package/dist/stamp.js +198 -0
  223. package/dist/stamp.js.map +1 -0
  224. package/dist/templates/banner/badge.d.ts +7 -0
  225. package/dist/templates/banner/badge.d.ts.map +1 -0
  226. package/dist/templates/banner/badge.js +24 -0
  227. package/dist/templates/banner/badge.js.map +1 -0
  228. package/dist/templates/banner/index.d.ts +9 -0
  229. package/dist/templates/banner/index.d.ts.map +1 -0
  230. package/dist/templates/banner/index.js +66 -0
  231. package/dist/templates/banner/index.js.map +1 -0
  232. package/dist/templates/banner/lines.d.ts +27 -0
  233. package/dist/templates/banner/lines.d.ts.map +1 -0
  234. package/dist/templates/banner/lines.js +46 -0
  235. package/dist/templates/banner/lines.js.map +1 -0
  236. package/dist/templates/card/index.d.ts +7 -0
  237. package/dist/templates/card/index.d.ts.map +1 -0
  238. package/dist/templates/card/index.js +43 -0
  239. package/dist/templates/card/index.js.map +1 -0
  240. package/dist/templates/card/lines.d.ts +17 -0
  241. package/dist/templates/card/lines.d.ts.map +1 -0
  242. package/dist/templates/card/lines.js +32 -0
  243. package/dist/templates/card/lines.js.map +1 -0
  244. package/dist/templates/code/chrome.d.ts +9 -0
  245. package/dist/templates/code/chrome.d.ts.map +1 -0
  246. package/dist/templates/code/chrome.js +32 -0
  247. package/dist/templates/code/chrome.js.map +1 -0
  248. package/dist/templates/code/clip.d.ts +18 -0
  249. package/dist/templates/code/clip.d.ts.map +1 -0
  250. package/dist/templates/code/clip.js +44 -0
  251. package/dist/templates/code/clip.js.map +1 -0
  252. package/dist/templates/code/ellipsis.d.ts +3 -0
  253. package/dist/templates/code/ellipsis.d.ts.map +1 -0
  254. package/dist/templates/code/ellipsis.js +3 -0
  255. package/dist/templates/code/ellipsis.js.map +1 -0
  256. package/dist/templates/code/fit.d.ts +27 -0
  257. package/dist/templates/code/fit.d.ts.map +1 -0
  258. package/dist/templates/code/fit.js +59 -0
  259. package/dist/templates/code/fit.js.map +1 -0
  260. package/dist/templates/code/index.d.ts +12 -0
  261. package/dist/templates/code/index.d.ts.map +1 -0
  262. package/dist/templates/code/index.js +64 -0
  263. package/dist/templates/code/index.js.map +1 -0
  264. package/dist/templates/code/layout.d.ts +21 -0
  265. package/dist/templates/code/layout.d.ts.map +1 -0
  266. package/dist/templates/code/layout.js +21 -0
  267. package/dist/templates/code/layout.js.map +1 -0
  268. package/dist/templates/code/panel.d.ts +17 -0
  269. package/dist/templates/code/panel.d.ts.map +1 -0
  270. package/dist/templates/code/panel.js +20 -0
  271. package/dist/templates/code/panel.js.map +1 -0
  272. package/dist/templates/code/plate.d.ts +15 -0
  273. package/dist/templates/code/plate.d.ts.map +1 -0
  274. package/dist/templates/code/plate.js +32 -0
  275. package/dist/templates/code/plate.js.map +1 -0
  276. package/dist/templates/code/spans.d.ts +19 -0
  277. package/dist/templates/code/spans.d.ts.map +1 -0
  278. package/dist/templates/code/spans.js +36 -0
  279. package/dist/templates/code/spans.js.map +1 -0
  280. package/dist/templates/code/warn.d.ts +11 -0
  281. package/dist/templates/code/warn.d.ts.map +1 -0
  282. package/dist/templates/code/warn.js +24 -0
  283. package/dist/templates/code/warn.js.map +1 -0
  284. package/dist/templates/footer.d.ts +21 -0
  285. package/dist/templates/footer.d.ts.map +1 -0
  286. package/dist/templates/footer.js +25 -0
  287. package/dist/templates/footer.js.map +1 -0
  288. package/dist/templates/index.d.ts +3 -3
  289. package/dist/templates/index.d.ts.map +1 -1
  290. package/dist/templates/index.js +6 -6
  291. package/dist/templates/index.js.map +1 -1
  292. package/dist/text/element.d.ts +18 -0
  293. package/dist/text/element.d.ts.map +1 -0
  294. package/dist/text/element.js +19 -0
  295. package/dist/text/element.js.map +1 -0
  296. package/dist/text/escape.d.ts +5 -0
  297. package/dist/text/escape.d.ts.map +1 -0
  298. package/dist/text/escape.js +12 -0
  299. package/dist/text/escape.js.map +1 -0
  300. package/dist/text/index.d.ts +7 -0
  301. package/dist/text/index.d.ts.map +1 -0
  302. package/dist/text/index.js +5 -0
  303. package/dist/text/index.js.map +1 -0
  304. package/dist/text/stack.d.ts +24 -0
  305. package/dist/text/stack.d.ts.map +1 -0
  306. package/dist/text/stack.js +18 -0
  307. package/dist/text/stack.js.map +1 -0
  308. package/dist/text/wrap.d.ts +12 -0
  309. package/dist/text/wrap.d.ts.map +1 -0
  310. package/dist/text/wrap.js +33 -0
  311. package/dist/text/wrap.js.map +1 -0
  312. package/dist/types.d.ts +326 -2
  313. package/dist/types.d.ts.map +1 -1
  314. package/dist/validate/check.d.ts +13 -0
  315. package/dist/validate/check.d.ts.map +1 -0
  316. package/dist/validate/check.js +34 -0
  317. package/dist/validate/check.js.map +1 -0
  318. package/dist/validate/index.d.ts +15 -0
  319. package/dist/validate/index.d.ts.map +1 -0
  320. package/dist/validate/index.js +41 -0
  321. package/dist/validate/index.js.map +1 -0
  322. package/dist/validate/keys.d.ts +58 -0
  323. package/dist/validate/keys.d.ts.map +1 -0
  324. package/dist/validate/keys.js +166 -0
  325. package/dist/validate/keys.js.map +1 -0
  326. package/dist/validate/overrides.d.ts +5 -0
  327. package/dist/validate/overrides.d.ts.map +1 -0
  328. package/dist/validate/overrides.js +43 -0
  329. package/dist/validate/overrides.js.map +1 -0
  330. package/dist/validate/placement.d.ts +10 -0
  331. package/dist/validate/placement.d.ts.map +1 -0
  332. package/dist/validate/placement.js +38 -0
  333. package/dist/validate/placement.js.map +1 -0
  334. package/dist/validate/sizes.d.ts +9 -0
  335. package/dist/validate/sizes.d.ts.map +1 -0
  336. package/dist/validate/sizes.js +27 -0
  337. package/dist/validate/sizes.js.map +1 -0
  338. package/dist/validate/suggest.d.ts +10 -0
  339. package/dist/validate/suggest.d.ts.map +1 -0
  340. package/dist/validate/suggest.js +55 -0
  341. package/dist/validate/suggest.js.map +1 -0
  342. package/dist/validate/values.d.ts +21 -0
  343. package/dist/validate/values.d.ts.map +1 -0
  344. package/dist/validate/values.js +57 -0
  345. package/dist/validate/values.js.map +1 -0
  346. package/dist/validate.d.ts +15 -0
  347. package/dist/validate.d.ts.map +1 -0
  348. package/dist/validate.js +300 -0
  349. package/dist/validate.js.map +1 -0
  350. package/package.json +13 -6
package/README.md CHANGED
@@ -16,6 +16,8 @@ finished work.
16
16
  from config, not from any one site's stylesheet.
17
17
  - **Multiple sizes from one input** — a 1:1 square plus a 1.91:1 landscape by
18
18
  default, or whatever set you configure.
19
+ - **Manifest and meta tags** — a JSON record of what was generated, and the
20
+ Open Graph and Twitter tags that go with it.
19
21
  - **Small, reusable API** — a render core with no filesystem concerns, plus an
20
22
  optional content walker and CLI.
21
23
 
@@ -25,8 +27,10 @@ finished work.
25
27
  pnpm add @kensio/colophon
26
28
  ```
27
29
 
28
- `sharp` is a dependency and does the SVG → PNG rasterisation; `shiki` provides
29
- the grammars and themes for the `code` template.
30
+ `@resvg/resvg-js` is a dependency and does the SVG → PNG rasterisation; `shiki`
31
+ provides the grammars and themes for the `code` template. No headless browser is
32
+ involved, and fonts can be handed to the renderer as files (see
33
+ [Fonts](#fonts)) so a build renders the same image everywhere.
30
34
 
31
35
  ## Quick start (CLI)
32
36
 
@@ -64,19 +68,47 @@ colophon content --config colophon.config.ts
64
68
 
65
69
  For every file that declares `meta_img_props`, Colophon writes one PNG per
66
70
  output size next to it, named `<slug>-<size>.png`
67
- (`post/index.md` → `post/post-og.png` and `post/post-square.png`). Existing
68
- files are skipped unless you pass `--overwrite`.
71
+ (`post/index.md` → `post/post-og.png` and `post/post-square.png`).
69
72
 
70
73
  ```
71
74
  colophon [contentDir] [options]
72
75
 
73
- -c, --config <path> Config module whose default export is a ColophonConfig
74
- -o, --overwrite Re-render even if the output file already exists
76
+ -c, --config <path> Config module whose default export is a ColophonConfig,
77
+ or a function returning one
78
+ -f, --force Re-render every image, ignoring the stamps
79
+ -o, --overwrite Alias for --force
80
+ --concurrency <n> How many images to render at once
75
81
  -h, --help Show help
76
82
 
77
83
  contentDir defaults to "content"
84
+ --concurrency defaults to one per available CPU
78
85
  ```
79
86
 
87
+ Images are rendered a few at a time rather than all at once, so a tree of a few
88
+ hundred posts does not start a few hundred rasterisations and thrash. The
89
+ default — one per CPU the process can use — suits a build machine that has
90
+ nothing else to do; lower it with `--concurrency` to leave room for whatever
91
+ else is running.
92
+
93
+ ### Rebuilds
94
+
95
+ Every image Colophon writes carries a stamp — a hash of the props, the config
96
+ and the output size it was rendered from, stored in the PNG itself as a `tEXt`
97
+ chunk. On the next run an image whose stamp still matches is left alone, and
98
+ one whose title, colours, template or size has moved on is rendered again. So
99
+ correcting a single post's title re-renders that post's images and nothing
100
+ else.
101
+
102
+ There is no cache directory and nothing to keep in sync: delete an image and
103
+ its stamp goes with it. An image Colophon did not write, or one written by an
104
+ older version, has no stamp and is rendered over.
105
+
106
+ The stamp covers the Colophon version too, since the built-in templates ship
107
+ with it — upgrading the package re-renders the tree once. A custom template is
108
+ covered by its own source code, which means a template that reads something its
109
+ source does not name (a closed-over value, a file it loads itself) can change
110
+ without being noticed; `--force` is the way out.
111
+
80
112
  ## Programmatic use
81
113
 
82
114
  ### Render from props (core)
@@ -120,7 +152,9 @@ await generate({
120
152
  contentDir: "content",
121
153
  config: { colors: { brand: "#2563eb" } },
122
154
  overwrite: false,
155
+ concurrency: 4, // defaults to one per available CPU
123
156
  onResult: (result) =>
157
+ // result.url is where it is served, when the placement knows.
124
158
  console.log(`${result.skipped ? "skip" : "wrote"} ${result.outputPath}`),
125
159
  });
126
160
  ```
@@ -220,25 +254,125 @@ export default defineConfig({
220
254
 
221
255
  `charWidthRatio` is how the layout knows where each token sits, so it must match
222
256
  the font actually used — `0.6` suits most monospace faces (Source Code Pro,
223
- Menlo, DejaVu Sans Mono); Consolas wants about `0.55`. Fonts must be installed
224
- where `sharp` can see them; the default stack ends in the generic `monospace`
225
- family so it always resolves to something.
257
+ Menlo, DejaVu Sans Mono); Consolas wants about `0.55`. Give the monospace face
258
+ as a file under `fonts` so the ratio you set is the ratio you get; the default
259
+ stack ends in the generic `monospace` family so it resolves to something
260
+ whatever the machine has.
226
261
 
227
262
  ## Configuration
228
263
 
229
264
  All fields are optional; sensible defaults apply.
230
265
 
231
- | Option | Default | Notes |
232
- | ------------ | ------------------------------ | --------------------------------------------------- |
233
- | `colors` | neutral indigo/pink | `brand`, `brandDark`, `brandWarm`, `foreground`. |
234
- | `background` | gradient derived from `colors` | Or a `{ type: "solid" }` / custom gradient. |
235
- | `fontFamily` | `Arial, Helvetica, sans-serif` | Uses fonts available to `sharp`/librsvg. |
236
- | `footer` | none | Footer text; omit the field for none. |
237
- | `badge` | none | Corner badge for `banner`; omit the field for none. |
238
- | `code` | `github-dark`, monospace stack | Styling for the `code` template (see above). |
239
- | `onWarning` | `console.warn` | Where compromises are reported (see below). |
240
- | `sizes` | `og` + `square` | Named output sizes (see below). |
241
- | `templates` | `banner`, `card`, `code` | Merged over the built-ins. |
266
+ | Option | Default | Notes |
267
+ | ------------- | ------------------------------ | --------------------------------------------------- |
268
+ | `colors` | neutral indigo/pink | `brand`, `brandDark`, `brandWarm`, `foreground`. |
269
+ | `background` | gradient derived from `colors` | Or a `{ type: "solid" }` / custom gradient. |
270
+ | `fonts` | none | Font files to render with (see below). |
271
+ | `systemFonts` | `true` until `fonts` is set | Whether installed fonts are loaded too. |
272
+ | `fontFamily` | first font, else `Arial, …` | Font stack for template text. |
273
+ | `footer` | none | Footer text; omit the field for none. |
274
+ | `badge` | none | Corner badge for `banner`; omit the field for none. |
275
+ | `code` | `github-dark`, monospace stack | Styling for the `code` template (see above). |
276
+ | `onWarning` | `console.warn` | Where compromises are reported (see below). |
277
+ | `sizes` | `og` + `square` | Named output sizes, each able to override config. |
278
+ | `templates` | `banner`, `card`, `code` | Merged over the built-ins. |
279
+ | `content` | `meta_img_props`, `.md` files | How props are read from the tree (see below). |
280
+ | `placement` | `beside-content` | Where images go and what URL they get (see below). |
281
+ | `manifest` | none | Path to write a JSON manifest to (see below). |
282
+ | `extra` | none | One-off images not tied to a post (see below). |
283
+
284
+ ### A config that computes something
285
+
286
+ The default export can also be a function returning the config, async or not.
287
+ Some configs cannot be written as a literal — brand colours read out of the
288
+ site's own stylesheet, a version pulled from `package.json`, a footer that
289
+ names the current build — and without this such a project has to give up the
290
+ CLI and drive `generate` from a script of its own.
291
+
292
+ ```ts
293
+ // colophon.config.ts
294
+ import { readFile } from "node:fs/promises";
295
+
296
+ import { defineConfig } from "@kensio/colophon";
297
+
298
+ export default defineConfig(async () => {
299
+ const theme = JSON.parse(await readFile("src/theme.json", "utf8"));
300
+
301
+ return {
302
+ colors: { brand: theme.primary, brandDark: theme.primaryDark },
303
+ footer: "example.com",
304
+ };
305
+ });
306
+ ```
307
+
308
+ The function takes no arguments: everything it might be handed is either
309
+ already on the command line or a decision the config itself is making. It is
310
+ called once per run, before anything is walked or rendered, and what it returns
311
+ is the config — validated and stamped exactly as a literal one would be.
312
+
313
+ A module that exports neither is an error rather than a run with the defaults:
314
+ the command line asked for that file, so quietly rendering the whole tree
315
+ without it would be the worst of both.
316
+
317
+ ### Unknown options
318
+
319
+ The config is closed: an option Colophon does not recognise stops the build
320
+ rather than being ignored. A key nobody reads is otherwise a build that
321
+ succeeds and images that are wrong — the default sizes, the default colours,
322
+ and nothing in the log to say why.
323
+
324
+ ```
325
+ Unknown option "dimensions". Did you mean "sizes"?
326
+ ```
327
+
328
+ Where there is an obvious near miss it is named, including options that have
329
+ been renamed between versions; where there is not, the message lists what is
330
+ valid at that point in the config. Nested objects are checked too and named by
331
+ their path — `code.tabsize`, `sizes[1].heigth`, `background.stops[0].ofset` —
332
+ and everything wrong with a config is reported in one go rather than one run
333
+ at a time.
334
+
335
+ Two parts stay open on purpose: the names under `templates` are your own, and a
336
+ post's props are read by whichever template understands them.
337
+
338
+ ### Fonts
339
+
340
+ By default Colophon names font families and hopes the machine has them, which
341
+ is how the same post ends up rendering differently on a laptop, in CI and in a
342
+ container. Point `fonts` at font files instead and the output stops depending
343
+ on the machine:
344
+
345
+ ```ts
346
+ export default defineConfig({
347
+ fonts: [
348
+ { family: "Inter", path: "./fonts/Inter-Regular.ttf" },
349
+ { path: "./fonts/Inter-Bold.ttf" },
350
+ { path: "./fonts/JetBrainsMono-Regular.ttf" },
351
+ ],
352
+ code: { fontFamily: "JetBrains Mono" },
353
+ });
354
+ ```
355
+
356
+ - **One entry per file.** Weight and style are read from the font itself, so a
357
+ regular and a bold face are two entries and the template's `font-weight`
358
+ picks between them. Supply the bold face: templates ask for weights up to
359
+ `900` for titles and badges, and a missing weight is drawn with the face you
360
+ did supply rather than being synthesised into a fake bold.
361
+ - **`family` is optional** and doesn't affect matching — the family name inside
362
+ the file does that. Naming it on the first font saves setting `fontFamily`,
363
+ which otherwise stays on the default stack.
364
+ - **Paths are files**, `.ttf`, `.otf`, `.ttc` or `.otc`, resolved from the
365
+ working directory when relative. A path that isn't there is an error rather
366
+ than a silently blank image. To load a font you already have in memory —
367
+ fetched at build time, or bundled — pass `{ data }` with its bytes instead.
368
+ - **System fonts switch off** as soon as you configure any font, so a family
369
+ you didn't supply can't quietly resolve to something installed. Set
370
+ `systemFonts: true` to have both, at the cost of the determinism you came
371
+ for.
372
+
373
+ An unknown family falls back to a configured font rather than rendering
374
+ nothing, so a mismatched name shows up as the wrong typeface, not a blank
375
+ image.
242
376
 
243
377
  ### Warnings
244
378
 
@@ -285,12 +419,411 @@ frontmatter (SEO-friendly, keyword-rich), falling back to the file name — or t
285
419
  parent directory for `index.*` files. Point `slugField` at a different key, or
286
420
  override naming entirely with `generate`'s `outputPath` callback.
287
421
 
422
+ #### Slug strategies
423
+
424
+ Two ways to derive a slug from a path, for sites that address content
425
+ differently. `basename` is the default and unchanged:
426
+
427
+ | Path | `basename` | `route` |
428
+ | ----------------------- | ---------- | -------------- |
429
+ | `index.md` | `index` | `index` |
430
+ | `blog/my-post.md` | `my-post` | `blog/my-post` |
431
+ | `services/iam/index.md` | `iam` | `services/iam` |
432
+
433
+ `basename` suits Hugo-style page bundles, where the image belongs beside its
434
+ post. `route` suits a site addressed by route — a docs tree where
435
+ `services/iam/index.md` is served at `/services/iam` and wants an image named to
436
+ match:
437
+
438
+ ```ts
439
+ export default defineConfig({
440
+ content: { slugStrategy: "route" },
441
+ });
442
+ ```
443
+
444
+ A slug carrying directories is written from the **content root** rather than
445
+ beside the file, so `services/iam` becomes `content/services/iam-og.png`.
446
+ Resolving it beside the file would repeat the directories already in the slug.
447
+ A frontmatter `slug` still wins over either strategy.
448
+
449
+ ### Placement
450
+
451
+ `outputPath` says where the bytes go and nothing about how anyone reaches them,
452
+ so every site rebuilds that mapping in its own templates — from information
453
+ Colophon had while generating and threw away. A placement says both:
454
+
455
+ ```ts
456
+ export default defineConfig({
457
+ // Astro, Eleventy, Vite: one directory, served under one prefix.
458
+ placement: { strategy: "public-dir", dir: "public/og", urlBase: "/og" },
459
+ });
460
+ ```
461
+
462
+ ```text
463
+ wrote public/og/my-post-og.png -> /og/my-post-og.png
464
+ ```
465
+
466
+ | Strategy | Writes | Suits |
467
+ | ---------------- | ------------------------------------- | ----------------------- |
468
+ | `beside-content` | Next to the post, as it always has | Hugo-style page bundles |
469
+ | `public-dir` | Into `dir`, one directory for the lot | Astro, Eleventy, Vite |
470
+ | `custom` | Wherever `path` says | Anything else |
471
+
472
+ The URL comes from `urlBase`, prefixed to the image's path under whatever root
473
+ placed it. **No `urlBase`, no URL** — a directory on disk does not say how, or
474
+ whether, it is served, and a URL Colophon invented would be worse than the gap
475
+ it fills. It can be site-relative (`/og`) or absolute, for images on a CDN.
476
+
477
+ `custom` works both halves out itself, for a mapping that is nobody else's —
478
+ images under a dated directory, say:
479
+
480
+ ```ts
481
+ placement: {
482
+ strategy: "custom",
483
+ path: (file, size) => `public/og/2026/${file.slug}-${size.name}.png`,
484
+ url: (file, size) => `/og/2026/${file.slug}-${size.name}.png`,
485
+ }
486
+ ```
487
+
488
+ Each result carries the URL as `result.url`, `undefined` where nothing says:
489
+ no `urlBase`, an image placed by `generate`'s `outputPath` callback (which
490
+ still wins, and then the placement no longer describes where the file went), or
491
+ an `extra` that named its own path.
492
+
493
+ #### Content hashed filenames
494
+
495
+ Social platforms cache share images hard, and they cache by **URL**. Correct a
496
+ post's image and the old one can keep turning up in feeds for a long time
497
+ afterwards. A hash in the filename is the reliable way round it:
498
+
499
+ ```ts
500
+ placement: { strategy: "public-dir", dir: "public/og", urlBase: "/og", hash: true }
501
+ ```
502
+
503
+ ```text
504
+ wrote public/og/my-post-og.ecd0aab2.png -> /og/my-post-og.ecd0aab2.png
505
+ ```
506
+
507
+ Correct the post and the name moves with it, so the URL is one nothing has
508
+ cached:
509
+
510
+ ```text
511
+ wrote public/og/my-post-og.2e7bd5a9.png -> /og/my-post-og.2e7bd5a9.png
512
+ ```
513
+
514
+ The hash is the image's [rebuild stamp](#rebuilds) — its props, config and
515
+ size. Hashing the rendered bytes would be a truer name, but they are not known
516
+ until the image has been rendered, and not rendering the unchanged ones is the
517
+ point of the stamp. It follows that anything the stamp covers moves the name,
518
+ including a Colophon upgrade: the images are re-rendered by that anyway, and a
519
+ fresh URL is the correct answer for an image that may have changed.
520
+
521
+ It is opt-in because the filename then moves whenever the image does, which not
522
+ every setup wants, and because it leaves the old files behind. **Nothing deletes
523
+ them** — which is the point under a `public-dir` you can rebuild from scratch,
524
+ since a crawler holding the old URL still gets an image, but means a
525
+ `beside-content` tree slowly accumulates them in your content directory. The
526
+ manifest always names the current one, so a site never has to work out which is
527
+ which.
528
+
529
+ `custom` has no `hash` option: a placement naming its own paths is the one that
530
+ can hash them itself.
531
+
532
+ A flat placement makes filename collisions much easier to hit — two posts named
533
+ `intro.md` in different sections both want `public/og/intro-og.png`. Colophon
534
+ refuses the build and names both posts rather than letting one overwrite the
535
+ other, which would also leave the pair re-rendering on every build. Pair
536
+ `public-dir` with `slugStrategy: "route"` and each post keeps its section:
537
+
538
+ ```ts
539
+ export default defineConfig({
540
+ content: { slugStrategy: "route" },
541
+ placement: { strategy: "public-dir", dir: "public/og", urlBase: "/og" },
542
+ });
543
+ // public/og/blog/intro-og.png -> /og/blog/intro-og.png
544
+ ```
545
+
546
+ ### Manifest
547
+
548
+ Colophon writes PNGs and then goes quiet, so the site works out for itself what
549
+ was generated, where it lives and how big it is — globbing for `*-og.png` to
550
+ find the landscape variant, or hardcoding 1200 and 630 into its meta tags. All
551
+ of that is known while generating. Point `manifest` at a file and it is written
552
+ down:
553
+
554
+ ```ts
555
+ export default defineConfig({
556
+ placement: { strategy: "public-dir", dir: "public/og", urlBase: "/og" },
557
+ manifest: "data/colophon.json", // src/data/ for Astro, _data/ for Eleventy
558
+ });
559
+ ```
560
+
561
+ ```json
562
+ {
563
+ "version": 1,
564
+ "pages": {
565
+ "blog/my-post": {
566
+ "images": {
567
+ "og": {
568
+ "url": "/og/blog/my-post-og.png",
569
+ "width": 1200,
570
+ "height": 630
571
+ },
572
+ "square": {
573
+ "url": "/og/blog/my-post-square.png",
574
+ "width": 1200,
575
+ "height": 1200
576
+ }
577
+ },
578
+ "widest": "og",
579
+ "alt": "My post"
580
+ }
581
+ }
582
+ }
583
+ ```
584
+
585
+ Every generator in scope reads JSON as native data: Hugo picks it up from
586
+ `data/`, Astro imports it, Eleventy and Jekyll read `_data/`, Zola has
587
+ `load_data`. Which makes the meta tags a lookup rather than a convention:
588
+
589
+ ```ts
590
+ const page = manifest.pages["blog/my-post"];
591
+ const image = page.images[page.widest];
592
+ // <meta property="og:image" content={image.url}>
593
+ // <meta property="og:image:width" content={image.width}>
594
+ // <meta name="twitter:card" content={image.width / image.height > 1.5
595
+ // ? "summary_large_image" : "summary"}>
596
+ ```
597
+
598
+ - **Pages are keyed by slug** — what the site addresses a page by, which under
599
+ `slugStrategy: "route"` is the route itself. Two pages cannot share one, and
600
+ a build that would need them to fails saying so, since a lookup that returned
601
+ the wrong post's image is worse than no manifest at all.
602
+ - **`widest`** names the most landscape image by aspect ratio, ties going to
603
+ the size configured first. It is what a `summary_large_image` card wants, and
604
+ the check every site currently writes for itself. Note that og (1200×630) and
605
+ square (1200×1200) are equally _wide_, so comparing widths would pick either.
606
+ - **`url`** is absent where the placement knows none — see
607
+ [Placement](#placement). The dimensions are always there.
608
+ - **`alt`** comes from the props' title, and is absent for a page without one.
609
+ - **`extra` images are not pages**, so they are not listed. A project that named
610
+ the output path of one already knows where it is.
611
+
612
+ The manifest describes what exists, not what a given run did: a rebuild that
613
+ skips every image still writes the whole thing. Pages and sizes are sorted, so
614
+ a manifest committed to a repository changes only when the build does.
615
+
616
+ ### Social meta tags
617
+
618
+ Generating the image is half the job — the site still has to write the tags,
619
+ and every site ends up with slightly different results. Given the manifest,
620
+ that is a lookup:
621
+
622
+ ```ts
623
+ import { metaTags, metaTagsHtml } from "@kensio/colophon/meta";
624
+ import manifest from "./data/colophon.json";
625
+
626
+ const site = { baseUrl: "https://example.com" };
627
+
628
+ metaTagsHtml(manifest, "blog/my-post", site);
629
+ ```
630
+
631
+ ```html
632
+ <meta
633
+ property="og:image"
634
+ content="https://example.com/og/blog/my-post-og.png"
635
+ />
636
+ <meta property="og:image:width" content="1200" />
637
+ <meta property="og:image:height" content="630" />
638
+ <meta property="og:image:alt" content="My post" />
639
+ <meta name="twitter:card" content="summary_large_image" />
640
+ <meta
641
+ name="twitter:image"
642
+ content="https://example.com/og/blog/my-post-og.png"
643
+ />
644
+ <meta name="twitter:image:alt" content="My post" />
645
+ ```
646
+
647
+ `metaTags` returns the same set as objects, for a component that spreads them
648
+ — Open Graph names its tags with `property` and Twitter with `name`, so the
649
+ type keeps them apart and `<meta {...tag} />` is right either way:
650
+
651
+ ```jsx
652
+ {
653
+ metaTags(manifest, slug, site).map((tag) => <meta {...tag} />);
654
+ }
655
+ ```
656
+
657
+ - **The card type follows the image.** `summary_large_image` when the page's
658
+ widest image is at least 1.5:1 — which the 1.91:1 Open Graph landscape and
659
+ the 2:1 Twitter size both clear — and `summary` otherwise, since a square
660
+ shown as a large card is cropped. Sites usually hardcode whichever answer
661
+ suited the image they had.
662
+ - **`baseUrl` makes the URL absolute**, which Open Graph needs: a crawler reads
663
+ the tag out of the page and has nothing to resolve a relative URL against. A
664
+ URL that is already absolute, from a CDN `urlBase`, is left alone.
665
+ - **Alt text goes to both platforms.** Twitter reads `twitter:image:alt` rather
666
+ than falling back to the Open Graph one, so emitting only the latter means no
667
+ alt text where most of the sharing happens.
668
+ - **A page with no share image gets no tags** — an empty array, an empty string
669
+ — rather than an error. Not every page has one, and a template asking should
670
+ not have to know which in advance.
671
+
672
+ Import it from `@kensio/colophon/meta` rather than the package root: emitting
673
+ tags reads a JSON file, and a site's templates should not load a rasteriser and
674
+ a syntax highlighter to write a `<head>`.
675
+
676
+ ### Per-size config
677
+
678
+ Some settings only make sense per size. `code.minFontScale` is the clearest
679
+ case: a 1:1 square and a 1.91:1 landscape have very different amounts of
680
+ vertical room, so a snippet that fits one gets truncated in the other. A size
681
+ can carry its own overrides, applied only when rendering it:
682
+
683
+ ```ts
684
+ export default defineConfig({
685
+ colors: { brand: "#2563eb" },
686
+ code: { theme: "github-dark" },
687
+ sizes: [
688
+ SIZE_PRESETS.square,
689
+ { ...SIZE_PRESETS.og, code: { minFontScale: 0.013 } },
690
+ ],
691
+ });
692
+ ```
693
+
694
+ One pass over the content tree, one config file. The alternative is running
695
+ `generate` once per size with a different config each time, which re-walks and
696
+ re-parses everything for each.
697
+
698
+ Overridable: `colors`, `background`, `fontFamily`, `footer`, `badge`, `code` —
699
+ what a template reads while drawing. Not overridable: `fonts`, `systemFonts` and
700
+ `templates`, which are shared build inputs rather than part of the picture, and
701
+ `onWarning`, which is where messages go rather than what they say. A size naming
702
+ one of those is an unknown-option error, not a setting that quietly does
703
+ nothing. `fontFamily` is overridable because it picks from the fonts already
704
+ loaded; supplying different font _files_ per size is not the same thing.
705
+
706
+ `colors` and `code` **merge** over their config-level counterparts, so the
707
+ example above keeps `github-dark` and changes only the minimum font size. Any
708
+ single shade can be overridden on its own — `colors: { foreground: "#111827" }`
709
+ on one size keeps the brand palette and changes just the text colour. The
710
+ rest **replace**: a `background` is a union whose variants have different keys,
711
+ so merging half of one onto half of another would produce a background that is
712
+ neither, and `badge` carries a required `text` a partial override could not
713
+ supply.
714
+
715
+ Overrides are part of an image's rebuild stamp, so changing one re-renders that
716
+ size and leaves the others alone.
717
+
718
+ ### One-off images
719
+
720
+ Not every image belongs to a post. A package card, a repository social preview
721
+ and a home page share image all want the same brand and the same templates, and
722
+ none of them has a markdown file behind it. List them under `extra` and the
723
+ build renders them alongside the tree:
724
+
725
+ ```ts
726
+ export default defineConfig({
727
+ colors: { brand: "#2563eb" },
728
+ footer: "example.com",
729
+ extra: [
730
+ {
731
+ props: {
732
+ template: "banner",
733
+ title: "@kensio/colophon",
734
+ version: "2.0.0",
735
+ },
736
+ output: "public/npm-card.png",
737
+ },
738
+ {
739
+ props: {
740
+ template: "card",
741
+ title: "colophon",
742
+ subtitle: "social meta images",
743
+ },
744
+ output: "public/repo-preview.png",
745
+ size: {
746
+ name: "repo",
747
+ width: 1280,
748
+ height: 640,
749
+ footer: "github.com/KensioSoftware/colophon",
750
+ },
751
+ },
752
+ ],
753
+ });
754
+ ```
755
+
756
+ `output` is the path to write, relative to the working directory, and any
757
+ directories it names are created. It is the whole path: an extra image has no
758
+ post to sit beside, so `generate`'s `outputPath` callback is not consulted and
759
+ nothing is appended to the filename. An extra that would land on another image
760
+ in the same build stops it before anything is written — two images sharing a
761
+ path do not merely lose one of themselves, they each stamp the file and so
762
+ re-render on every build afterwards.
763
+
764
+ `size` is an output size like any other, [overrides](#per-size-config) and all —
765
+ that is how the preview above gets its own footer without adding an entry to
766
+ `sizes` that every post would then be rendered at. Leave it out and the image
767
+ takes the first configured size, which for the card above is the default `og`
768
+ 1200×630.
769
+
770
+ Extras are stamped and skipped exactly as content images are, so editing one
771
+ card's title re-renders that card and leaves the rest of the build alone. They
772
+ are reported by `onResult` too, with `contentPath` left `undefined`: there is no
773
+ post behind them to name.
774
+
288
775
  ### Frontmatter shape
289
776
 
290
777
  By default Colophon reads a `meta_img_props` object and a `template` field
291
- within it, plus a top-level `slug`. All are configurable via walk options
292
- (`propsKey`, `templateField`, `defaultTemplate`, `slugField`, `extensions`) so
293
- you can match an existing convention.
778
+ within it, plus a top-level `slug`. All are configurable under `content`
779
+ (`propsKey`, `templateField`, `defaultTemplate`, `slugField`, `slugStrategy`,
780
+ `extensions`) so you can match an existing convention.
781
+
782
+ ### Using the frontmatter you already have
783
+
784
+ A site with 200 existing posts gets no images until someone adds a props block
785
+ to 200 files. Most posts already carry the fields an image needs, just under
786
+ different names — so map them instead:
787
+
788
+ ```ts
789
+ export default defineConfig({
790
+ content: {
791
+ defaultTemplate: "banner",
792
+ props: (frontmatter) =>
793
+ frontmatter.draft === true
794
+ ? undefined
795
+ : { title: frontmatter.title, subtitle: frontmatter.description },
796
+ },
797
+ });
798
+ ```
799
+
800
+ Point it at your content directory and the site gets its images, without
801
+ editing a single post.
802
+
803
+ **Returning `undefined` skips a post.** That is the filter for drafts, section
804
+ indexes and anything else in the tree that is not a page worth sharing —
805
+ without it, mapping frontmatter means an image for every markdown file there
806
+ is.
807
+
808
+ **An explicit props block still wins, field by field.** A post that wants a
809
+ different subtitle writes just that:
810
+
811
+ ```yaml
812
+ ---
813
+ title: Colophon 2.3.0
814
+ description: Autogenerated release notes
815
+ meta_img_props:
816
+ subtitle: Per-size config, frontmatter mapping
817
+ ---
818
+ ```
819
+
820
+ The title still comes from the mapper; only the subtitle is overridden. A post
821
+ declaring a block is never skipped, even if the mapper would have skipped it —
822
+ asking for an image outright is the stronger signal.
823
+
824
+ `content` lives in the config module because a `props` mapper is a function and
825
+ cannot be passed as a CLI flag. `generate`'s `walk` option is the programmatic
826
+ equivalent and takes precedence where both are given.
294
827
 
295
828
  ## Sample output
296
829
 
@@ -350,7 +883,10 @@ Adding the `code` template made two small breaking changes:
350
883
  - `pnpm lint` — ESLint + Prettier check.
351
884
  - `pnpm fmt` — auto-fix.
352
885
  - `pnpm samples` — regenerate the README sample images into `docs/samples/`.
353
- - `pnpm check` — format, typecheck and test with coverage (run before committing).
886
+ - `pnpm fta` — [FTA](https://ftaproject.dev) maintainability scores for `src/`,
887
+ failing on any file that scores 50 or above.
888
+ - `pnpm check` — format, FTA, typecheck and test with coverage (run before
889
+ committing).
354
890
 
355
891
  ## License
356
892
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The parsed command line.
3
+ */
4
+ export interface CliArgs {
5
+ readonly contentDir: string;
6
+ readonly configPath: string | undefined;
7
+ readonly overwrite: boolean;
8
+ readonly concurrency: number | undefined;
9
+ }
10
+ /** Help text for `--help`, and for a run that asks for nothing else. */
11
+ export declare const usage = "colophon \u2014 generate social meta images from frontmatter\n\nUsage:\n colophon [contentDir] [options]\n\nImages carry a stamp of the props, config and size they came from, so a rebuild\nrenders only the ones that have actually changed.\n\nOptions:\n -c, --config <path> Load a config module. Its default export is either a\n ColophonConfig or a function (async or not) returning\n one, for a config that has to compute something.\n -f, --force Re-render every image, ignoring the stamps\n -o, --overwrite Alias for --force\n --concurrency <n> How many images to render at once\n -h, --help Show this help\n\nDefaults:\n contentDir content\n --concurrency one per available CPU";
12
+ /** Read the command line into the options `generate` takes. */
13
+ export declare function parseCliArgs(argv: readonly string[]): CliArgs;
14
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,wEAAwE;AACxE,eAAO,MAAM,KAAK,sxBAmB4B,CAAC;AAwB/C,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA4B7D"}