mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Compare the package with the other ADF converters in the README
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
- `losslessMarkdownToAdf` names the CommonMark spelling to write when it refuses a directive whose
|
||||
node CommonMark spells, such as `[](href)` for a linked `!adf:mediaSingle`.
|
||||
|
||||
- The README compares the package with seven other ADF converters, each checked against its
|
||||
published version.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- **Breaking:** each call names the markdown it reads or writes. `adfToMarkdown` and `markdownToAdf`
|
||||
|
||||
@@ -146,6 +146,7 @@ Nothing throws. `code` is from a closed list, so a `switch` with no `default` co
|
||||
|
||||
- [Goals](#goals)
|
||||
- [Audience](#audience)
|
||||
- [Other ADF converters](#other-adf-converters)
|
||||
- [The calls](#the-calls)
|
||||
- [The lossless flavour](#the-lossless-flavour)
|
||||
- [Portable markdown](#portable-markdown)
|
||||
@@ -191,6 +192,24 @@ Behind those apps, the people who read and write the markdown, and later the HTM
|
||||
managers, engineers and support agents working in Atlassian products through a plugin or another
|
||||
UI. They know markdown and not ADF, and rely on every spelling saying what it means to them.
|
||||
|
||||
## Other ADF converters
|
||||
|
||||
Serves [Goals](#goals) 3. Each package at the version named, checked 2026-10-14 by running its
|
||||
published code on one document holding marks, a link, a mention, a status, a panel and a list, and
|
||||
on one holding a node type it cannot know:
|
||||
[`converter-comparison/`](https://github.com/larvit/adf-codec/tree/main/converter-comparison).
|
||||
|
||||
| Package | Converts | ADF to markdown to ADF | A node type it does not know | Formats | Runtime dependencies |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `@larvit/adf-codec` 0.2.0 | both ways | the same document | kept | markdown: lossless, portable, CommonMark | 0 |
|
||||
| `@atlaskit/editor-markdown-transformer` 6.3.1 | markdown to ADF, through `@atlaskit/editor-json-transformer`; ADF to markdown throws "not implemented" | – | – | markdown | 5 |
|
||||
| `adf-to-markdown` 1.0.1 | ADF to markdown | – | dropped with its text | markdown | 0 |
|
||||
| `adf-to-md` 1.2.1 | ADF to markdown | – | dropped with its text, as are mentions, statuses and panels | markdown | 1 |
|
||||
| `adf2markdown` 1.2.0 | ADF to markdown | – | its text kept | markdown | 0 |
|
||||
| `extended-markdown-adf-parser` 2.4.0 | both ways | a different document: the mention comes back as linked text, the status without its `localId` | written as JSON, read back as a paragraph of that JSON's text | markdown | 9 |
|
||||
| `marklassian` 1.2.1 | markdown to ADF | – | – | markdown | 1 |
|
||||
| `md-to-adf` 0.6.4 | markdown to ADF | – | – | markdown | 2 |
|
||||
|
||||
## The calls
|
||||
|
||||
Serves [Goals](#goals) 1, 2 and 8. Pure functions, each taking a whole document and returning a
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { isDeepStrictEqual } from "node:util";
|
||||
import * as atlaskitJson from "@atlaskit/editor-json-transformer";
|
||||
import * as atlaskitMarkdown from "@atlaskit/editor-markdown-transformer";
|
||||
import * as adfCodec from "@larvit/adf-codec";
|
||||
import * as adfToMarkdown from "adf-to-markdown";
|
||||
import adfToMd from "adf-to-md";
|
||||
import * as adf2markdown from "adf2markdown";
|
||||
import * as extendedParser from "extended-markdown-adf-parser";
|
||||
import * as marklassian from "marklassian";
|
||||
import mdToAdf from "md-to-adf";
|
||||
|
||||
const { defaultSchema } = createRequire(import.meta.url)("@atlaskit/adf-schema/schema-default");
|
||||
|
||||
const roundTripDoc = {
|
||||
type: "doc", version: 1, content: [
|
||||
{ type: "paragraph", content: [
|
||||
{ type: "text", text: "bold italic", marks: [{ type: "strong" }, { type: "em" }] },
|
||||
{ type: "text", text: " and " },
|
||||
{ type: "text", text: "a link", marks: [{ type: "link", attrs: { href: "https://example.com" } }] },
|
||||
{ type: "text", text: " hi " },
|
||||
{ type: "mention", attrs: { id: "abc", text: "@Ann" } },
|
||||
{ type: "text", text: " state " },
|
||||
{ type: "status", attrs: { color: "green", localId: "s1", text: "DONE" } },
|
||||
] },
|
||||
{ type: "panel", attrs: { panelType: "info" }, content: [{ type: "paragraph", content: [{ type: "text", text: "note" }] }] },
|
||||
{ type: "bulletList", content: [
|
||||
{ type: "listItem", content: [{ type: "paragraph", content: [{ type: "text", text: "one" }] }] },
|
||||
{ type: "listItem", content: [{ type: "paragraph", content: [{ type: "text", text: "two" }] }] },
|
||||
] },
|
||||
],
|
||||
};
|
||||
const unknownNodeDoc = {
|
||||
type: "doc", version: 1, content: [
|
||||
{ type: "paragraph", content: [{ type: "text", text: "before" }] },
|
||||
{ type: "futureThing", attrs: { x: 1 }, content: [{ type: "paragraph", content: [{ type: "text", text: "inside" }] }] },
|
||||
{ type: "paragraph", content: [{ type: "text", text: "after" }] },
|
||||
],
|
||||
};
|
||||
const markdown = "***bold italic*** and [a link](https://example.com) hi @Ann\n\n> note\n\n- one\n- two\n";
|
||||
|
||||
const extended = new extendedParser.Parser();
|
||||
const json = new atlaskitJson.JSONTransformer();
|
||||
const atlaskit = new atlaskitMarkdown.MarkdownTransformer(defaultSchema);
|
||||
const converters = {
|
||||
"@atlaskit/editor-markdown-transformer": {
|
||||
toAdf: (md) => json.encode(atlaskit.parse(md)),
|
||||
toMarkdown: (doc) => atlaskit.encode(json.parse(doc)),
|
||||
},
|
||||
"@larvit/adf-codec": {
|
||||
toAdf: (md) => adfCodec.losslessMarkdownToAdf(md).value,
|
||||
toMarkdown: (doc) => adfCodec.adfToLosslessMarkdown(doc).value,
|
||||
},
|
||||
"adf-to-markdown": { toMarkdown: (doc) => adfToMarkdown.convertADFToMarkdown(doc) },
|
||||
"adf-to-md": { toMarkdown: (doc) => adfToMd.convert(doc).result },
|
||||
adf2markdown: { toMarkdown: (doc) => adf2markdown.adf2markdown(doc) },
|
||||
"extended-markdown-adf-parser": {
|
||||
toAdf: (md) => extended.markdownToAdf(md),
|
||||
toMarkdown: (doc) => extended.adfToMarkdown(doc),
|
||||
},
|
||||
marklassian: { toAdf: (md) => marklassian.markdownToAdf(md) },
|
||||
"md-to-adf": { toAdf: (md) => mdToAdf(md) },
|
||||
};
|
||||
|
||||
const show = (label, call) => {
|
||||
try {
|
||||
const value = call();
|
||||
console.log(`${label}:`, JSON.stringify(value));
|
||||
return value;
|
||||
} catch (error) {
|
||||
console.log(`${label} threw:`, error.message);
|
||||
}
|
||||
};
|
||||
for (const [name, { toAdf, toMarkdown }] of Object.entries(converters)) {
|
||||
const manifest = JSON.parse(readFileSync(`node_modules/${name}/package.json`, "utf8"));
|
||||
console.log(`\n## ${name} ${manifest.version}`);
|
||||
console.log("runtime dependencies:", Object.keys(manifest.dependencies ?? {}).length);
|
||||
const written = toMarkdown && show("ADF to markdown", () => toMarkdown(roundTripDoc));
|
||||
if (written !== undefined) {
|
||||
if (toAdf) show("round-trip deep-equal", () => isDeepStrictEqual(toAdf(written), roundTripDoc));
|
||||
const unknown = show("unknown node to markdown", () => toMarkdown(unknownNodeDoc));
|
||||
if (toAdf && unknown !== undefined) show("unknown node read back", () => toAdf(unknown));
|
||||
}
|
||||
if (toAdf) show("markdown to ADF", () => toAdf(markdown));
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
# Re-checks the README's comparison table against each package's published version.
|
||||
set -eu
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
docker run --rm --ulimit core=0 -u "$(id -u):$(id -g)" -e HOME=/tmp -v "$here:/comparison:ro" -w /tmp \
|
||||
node:24.21.0-alpine3.24 sh -c '
|
||||
npm install --ignore-scripts --no-audit --no-fund --silent \
|
||||
@atlaskit/editor-json-transformer@10.0.0 \
|
||||
@atlaskit/editor-markdown-transformer@6.3.1 \
|
||||
@larvit/adf-codec@0.2.0 \
|
||||
adf-to-markdown@1.0.1 \
|
||||
adf-to-md@1.2.1 \
|
||||
adf2markdown@1.2.0 \
|
||||
extended-markdown-adf-parser@2.4.0 \
|
||||
marklassian@1.2.1 \
|
||||
md-to-adf@0.6.4 &&
|
||||
cp /comparison/compare.mjs . && node compare.mjs'
|
||||
@@ -26,7 +26,6 @@
|
||||
| ID | Release | Exempt | Item | R | S | C | A | G | Goals | Score |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 73 | 0.3.0 | | **Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.** | 1 | 2 | 1 | 6 | 8 | 3 | 23.0 |
|
||||
| 71 | 0.3.0 | | **Add a README table comparing the package with the other ADF converters, each cell checked against that package's own docs or behaviour.** | 1 | 3 | 2 | 6 | 7 | 3 | 21.2 |
|
||||
| 114 | 0.3.0 | defect | **Build code-marked text holding no mark besides `link` and `annotation`, in every reader, and have `adfToLosslessMarkdown` carry code text holding any other mark.** | 3 | 4 | 5 | 5 | 7 | 4 | 15.4 |
|
||||
| 99 | 0.3.0 | defect | **Write a linked external image standing inline or in a table cell in the spelling a reader panel picks, in `adfToPortableMarkdown`.** | 3 | 3 | 5 | 6 | 7 | 7 | 12.9 |
|
||||
| 100 | 0.3.0 | decision | **Put the reading of an image's title as its caption back to a reader panel, now that a writer panel reads a title as hover text.** | 3 | 3 | 3 | 4 | 5 | 4, 6 | 10.9 |
|
||||
@@ -71,20 +70,9 @@
|
||||
|
||||
### 73. Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.
|
||||
|
||||
Lands after item 71, so each post links the GitHub repository, the README's comparison table and a
|
||||
README that opens with what the package is. We draft each post for the place it goes; the maintainer
|
||||
posts them once `0.3.0` is published. Found while planning discoverability, 2026-10-04.
|
||||
|
||||
### 71. Add a README table comparing the package with the other ADF converters, each cell checked against that package's own docs or behaviour.
|
||||
|
||||
Lands before items 7 and 46, which add HTML and a size column to the table. Its columns: which
|
||||
directions each converts, whether the round-trip holds, what an unknown node does, the formats and
|
||||
runtime dependencies. Candidates are the packages an npm search for ADF and markdown returns. Every
|
||||
cell cites that package's README or a run against its published version, and the table names each
|
||||
version and the date it was checked. Found while planning discoverability, 2026-10-04. Checked
|
||||
2026-10-05: no other package converts HTML or holds the round-trip; the candidates are
|
||||
`@atlaskit/editor-markdown-transformer`, `adf-to-markdown`, `adf-to-md`, `adf2markdown`,
|
||||
`extended-markdown-adf-parser`, `marklassian` and `md-to-adf`.
|
||||
Each post links the GitHub repository, the README's comparison table and a README that opens with
|
||||
what the package is. We draft each post for the place it goes; the maintainer posts them once
|
||||
`0.3.0` is published. Found while planning discoverability, 2026-10-04.
|
||||
|
||||
### 114. Build code-marked text holding no mark besides `link` and `annotation`, in every reader, and have `adfToLosslessMarkdown` carry code text holding any other mark.
|
||||
|
||||
@@ -185,7 +173,7 @@ on such a document. Found while shipping item 106, 2026-10-11.
|
||||
|
||||
Lands after items 6, 59, 60 and 62. Which flavours get a composed call is this item's to settle; the
|
||||
CommonMark spec suite also runs against the one reading CommonMark. The README documents HTML as it
|
||||
documents markdown, its comparison table (item 71) names HTML, and its tagline and `package.json`'s
|
||||
documents markdown, its comparison table names HTML, and its tagline and `package.json`'s
|
||||
description and keywords name HTML again. `htmlToAdf` refuses a value that is not a string as
|
||||
`not-a-string` (`docs/decisions.md` §Which code a cause takes).
|
||||
|
||||
@@ -266,7 +254,7 @@ tarball `npm pack` produces, its unpacked `dist`, and the built JavaScript minif
|
||||
figure the competitors advertise (marklassian's "12kb") and the only apples-to-apples one, since
|
||||
ours ships tsc's unminified output and no minifier yet (decide here whether to minify for the build
|
||||
or report the unminified gzip). The figure lands in README §The package beside the "no runtime
|
||||
dependencies" claim, and in a size column of the comparison table (item 71). Measured today,
|
||||
dependencies" claim, and in a size column of the comparison table. Measured today,
|
||||
unminified: tarball 60.4 kB, unpacked 221.5 kB, JS gzipped 45.6 kB.
|
||||
|
||||
### 72. Re-create `gitea.larvit.se/larvit/adf-codec` as a read-only pull mirror of GitHub, its issues and Actions off and its description pointing at GitHub.
|
||||
|
||||
Reference in New Issue
Block a user