Programmatic use
The CLI is a thin wrapper over two entry points. Use them directly when a project needs to drive generation itself.
Render from props
Section titled “Render from props”The core takes props and config and returns rendered bytes. It touches no files and knows nothing about content trees:
import { renderMetaImages } from "@kensio/colophon";import { writeFile } from "node:fs/promises";
const images = await renderMetaImages( { template: "banner", title: "@kensio/colophon", subtitle: "Generate social meta images from frontmatter", version: "1.2.0", }, { colors: { brand: "#2563eb" }, footer: "example.com", badge: { text: "npm" }, },);
for (const image of images) { // image.name is the output-size name ("og", "square", and so on). await writeFile(`social-${image.name}.png`, image.bytes);}One image is returned per configured output size. Each carries its name,
dimensions, the source svg and the encoded bytes, in whatever
format the config asked for. extensionFor is
what a build names its own files with, if you would rather not hardcode .png.
This is the right entry point for rendering an image from data that is not a markdown file at all, such as a database row or an API response.
Walk content and generate
Section titled “Walk content and generate”generate ties walking, rendering and writing together. This is what the CLI
calls:
import { generate } from "@kensio/colophon";
await generate({ contentDir: "content", config: { colors: { brand: "#2563eb" } }, overwrite: false, dryRun: false, // work out what would change and write nothing concurrency: 4, // defaults to one per available CPU onResult: (result) => // result.url is where it is served, when the placement knows. console.log(`${result.skipped ? "skip" : "wrote"} ${result.outputPath}`),});onResult is called once per image, including the ones that were skipped
because their stamp still matched. A result for an
extra image has contentPath set to
undefined, since there is no post behind it.
Under dryRun the results say what a real build would have done: skipped is
true for an image whose stamp still matches, and false for one that would be
rendered. Nothing is written, not even the manifest, and every check a build
makes still runs. It is what the CLI’s --dry-run is.
Options that are not config
Section titled “Options that are not config”Two generate options deliberately live outside ColophonConfig:
concurrencyis a property of the machine doing the build rather than of the images. Putting it in config would drag it into the rebuild stamp, so changing it would re-render the tree. What a build reaches is capped by the libuv thread pool, which holds four threads unlessUV_THREADPOOL_SIZEwas set in the environment the process started in.generatewarns once where the concurrency is above it. The command line has the measurements.outputPathis a callback that decides where each image is written. It takes precedence overplacement, and when it is used there is no URL, because the placement no longer describes where the file went.
generate’s walk option is the programmatic equivalent of
config.content and wins where both are given.
Walking on its own
Section titled “Walking on its own”walkContent finds content files and reads their frontmatter, without rendering
anything:
import { walkContent } from "@kensio/colophon/content";
const files = await walkContent({ dir: "content" });Import it from the @kensio/colophon/content subpath when frontmatter discovery
is all you want. The root entry point pulls in the rasteriser and the syntax
highlighter, and this subpath does not.
readContentFile is the same work for one file, which is what
colophon preview uses. It takes the file, the content root its path
and slug are relative to, and the same options, and returns undefined where
the file asks for no image:
import { readContentFile } from "@kensio/colophon/content";
const file = await readContentFile("content/posts/hello.md", "content");Other exports
Section titled “Other exports”The pieces the above are built from are exported too, for anything that needs to
work at a lower level: buildSvg and renderSvgToImage for the two halves of
rendering, resolveConfig and resolveConfigForSize for config, the
layout toolkit and createMeasurer for template authors, and
createStamper, readImageStamp and stampImage for the rebuild stamps.
metaTags has its own @kensio/colophon/meta
subpath, which loads neither the rasteriser nor the highlighter. So does
the layout toolkit, as @kensio/colophon/layout, which loads
nothing from Node at all.
