slackmark
v0.6.2
Published
Compile SlackMark (CommonMark and GFM plus Slack extensions) to Slack Block Kit JSON
Maintainers
Readme
slackmark
SlackMark is the Markdown dialect this compiler understands: CommonMark → GFM → SlackMark. It adds Mermaid charts, math, admonitions, task lists, and mentions to GFM, then compiles the result to Slack Block Kit JSON. Compilation is rich_text-first, capability-gated, and reports structured degradations.
Need a React preview? react-blockkit
renders any Block Kit JSON and pairs perfectly with slackmark.
Features
- SlackMark → Block Kit (
rich_text,header,table,data_table,markdown,data_visualization,container, …) - Capability profiles (
latest,conservative, dated presets) - Ordered fallback chains with
Degradationrecords (never silent) - Message budgets (50 blocks, table/markdown/chart caps)
- Mermaid
pie/xychart-beta→ native charts; other mermaid diagrams → configuredrenderersor preformatted fallback - Display math and non-mermaid code fences → configured
renderersor preformatted/code text - OOP + constructor DI;
convert()convenience for simple use
Quickstart
pnpm add slackmarkimport { convert } from "slackmark";
const { blocks, text, degradations, receipts } = await convert("# Hello **Slack**", {
timeoutMs: 5_000,
});Advanced (inject fakes / custom adapters):
import { createConverter } from "slackmark";
const converter = createConverter({
capabilities: "2026-03",
renderers: [myRenderer],
uploader: myUploader,
});
const result = await converter.convert(markdown);API
| Export | Role |
| --- | --- |
| convert / convertToMessages | Convenience functions (default wiring per call) |
| createConverter | Composition root |
| SlackMarkConverter | Stateless converter class |
| NodeAdapter / Candidate | Extension seams |
| ImageRenderer / SlackUploader | Renderer and upload seams |
| validateBlocks | Limit validator |
Result shape: { blocks, text, degradations, receipts }. Receipts report external upload
effects separately from content fallbacks.
Compat profiles
| Preset | Flags |
| --- | --- |
| latest (default) | all |
| conservative | all flags off |
| 2025-02 | + markdownBlock |
| 2025-08 | + tableBlock |
| 2026-03 | + preformattedLanguage, headerLevel |
| 2026-05 | + dataTable |
| 2026-06 | + dataVisualization, containerBlock |
Pass a preset name, a flags object, and/or capabilityOverrides.
Renderers & uploads
Built-in adapters cover the mapping matrix. Prepend custom NodeAdapters via createConverter({ adapters }).
No renderer or uploader is enabled by default, so default conversion performs no network I/O. Mermaid fences consult config.renderers after native pie / xychart-beta candidates. Display math and generic code fences consult the same ordered renderer list before their preformatted fallback. Each renderer's canRender method decides which normalized languages it accepts.
Hosted URL renderers compose durable public HTTPS PNG URLs (no network I/O in core); they return null when Slack's 3,000-character image_url limit would be exceeded. Renderer, uploader, and capability-check failures remain scoped to one Markdown node. Attempt failures are buffered while later renderers run: a later success emits no degradation, while an all-failed chain records structured details before text fallback.
Import first-party URL adapters from the dedicated subpath:
import { createConverter } from "slackmark";
import { KrokiRenderer, MermaidInkRenderer } from "slackmark/renderers";
const converter = createConverter({
renderers: [new MermaidInkRenderer()],
// or: renderers: [new KrokiRenderer()],
});MermaidInkRendereremits pako-state PNG URLs for mermaid fences with a non-transparent background by default.KrokiRendererhandles Mermaid and supported diagram fence languages such asd2,dot, andplantuml.CodeCogsRendererhandles display math andkatex,latex,math, ortexfences.QuickChartRendererhandleschart,chart.js,chartjs, andquickchartfences.
For local Mermaid and KaTeX PNGs in a Node bot, install slackmark-node and use
its resource-owning helper. See the
slackmark-node README and the
Rasterize content guide,
whose lifecycle example preserves both the primary error and a close error.
It reuses one lazy Chromium instance; the uploader lifetime and credentials stay
separate. slackmark-node requires Node 22.12+; core remains universal.
Hosted services receive the diagram source encoded in the URL when Slack fetches the image. Treat them as explicit data-egress opt-ins. Each adapter accepts baseUrl for a self-hosted deployment:
const privateRenderers = [
new KrokiRenderer({ baseUrl: "https://kroki.internal.example" }),
new MermaidInkRenderer({ baseUrl: "https://mermaid.internal.example" }),
];Bytes results use a SlackUploader. A missing uploader is deferred while later renderers can recover, then becomes a terminal ConfigurationError only if no renderer succeeds. Upload effects are returned separately in immutable receipts, including effects from failed attempts. The fetch implementation uses Slack's external upload flow:
import { createConverter } from "slackmark";
import { FetchSlackUploader } from "slackmark/slack";
const uploader = new FetchSlackUploader({
token: slackBotToken,
});
const converter = createConverter({
renderers: [localBytesRenderer],
uploader,
});Inject fetch, sleep, and clock functions for deterministic tests. The Slack token needs files:write. Optional files.info processing polling additionally needs files:read; explicit channelId sharing can create a separate file-share message.
Raw Slack entities
Markdown that already contains Slack mrkdwn tokens — <@U…>, <#C…>, <!here>,
<!subteam^S…>, <!date^…> — converts to native rich text elements (no resolvers
required). HTML-entity-escaped forms such as <@U123> convert the same way:
remark decodes character references before slackmark sees the text. To keep the
syntax literal, wrap it in a code span (`<@U123>`).
Optional remark plugins
remark-math and remark-frontmatter are dependencies; enable with enableMath / enableFrontmatter. Display math is parsed when enabled, then offered to configured renderers before preformatted fallback. Single-dollar inline math is a separate opt-in (enableInlineMath): agent prose is far likelier to contain $200/mo than $x$, and reading the span between two dollar signs as math deletes both signs. When it is opted into, inline math stays code-styled text.
License
MIT © Siraj
