@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
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Social meta tags, built from the manifest a build wrote.
3
+ *
4
+ * Importable on its own as `@kensio/colophon/meta`: emitting tags reads a JSON
5
+ * file and needs none of the rendering machinery, and a site's templates
6
+ * should not load a rasteriser and a syntax highlighter to write a `<head>`.
7
+ */
8
+ export { metaTagsHtml } from "./html.js";
9
+ export { metaTags } from "./tags.js";
10
+ export { absoluteUrl } from "./url.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/meta/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Social meta tags, built from the manifest a build wrote.
3
+ *
4
+ * Importable on its own as `@kensio/colophon/meta`: emitting tags reads a JSON
5
+ * file and needs none of the rendering machinery, and a site's templates
6
+ * should not load a rasteriser and a syntax highlighter to write a `<head>`.
7
+ */
8
+ export { metaTagsHtml } from "./html.js";
9
+ export { metaTags } from "./tags.js";
10
+ export { absoluteUrl } from "./url.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/meta/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Manifest, MetaTag, MetaTagOptions } from "../types.js";
2
+ /**
3
+ * The social meta tags for one page of a {@link Manifest}.
4
+ *
5
+ * Generating the image is half the job: a site still has to write the tags,
6
+ * and every site ends up with slightly different results — a missing width, a
7
+ * relative URL a crawler cannot resolve, a large card promising an image that
8
+ * is square.
9
+ *
10
+ * A page the manifest does not have gets no tags rather than an error. Not
11
+ * every page has a share image — a props mapper returning `undefined` is how a
12
+ * site says so — and a template asking about one should not have to know in
13
+ * advance.
14
+ *
15
+ * Alt text is emitted for both platforms when the page has any. Twitter reads
16
+ * its own `twitter:image:alt` rather than falling back to the Open Graph one,
17
+ * so leaving it out would mean no alt text on the platform, which is most of
18
+ * the reason for carrying it this far.
19
+ */
20
+ export declare function metaTags(manifest: Manifest, slug: string, options?: MetaTagOptions): readonly MetaTag[];
21
+ //# sourceMappingURL=tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/meta/tags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAGR,OAAO,EACP,cAAc,EACf,MAAM,aAAa,CAAC;AA4CrB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,SAAS,OAAO,EAAE,CA2BpB"}
@@ -0,0 +1,72 @@
1
+ import { absoluteUrl } from "./url.js";
2
+ /**
3
+ * How landscape an image has to be for a large Twitter card.
4
+ *
5
+ * `summary_large_image` is specified for 2:1 and the Open Graph landscape is
6
+ * 1.91:1, so both clear this; a 1:1 square and a 4:3 do not, and are shown
7
+ * better by `summary` than by a large card that crops them. Choosing between
8
+ * the two is a check every site writes for itself, usually by hardcoding the
9
+ * answer for whichever image it happens to have.
10
+ */
11
+ const largeCardRatio = 1.5;
12
+ /** The image a page's tags describe: the most landscape one it has. */
13
+ function chooseImage(slug, page) {
14
+ const image = page.images[page.widest];
15
+ if (image === undefined) {
16
+ throw new Error(`The manifest page "${slug}" names "${page.widest}" as its widest` +
17
+ ` image but has no image by that name.`);
18
+ }
19
+ return image;
20
+ }
21
+ /** The URL to point at, or a complaint that there is none to point at. */
22
+ function imageUrl(slug, image, options) {
23
+ if (image.url === undefined) {
24
+ throw new Error(`The manifest has no URL for "${slug}", so there is nothing to point a` +
25
+ ` meta tag at. Set placement.urlBase, which is what records one.`);
26
+ }
27
+ return absoluteUrl(image.url, options?.baseUrl);
28
+ }
29
+ /**
30
+ * The social meta tags for one page of a {@link Manifest}.
31
+ *
32
+ * Generating the image is half the job: a site still has to write the tags,
33
+ * and every site ends up with slightly different results — a missing width, a
34
+ * relative URL a crawler cannot resolve, a large card promising an image that
35
+ * is square.
36
+ *
37
+ * A page the manifest does not have gets no tags rather than an error. Not
38
+ * every page has a share image — a props mapper returning `undefined` is how a
39
+ * site says so — and a template asking about one should not have to know in
40
+ * advance.
41
+ *
42
+ * Alt text is emitted for both platforms when the page has any. Twitter reads
43
+ * its own `twitter:image:alt` rather than falling back to the Open Graph one,
44
+ * so leaving it out would mean no alt text on the platform, which is most of
45
+ * the reason for carrying it this far.
46
+ */
47
+ export function metaTags(manifest, slug, options) {
48
+ const page = manifest.pages[slug];
49
+ if (page === undefined) {
50
+ return [];
51
+ }
52
+ const image = chooseImage(slug, page);
53
+ const url = imageUrl(slug, image, options);
54
+ const isLarge = image.width / image.height >= largeCardRatio;
55
+ return [
56
+ { property: "og:image", content: url },
57
+ { property: "og:image:width", content: String(image.width) },
58
+ { property: "og:image:height", content: String(image.height) },
59
+ ...(page.alt === undefined
60
+ ? []
61
+ : [{ property: "og:image:alt", content: page.alt }]),
62
+ {
63
+ name: "twitter:card",
64
+ content: isLarge ? "summary_large_image" : "summary",
65
+ },
66
+ { name: "twitter:image", content: url },
67
+ ...(page.alt === undefined
68
+ ? []
69
+ : [{ name: "twitter:image:alt", content: page.alt }]),
70
+ ];
71
+ }
72
+ //# sourceMappingURL=tags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/meta/tags.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,uEAAuE;AACvE,SAAS,WAAW,CAAC,IAAY,EAAE,IAAkB;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,YAAY,IAAI,CAAC,MAAM,iBAAiB;YAChE,uCAAuC,CAC1C,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,0EAA0E;AAC1E,SAAS,QAAQ,CACf,IAAY,EACZ,KAAoB,EACpB,OAAmC;IAEnC,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,mCAAmC;YACrE,iEAAiE,CACpE,CAAC;IACJ,CAAC;IAED,OAAO,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,QAAQ,CACtB,QAAkB,EAClB,IAAY,EACZ,OAAwB;IAExB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,cAAc,CAAC;IAE7D,OAAO;QACL,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE;QACtC,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;QAC5D,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE;QAC9D,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS;YACxB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACtD;YACE,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS;SACrD;QACD,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,EAAE;QACvC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS;YACxB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;KACxD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve an image URL against the site's base.
3
+ *
4
+ * Open Graph wants an absolute URL: a crawler reads the tag out of the page
5
+ * and has nothing to resolve a relative one against. A manifest holds whatever
6
+ * the placement's `urlBase` said, which is usually site-relative, so this is
7
+ * where the two halves meet.
8
+ *
9
+ * A URL that names its own scheme is returned untouched — a placement pointing
10
+ * at a CDN has said everything there is to say. A scheme-relative one (`//cdn`)
11
+ * has not: it is relative to the page's scheme, which is exactly what a crawler
12
+ * reading the tag elsewhere does not have, so it borrows the base's.
13
+ */
14
+ export declare function absoluteUrl(url: string, baseUrl: string | undefined): string;
15
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/meta/url.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAa5E"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A URL that names its own scheme. Schemes are case-insensitive, so `HTTPS://`
3
+ * is as absolute as `https://` and prefixing it would produce nonsense.
4
+ */
5
+ const schemed = /^[a-z][a-z\d+.-]*:\/\//i;
6
+ /** The scheme a base URL is served over, `https:` included. */
7
+ function schemeOf(url) {
8
+ return /^[a-z][a-z\d+.-]*:/i.exec(url)?.[0];
9
+ }
10
+ /**
11
+ * Resolve an image URL against the site's base.
12
+ *
13
+ * Open Graph wants an absolute URL: a crawler reads the tag out of the page
14
+ * and has nothing to resolve a relative one against. A manifest holds whatever
15
+ * the placement's `urlBase` said, which is usually site-relative, so this is
16
+ * where the two halves meet.
17
+ *
18
+ * A URL that names its own scheme is returned untouched — a placement pointing
19
+ * at a CDN has said everything there is to say. A scheme-relative one (`//cdn`)
20
+ * has not: it is relative to the page's scheme, which is exactly what a crawler
21
+ * reading the tag elsewhere does not have, so it borrows the base's.
22
+ */
23
+ export function absoluteUrl(url, baseUrl) {
24
+ if (baseUrl === undefined || schemed.test(url)) {
25
+ return url;
26
+ }
27
+ if (url.startsWith("//")) {
28
+ const scheme = schemeOf(baseUrl);
29
+ return scheme === undefined ? url : `${scheme}${url}`;
30
+ }
31
+ const base = baseUrl.replace(/\/+$/, "");
32
+ return url.startsWith("/") ? `${base}${url}` : `${base}/${url}`;
33
+ }
34
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/meta/url.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,GAAG,yBAAyB,CAAC;AAE1C,+DAA+D;AAC/D,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,OAA2B;IAClE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;IACxD,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEzC,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;AAClE,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Placement } from "../types.js";
2
+ /**
3
+ * Reject a placement that cannot place anything.
4
+ *
5
+ * The types require all of this, but a CLI user's config module is plain
6
+ * JavaScript with nothing checking it, and none of these omissions fails
7
+ * anywhere near the config: a missing `dir` surfaces as a complaint about an
8
+ * argument to `path`, and the rest as something not being a function. Key
9
+ * validation is no help either — it is the option that is not there.
10
+ *
11
+ * This sits here rather than in `validate/`, which checks keys and the names
12
+ * of closed sets, for the reason `fonts/resolve.ts` does: what a value has to
13
+ * be is known by whatever is about to use it.
14
+ */
15
+ export declare function assertPlacement(placement: Placement): void;
16
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/placement/check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CA+C1D"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Reject a placement that cannot place anything.
3
+ *
4
+ * The types require all of this, but a CLI user's config module is plain
5
+ * JavaScript with nothing checking it, and none of these omissions fails
6
+ * anywhere near the config: a missing `dir` surfaces as a complaint about an
7
+ * argument to `path`, and the rest as something not being a function. Key
8
+ * validation is no help either — it is the option that is not there.
9
+ *
10
+ * This sits here rather than in `validate/`, which checks keys and the names
11
+ * of closed sets, for the reason `fonts/resolve.ts` does: what a value has to
12
+ * be is known by whatever is about to use it.
13
+ */
14
+ export function assertPlacement(placement) {
15
+ const declared = placement;
16
+ if (declared.urlBase !== undefined && typeof declared.urlBase !== "string") {
17
+ throw new TypeError(`placement urlBase must be a string; it is prefixed to each image's path.`);
18
+ }
19
+ if (declared.hash !== undefined && typeof declared.hash !== "boolean") {
20
+ throw new TypeError(`placement hash must be true or false. Anything else reads as neither,` +
21
+ ` so the images would be written unhashed without a word about it.`);
22
+ }
23
+ if (placement.strategy === "public-dir") {
24
+ if (typeof declared.dir !== "string" || declared.dir === "") {
25
+ throw new TypeError(`placement "public-dir" needs a "dir" to write the images into.`);
26
+ }
27
+ return;
28
+ }
29
+ if (placement.strategy !== "custom") {
30
+ return;
31
+ }
32
+ if (typeof declared.path !== "function") {
33
+ throw new TypeError(`placement "custom" needs a "path" function saying where each image goes.`);
34
+ }
35
+ if (declared.url !== undefined && typeof declared.url !== "function") {
36
+ throw new TypeError(`placement "custom" takes a "url" function, or none at all for images` +
37
+ ` that are written but not served.`);
38
+ }
39
+ }
40
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/placement/check.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,SAAoB;IAClD,MAAM,QAAQ,GAAG,SAMhB,CAAC;IAEF,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC3E,MAAM,IAAI,SAAS,CACjB,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACtE,MAAM,IAAI,SAAS,CACjB,uEAAuE;YACrE,mEAAmE,CACtE,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QACxC,IAAI,OAAO,QAAQ,CAAC,GAAG,KAAK,QAAQ,IAAI,QAAQ,CAAC,GAAG,KAAK,EAAE,EAAE,CAAC;YAC5D,MAAM,IAAI,SAAS,CACjB,gEAAgE,CACjE,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO;IACT,CAAC;IAED,IAAI,OAAO,QAAQ,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACxC,MAAM,IAAI,SAAS,CACjB,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CACjB,sEAAsE;YACpE,mCAAmC,CACtC,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { ContentFile, OutputSize, Placement } from "../types.js";
2
+ export { besideContent, imageName } from "./relative.js";
3
+ /**
4
+ * Where one image ends up: the path its bytes are written to, and the URL it
5
+ * is served at, if the placement knows one.
6
+ */
7
+ export interface PlacedImage {
8
+ readonly path: string;
9
+ readonly url: string | undefined;
10
+ }
11
+ /**
12
+ * Places each image of a build.
13
+ *
14
+ * The stamp is handed over whether or not the placement wants it: it is what a
15
+ * hashed filename is built from, and a placement that does not hash simply
16
+ * ignores it.
17
+ */
18
+ export type Placer = (file: ContentFile, size: OutputSize, stamp: string) => PlacedImage;
19
+ /**
20
+ * Build the function that places every image of a build.
21
+ *
22
+ * The strategies differ only in the root an image is placed under and whether
23
+ * it keeps the directories its post sits in: `beside-content` keeps them,
24
+ * which is the page-bundle convention, and `public-dir` drops them so that a
25
+ * flat directory can be served as one. Either way the same relative path
26
+ * makes the disk path and the URL, so the two cannot drift apart.
27
+ */
28
+ export declare function createPlacer(placement: Placement | undefined, contentDir: string): Placer;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/placement/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAItE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,MAAM,GAAG,CACnB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,KACV,WAAW,CAAC;AAyBjB;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,UAAU,EAAE,MAAM,GACjB,MAAM,CAkCR"}
@@ -0,0 +1,61 @@
1
+ import path from "node:path";
2
+ import { assertPlacement } from "./check.js";
3
+ import { besideContent, imageName } from "./relative.js";
4
+ export { besideContent, imageName } from "./relative.js";
5
+ /**
6
+ * Prefix a `urlBase` onto an image's path under the root that placed it.
7
+ *
8
+ * No base, no URL: a directory on disk does not say how — or whether — it is
9
+ * served, and a URL Colophon invented would be worse than the gap it fills,
10
+ * since the whole point of writing one down is that a site can trust it.
11
+ */
12
+ function toUrl(urlBase, relative) {
13
+ if (urlBase === undefined) {
14
+ return undefined;
15
+ }
16
+ return `${urlBase.replace(/\/+$/, "")}/${relative}`;
17
+ }
18
+ /** Join a path that uses URL separators back into a platform path. */
19
+ function under(root, relative) {
20
+ return path.join(root, ...relative.split("/"));
21
+ }
22
+ /**
23
+ * Build the function that places every image of a build.
24
+ *
25
+ * The strategies differ only in the root an image is placed under and whether
26
+ * it keeps the directories its post sits in: `beside-content` keeps them,
27
+ * which is the page-bundle convention, and `public-dir` drops them so that a
28
+ * flat directory can be served as one. Either way the same relative path
29
+ * makes the disk path and the URL, so the two cannot drift apart.
30
+ */
31
+ export function createPlacer(placement, contentDir) {
32
+ if (placement !== undefined) {
33
+ assertPlacement(placement);
34
+ }
35
+ if (placement === undefined || placement.strategy === "beside-content") {
36
+ const urlBase = placement?.urlBase;
37
+ const isHashes = placement?.hash === true;
38
+ return (file, size, stamp) => {
39
+ const relative = besideContent(file, size, isHashes ? stamp : undefined);
40
+ return {
41
+ path: under(contentDir, relative),
42
+ url: toUrl(urlBase, relative),
43
+ };
44
+ };
45
+ }
46
+ if (placement.strategy === "public-dir") {
47
+ const isHashes = placement.hash === true;
48
+ return (file, size, stamp) => {
49
+ const relative = imageName(file, size, isHashes ? stamp : undefined);
50
+ return {
51
+ path: under(placement.dir, relative),
52
+ url: toUrl(placement.urlBase, relative),
53
+ };
54
+ };
55
+ }
56
+ return (file, size) => ({
57
+ path: placement.path(file, size),
58
+ url: placement.url?.(file, size),
59
+ });
60
+ }
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/placement/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAwBzD;;;;;;GAMG;AACH,SAAS,KAAK,CACZ,OAA2B,EAC3B,QAAgB;IAEhB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;AACtD,CAAC;AAED,sEAAsE;AACtE,SAAS,KAAK,CAAC,IAAY,EAAE,QAAgB;IAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAgC,EAChC,UAAkB;IAElB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,eAAe,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QACvE,MAAM,OAAO,GAAG,SAAS,EAAE,OAAO,CAAC;QACnC,MAAM,QAAQ,GAAG,SAAS,EAAE,IAAI,KAAK,IAAI,CAAC;QAE1C,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAC3B,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACzE,OAAO;gBACL,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC;gBACjC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC;aAC9B,CAAC;QACJ,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC;QAEzC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAC3B,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACrE,OAAO;gBACL,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC;gBACpC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC;aACxC,CAAC;QACJ,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACtB,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;QAChC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;KACjC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { ContentFile, OutputSize } from "../types.js";
2
+ /**
3
+ * The filename one image is written under: `<slug>-<size>.png`, or
4
+ * `<slug>-<size>.<hash>.png` where the placement asks for a hash. The slug
5
+ * carries the post's keywords, and the size name keeps every image of a post
6
+ * distinct (`my-post-og.png`, `my-post-square.png`).
7
+ *
8
+ * A slug carrying directories of its own — what the `route` strategy produces,
9
+ * and what a frontmatter `slug` of `docs/intro` means — puts them in the name,
10
+ * so the image's path mirrors the route wherever it is placed.
11
+ *
12
+ * The digest is the image's rebuild stamp: the props, the config and the size
13
+ * it is drawn from. Hashing the rendered bytes would be a truer name, but they
14
+ * are not known until the image has been rendered, and the point of the stamp
15
+ * is not rendering the ones that have not changed.
16
+ */
17
+ export declare function imageName(file: ContentFile, size: OutputSize, hash: string | undefined): string;
18
+ /**
19
+ * Where an image goes relative to the content root, in URL form.
20
+ *
21
+ * Slashes rather than the platform's separator, because this is also what a
22
+ * URL is built from: a Windows build must not serve `posts\my-post\a.png`.
23
+ * The disk path is joined back up per platform by whoever places it.
24
+ *
25
+ * A slug with directories of its own is placed from the content root, so its
26
+ * path mirrors the route. Resolving it beside the file would repeat the
27
+ * directories already in the slug, putting `services/iam` under
28
+ * `content/services/iam/`.
29
+ */
30
+ export declare function besideContent(file: ContentFile, size: OutputSize, hash: string | undefined): string;
31
+ //# sourceMappingURL=relative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relative.d.ts","sourceRoot":"","sources":["../../src/placement/relative.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAW3D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CAMR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CAYR"}
@@ -0,0 +1,53 @@
1
+ import path from "node:path";
2
+ /**
3
+ * How much of an image's digest goes in its filename.
4
+ *
5
+ * Eight hex characters is what the asset pipelines settle on, and it is the
6
+ * length at which a site would need tens of thousands of images before two of
7
+ * them were likely to collide.
8
+ */
9
+ const hashLength = 8;
10
+ /**
11
+ * The filename one image is written under: `<slug>-<size>.png`, or
12
+ * `<slug>-<size>.<hash>.png` where the placement asks for a hash. The slug
13
+ * carries the post's keywords, and the size name keeps every image of a post
14
+ * distinct (`my-post-og.png`, `my-post-square.png`).
15
+ *
16
+ * A slug carrying directories of its own — what the `route` strategy produces,
17
+ * and what a frontmatter `slug` of `docs/intro` means — puts them in the name,
18
+ * so the image's path mirrors the route wherever it is placed.
19
+ *
20
+ * The digest is the image's rebuild stamp: the props, the config and the size
21
+ * it is drawn from. Hashing the rendered bytes would be a truer name, but they
22
+ * are not known until the image has been rendered, and the point of the stamp
23
+ * is not rendering the ones that have not changed.
24
+ */
25
+ export function imageName(file, size, hash) {
26
+ const stem = `${file.slug}-${size.name}`;
27
+ return hash === undefined
28
+ ? `${stem}.png`
29
+ : `${stem}.${hash.slice(0, hashLength)}.png`;
30
+ }
31
+ /**
32
+ * Where an image goes relative to the content root, in URL form.
33
+ *
34
+ * Slashes rather than the platform's separator, because this is also what a
35
+ * URL is built from: a Windows build must not serve `posts\my-post\a.png`.
36
+ * The disk path is joined back up per platform by whoever places it.
37
+ *
38
+ * A slug with directories of its own is placed from the content root, so its
39
+ * path mirrors the route. Resolving it beside the file would repeat the
40
+ * directories already in the slug, putting `services/iam` under
41
+ * `content/services/iam/`.
42
+ */
43
+ export function besideContent(file, size, hash) {
44
+ const name = imageName(file, size, hash);
45
+ if (file.slug.includes("/")) {
46
+ return name;
47
+ }
48
+ const directory = path.dirname(file.contentPath);
49
+ return directory === "." || directory === ""
50
+ ? name
51
+ : `${directory.split(path.sep).join("/")}/${name}`;
52
+ }
53
+ //# sourceMappingURL=relative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relative.js","sourceRoot":"","sources":["../../src/placement/relative.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CAAC,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CACvB,IAAiB,EACjB,IAAgB,EAChB,IAAwB;IAExB,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;IAEzC,OAAO,IAAI,KAAK,SAAS;QACvB,CAAC,CAAC,GAAG,IAAI,MAAM;QACf,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAiB,EACjB,IAAgB,EAChB,IAAwB;IAExB,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAEzC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAEjD,OAAO,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,EAAE;QAC1C,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;AACvD,CAAC"}
package/dist/pool.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Map over `items` with at most `limit` calls to `worker` in flight at once,
3
+ * returning the results in input order.
4
+ *
5
+ * `Promise.all` over a mapped array starts everything at once, which for a
6
+ * few hundred images means a few hundred rasterisations queued on the thread
7
+ * pool with every pending bitmap held in memory. This starts `limit` workers
8
+ * instead and lets each pull the next item as it finishes, so the work in
9
+ * flight stays bounded however large the input is.
10
+ *
11
+ * The first rejection propagates, as it would from `Promise.all`. Items
12
+ * already in flight run to completion — there is no cancelling a render — but
13
+ * no further ones are started.
14
+ *
15
+ * Throws if `limit` is not a positive integer.
16
+ */
17
+ export declare function mapConcurrent<Item, Result>(items: readonly Item[], limit: number, worker: (item: Item, index: number) => Promise<Result>): Promise<Result[]>;
18
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAC9C,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACrD,OAAO,CAAC,MAAM,EAAE,CAAC,CA8CnB"}
package/dist/pool.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Map over `items` with at most `limit` calls to `worker` in flight at once,
3
+ * returning the results in input order.
4
+ *
5
+ * `Promise.all` over a mapped array starts everything at once, which for a
6
+ * few hundred images means a few hundred rasterisations queued on the thread
7
+ * pool with every pending bitmap held in memory. This starts `limit` workers
8
+ * instead and lets each pull the next item as it finishes, so the work in
9
+ * flight stays bounded however large the input is.
10
+ *
11
+ * The first rejection propagates, as it would from `Promise.all`. Items
12
+ * already in flight run to completion — there is no cancelling a render — but
13
+ * no further ones are started.
14
+ *
15
+ * Throws if `limit` is not a positive integer.
16
+ */
17
+ export async function mapConcurrent(items, limit, worker) {
18
+ // A limit below one starts no workers at all, and an empty `Promise.all`
19
+ // resolves happily — so the caller would get an empty result back as though
20
+ // there had been nothing to do. A build reporting success having rendered
21
+ // nothing is the failure worth being loud about.
22
+ if (!Number.isSafeInteger(limit) || limit < 1) {
23
+ throw new Error(`Invalid concurrency limit ${String(limit)}; expected a positive integer.`);
24
+ }
25
+ // Assigned by index rather than pushed, so results come back in input order
26
+ // however the workers interleave.
27
+ const results = [];
28
+ // One shared iterator is the queue: each worker pulls the next entry when it
29
+ // is free, so a slow item holds up only itself rather than a fixed share of
30
+ // the work.
31
+ const queue = items.entries();
32
+ let isStopped = false;
33
+ async function run() {
34
+ for (const [index, item] of queue) {
35
+ if (isStopped) {
36
+ return;
37
+ }
38
+ // Awaiting in the loop is the whole point: a worker takes one item at a
39
+ // time, and the parallelism comes from there being several of them.
40
+ // eslint-disable-next-line no-await-in-loop
41
+ results[index] = await worker(item, index);
42
+ }
43
+ }
44
+ const workers = Array.from({ length: Math.min(limit, items.length) }, () => run());
45
+ try {
46
+ await Promise.all(workers);
47
+ }
48
+ catch (error) {
49
+ // The run is already lost, so stop feeding it: a build that fails on the
50
+ // first file should not render the rest of the tree before it exits.
51
+ isStopped = true;
52
+ throw error;
53
+ }
54
+ return results;
55
+ }
56
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAsB,EACtB,KAAa,EACb,MAAsD;IAEtD,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,iDAAiD;IACjD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,6BAA6B,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAC3E,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,kCAAkC;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,6EAA6E;IAC7E,4EAA4E;IAC5E,YAAY;IACZ,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9B,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,KAAK,UAAU,GAAG;QAChB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;YAClC,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO;YACT,CAAC;YACD,wEAAwE;YACxE,oEAAoE;YACpE,4CAA4C;YAC5C,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CACzE,GAAG,EAAE,CACN,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yEAAyE;QACzE,qEAAqE;QACrE,SAAS,GAAG,IAAI,CAAC;QACjB,MAAM,KAAK,CAAC;IACd,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ColophonConfig, MetaImageProps, RenderedMetaImage } from "../types.js";
2
+ export { buildSvg } from "./svg.js";
3
+ export { renderSvgToPng } from "./png.js";
4
+ /**
5
+ * Render an image for every configured output size from a single set of props.
6
+ * This is the reusable core: it takes props and config and returns rendered
7
+ * bytes, with no filesystem or content-discovery concerns.
8
+ */
9
+ export declare function renderMetaImages(props: MetaImageProps, config?: ColophonConfig): Promise<RenderedMetaImage[]>;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,cAAc,EACrB,MAAM,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiB9B"}
@@ -0,0 +1,27 @@
1
+ import { resolveConfig } from "../config/index.js";
2
+ import { resolveConfigForSize } from "../config/size.js";
3
+ import { renderSvgToPng } from "./png.js";
4
+ import { buildSvg } from "./svg.js";
5
+ import { selectTemplate } from "./template.js";
6
+ export { buildSvg } from "./svg.js";
7
+ export { renderSvgToPng } from "./png.js";
8
+ /**
9
+ * Render an image for every configured output size from a single set of props.
10
+ * This is the reusable core: it takes props and config and returns rendered
11
+ * bytes, with no filesystem or content-discovery concerns.
12
+ */
13
+ export async function renderMetaImages(props, config) {
14
+ const resolved = resolveConfig(config);
15
+ // Validate the template once up front so a bad name fails fast.
16
+ selectTemplate(resolved, props.template);
17
+ return Promise.all(resolved.sizes.map(async (size) => {
18
+ // Each size renders with its own overrides folded in, so one size wanting
19
+ // a smaller minimum font does not mean a second pass over everything.
20
+ const sizeConfig = resolveConfigForSize(config, size);
21
+ const dimensions = { width: size.width, height: size.height };
22
+ const svg = await buildSvg(props, sizeConfig, dimensions);
23
+ const png = await renderSvgToPng(svg, dimensions, sizeConfig);
24
+ return { name: size.name, dimensions, svg, png };
25
+ }));
26
+ }
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAMzD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAqB,EACrB,MAAuB;IAEvB,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAEvC,gEAAgE;IAChE,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEzC,OAAO,OAAO,CAAC,GAAG,CAChB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAChC,0EAA0E;QAC1E,sEAAsE;QACtE,MAAM,UAAU,GAAG,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACtD,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAC9D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAC9D,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACnD,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Dimensions, ResolvedConfig } from "../types.js";
2
+ /**
3
+ * Rasterise an SVG string to a PNG buffer, scaled to `dimensions.width`.
4
+ *
5
+ * Text is drawn with the fonts named by `config`, which is what keeps the
6
+ * output the same on a laptop, in CI and in a container. Height follows the
7
+ * SVG's own aspect ratio; `buildSvg` sizes it to match.
8
+ */
9
+ export declare function renderSvgToPng(svg: string, dimensions: Dimensions, config?: ResolvedConfig): Promise<Buffer>;
10
+ //# sourceMappingURL=png.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../../src/render/png.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAkB9D;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,UAAU,EACtB,MAAM,GAAE,cAAgC,GACvC,OAAO,CAAC,MAAM,CAAC,CAOjB"}