diff --git a/corpus/README.md b/corpus/README.md new file mode 100644 index 0000000..4122780 --- /dev/null +++ b/corpus/README.md @@ -0,0 +1,8 @@ +# The corpus + +`.json` is an ADF document; `.md` is the markdown `adfToMarkdown` must emit for it, +byte for byte including the trailing newline, and that `markdownToAdf` must read back to that same +document (AGENTS.md §2). Directories mirror `spec/flavour.md`'s sections. + +The JSON is editor-normal — empty `attrs`, `marks` and `content` as the absent key, adjacent +identical-mark text nodes merged — two-space indent, keys sorted. diff --git a/corpus/canonical-form/blockquote.json b/corpus/canonical-form/blockquote.json new file mode 100644 index 0000000..145989c --- /dev/null +++ b/corpus/canonical-form/blockquote.json @@ -0,0 +1,29 @@ +{ + "content": [ + { + "content": [ + { + "content": [ + { + "text": "Ship it.", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Then tell them.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "blockquote" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/blockquote.md b/corpus/canonical-form/blockquote.md new file mode 100644 index 0000000..f46663f --- /dev/null +++ b/corpus/canonical-form/blockquote.md @@ -0,0 +1,3 @@ +> Ship it. +> +> Then tell them. diff --git a/corpus/canonical-form/bullet-list.json b/corpus/canonical-form/bullet-list.json new file mode 100644 index 0000000..17d1b65 --- /dev/null +++ b/corpus/canonical-form/bullet-list.json @@ -0,0 +1,72 @@ +{ + "content": [ + { + "content": [ + { + "content": [ + { + "content": [ + { + "text": "Bolt M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + }, + { + "content": [ + { + "content": [ + { + "text": "Nut M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + }, + { + "content": [ + { + "content": [ + { + "text": "Washer M8", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "content": [ + { + "content": [ + { + "text": "Fibre", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + } + ], + "type": "bulletList" + } + ], + "type": "listItem" + } + ], + "type": "bulletList" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/bullet-list.md b/corpus/canonical-form/bullet-list.md new file mode 100644 index 0000000..51738d8 --- /dev/null +++ b/corpus/canonical-form/bullet-list.md @@ -0,0 +1,4 @@ +- Bolt M8 +- Nut M8 +- Washer M8 + - Fibre diff --git a/corpus/canonical-form/code-block-backtick-run.json b/corpus/canonical-form/code-block-backtick-run.json new file mode 100644 index 0000000..012ffd4 --- /dev/null +++ b/corpus/canonical-form/code-block-backtick-run.json @@ -0,0 +1,15 @@ +{ + "content": [ + { + "content": [ + { + "text": "```\ncode\n```", + "type": "text" + } + ], + "type": "codeBlock" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/code-block-backtick-run.md b/corpus/canonical-form/code-block-backtick-run.md new file mode 100644 index 0000000..436503d --- /dev/null +++ b/corpus/canonical-form/code-block-backtick-run.md @@ -0,0 +1,5 @@ +```` +``` +code +``` +```` diff --git a/corpus/canonical-form/code-block.json b/corpus/canonical-form/code-block.json new file mode 100644 index 0000000..e22ce1e --- /dev/null +++ b/corpus/canonical-form/code-block.json @@ -0,0 +1,18 @@ +{ + "content": [ + { + "attrs": { + "language": "sql" + }, + "content": [ + { + "text": "SELECT id\nFROM part\nWHERE qty > 0;", + "type": "text" + } + ], + "type": "codeBlock" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/code-block.md b/corpus/canonical-form/code-block.md new file mode 100644 index 0000000..1b22ef2 --- /dev/null +++ b/corpus/canonical-form/code-block.md @@ -0,0 +1,5 @@ +```sql +SELECT id +FROM part +WHERE qty > 0; +``` diff --git a/corpus/canonical-form/code-span.json b/corpus/canonical-form/code-span.json new file mode 100644 index 0000000..dfdda44 --- /dev/null +++ b/corpus/canonical-form/code-span.json @@ -0,0 +1,118 @@ +{ + "content": [ + { + "content": [ + { + "text": "Run ", + "type": "text" + }, + { + "marks": [ + { + "type": "code" + } + ], + "text": "npm ci", + "type": "text" + }, + { + "text": " first.", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Nested backticks: ", + "type": "text" + }, + { + "marks": [ + { + "type": "code" + } + ], + "text": "run `date` twice", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "A span holding ", + "type": "text" + }, + { + "marks": [ + { + "type": "code" + } + ], + "text": "`code`", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Held: ", + "type": "text" + }, + { + "marks": [ + { + "type": "code" + } + ], + "text": " spaced ", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Three: ", + "type": "text" + }, + { + "marks": [ + { + "type": "code" + } + ], + "text": " ", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Literal: ", + "type": "text" + }, + { + "marks": [ + { + "type": "code" + } + ], + "text": "~~not strike~~", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/code-span.md b/corpus/canonical-form/code-span.md new file mode 100644 index 0000000..274490e --- /dev/null +++ b/corpus/canonical-form/code-span.md @@ -0,0 +1,11 @@ +Run `npm ci` first. + +Nested backticks: ``run `date` twice`` + +A span holding `` `code` `` + +Held: ` spaced ` + +Three: ` ` + +Literal: `~~not strike~~` diff --git a/corpus/canonical-form/em-strong-strike.json b/corpus/canonical-form/em-strong-strike.json new file mode 100644 index 0000000..a185740 --- /dev/null +++ b/corpus/canonical-form/em-strong-strike.json @@ -0,0 +1,76 @@ +{ + "content": [ + { + "content": [ + { + "text": "Read the ", + "type": "text" + }, + { + "marks": [ + { + "type": "em" + } + ], + "text": "manual", + "type": "text" + }, + { + "text": " before ", + "type": "text" + }, + { + "marks": [ + { + "type": "strong" + } + ], + "text": "wiring", + "type": "text" + }, + { + "text": " the ", + "type": "text" + }, + { + "marks": [ + { + "type": "strike" + } + ], + "text": "relay", + "type": "text" + }, + { + "text": ".", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "An un", + "type": "text" + }, + { + "marks": [ + { + "type": "em" + } + ], + "text": "real", + "type": "text" + }, + { + "text": "istic goal.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/em-strong-strike.md b/corpus/canonical-form/em-strong-strike.md new file mode 100644 index 0000000..d14ffda --- /dev/null +++ b/corpus/canonical-form/em-strong-strike.md @@ -0,0 +1,3 @@ +Read the _manual_ before **wiring** the ~~relay~~. + +An un*real*istic goal. diff --git a/corpus/canonical-form/hard-break.json b/corpus/canonical-form/hard-break.json new file mode 100644 index 0000000..85f8cf6 --- /dev/null +++ b/corpus/canonical-form/hard-break.json @@ -0,0 +1,53 @@ +{ + "content": [ + { + "content": [ + { + "text": "Line one", + "type": "text" + }, + { + "type": "hardBreak" + }, + { + "text": "Line two", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Trailing", + "type": "text" + }, + { + "type": "hardBreak" + } + ], + "type": "paragraph" + }, + { + "attrs": { + "level": 2 + }, + "content": [ + { + "text": "Two", + "type": "text" + }, + { + "type": "hardBreak" + }, + { + "text": "lines", + "type": "text" + } + ], + "type": "heading" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/hard-break.md b/corpus/canonical-form/hard-break.md new file mode 100644 index 0000000..ebede47 --- /dev/null +++ b/corpus/canonical-form/hard-break.md @@ -0,0 +1,6 @@ +Line one\ +Line two + +Trailing:hardBreak{} + +## Two:hardBreak{}lines diff --git a/corpus/canonical-form/heading.json b/corpus/canonical-form/heading.json new file mode 100644 index 0000000..589c4b3 --- /dev/null +++ b/corpus/canonical-form/heading.json @@ -0,0 +1,78 @@ +{ + "content": [ + { + "attrs": { + "level": 1 + }, + "content": [ + { + "text": "Assembly", + "type": "text" + } + ], + "type": "heading" + }, + { + "attrs": { + "level": 2 + }, + "content": [ + { + "text": "Parts", + "type": "text" + } + ], + "type": "heading" + }, + { + "attrs": { + "level": 3 + }, + "content": [ + { + "text": "Fasteners", + "type": "text" + } + ], + "type": "heading" + }, + { + "attrs": { + "level": 4 + }, + "content": [ + { + "text": "Bolts", + "type": "text" + } + ], + "type": "heading" + }, + { + "attrs": { + "level": 5 + }, + "content": [ + { + "text": "Sizes", + "type": "text" + } + ], + "type": "heading" + }, + { + "attrs": { + "level": 6 + }, + "content": [ + { + "text": "M8", + "type": "text" + } + ], + "type": "heading" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/heading.md b/corpus/canonical-form/heading.md new file mode 100644 index 0000000..976188c --- /dev/null +++ b/corpus/canonical-form/heading.md @@ -0,0 +1,11 @@ +# Assembly + +## Parts + +### Fasteners + +#### Bolts + +##### Sizes + +###### M8 diff --git a/corpus/canonical-form/link.json b/corpus/canonical-form/link.json new file mode 100644 index 0000000..d50684b --- /dev/null +++ b/corpus/canonical-form/link.json @@ -0,0 +1,116 @@ +{ + "content": [ + { + "content": [ + { + "text": "See ", + "type": "text" + }, + { + "marks": [ + { + "attrs": { + "href": "https://example.com/changelog" + }, + "type": "link" + } + ], + "text": "the changelog", + "type": "text" + }, + { + "text": ".", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "marks": [ + { + "attrs": { + "href": "https://example.com/" + }, + "type": "link" + } + ], + "text": "https://example.com/", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Read ", + "type": "text" + }, + { + "marks": [ + { + "attrs": { + "href": "https://example.com/guide", + "title": "Setup guide" + }, + "type": "link" + } + ], + "text": "the guide", + "type": "text" + }, + { + "text": ".", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Open ", + "type": "text" + }, + { + "marks": [ + { + "attrs": { + "href": "https://example.com/the plan.pdf" + }, + "type": "link" + } + ], + "text": "the plan", + "type": "text" + }, + { + "text": ".", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "marks": [ + { + "attrs": { + "href": "/parts/m8" + }, + "type": "link" + } + ], + "text": "/parts/m8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/link.md b/corpus/canonical-form/link.md new file mode 100644 index 0000000..ab5d9b1 --- /dev/null +++ b/corpus/canonical-form/link.md @@ -0,0 +1,9 @@ +See [the changelog](https://example.com/changelog). + + + +Read [the guide](https://example.com/guide "Setup guide"). + +Open [the plan](). + +[/parts/m8](/parts/m8) diff --git a/corpus/canonical-form/ordered-list-order.json b/corpus/canonical-form/ordered-list-order.json new file mode 100644 index 0000000..c7d1fbe --- /dev/null +++ b/corpus/canonical-form/ordered-list-order.json @@ -0,0 +1,65 @@ +{ + "content": [ + { + "attrs": { + "order": 9 + }, + "content": [ + { + "content": [ + { + "content": [ + { + "text": "Bolt M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + }, + { + "content": [ + { + "content": [ + { + "text": "Nut M8", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Zinc-plated.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + }, + { + "content": [ + { + "content": [ + { + "text": "Washer M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + } + ], + "type": "orderedList" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/ordered-list-order.md b/corpus/canonical-form/ordered-list-order.md new file mode 100644 index 0000000..163e5a2 --- /dev/null +++ b/corpus/canonical-form/ordered-list-order.md @@ -0,0 +1,5 @@ +9. Bolt M8 +10. Nut M8 + + Zinc-plated. +11. Washer M8 diff --git a/corpus/canonical-form/ordered-list.json b/corpus/canonical-form/ordered-list.json new file mode 100644 index 0000000..a39528a --- /dev/null +++ b/corpus/canonical-form/ordered-list.json @@ -0,0 +1,53 @@ +{ + "content": [ + { + "content": [ + { + "content": [ + { + "content": [ + { + "text": "Bolt M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + }, + { + "content": [ + { + "content": [ + { + "text": "Nut M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + }, + { + "content": [ + { + "content": [ + { + "text": "Washer M8", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "listItem" + } + ], + "type": "orderedList" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/ordered-list.md b/corpus/canonical-form/ordered-list.md new file mode 100644 index 0000000..3e512e7 --- /dev/null +++ b/corpus/canonical-form/ordered-list.md @@ -0,0 +1,3 @@ +1. Bolt M8 +2. Nut M8 +3. Washer M8 diff --git a/corpus/canonical-form/paragraph-empty.json b/corpus/canonical-form/paragraph-empty.json new file mode 100644 index 0000000..f7ebf4b --- /dev/null +++ b/corpus/canonical-form/paragraph-empty.json @@ -0,0 +1,27 @@ +{ + "content": [ + { + "content": [ + { + "text": "First.", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "type": "paragraph" + }, + { + "content": [ + { + "text": "Second.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/paragraph-empty.md b/corpus/canonical-form/paragraph-empty.md new file mode 100644 index 0000000..9255212 --- /dev/null +++ b/corpus/canonical-form/paragraph-empty.md @@ -0,0 +1,5 @@ +First. + +::paragraph + +Second. diff --git a/corpus/canonical-form/paragraph.json b/corpus/canonical-form/paragraph.json new file mode 100644 index 0000000..dd8141f --- /dev/null +++ b/corpus/canonical-form/paragraph.json @@ -0,0 +1,24 @@ +{ + "content": [ + { + "content": [ + { + "text": "The converter emits every paragraph on a single line, however long it runs, because a soft line break in input carries no meaning ADF can hold.", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "Blocks are separated by exactly one blank line.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/paragraph.md b/corpus/canonical-form/paragraph.md new file mode 100644 index 0000000..3dd7081 --- /dev/null +++ b/corpus/canonical-form/paragraph.md @@ -0,0 +1,3 @@ +The converter emits every paragraph on a single line, however long it runs, because a soft line break in input carries no meaning ADF can hold. + +Blocks are separated by exactly one blank line. diff --git a/corpus/canonical-form/rule.json b/corpus/canonical-form/rule.json new file mode 100644 index 0000000..8caf8ab --- /dev/null +++ b/corpus/canonical-form/rule.json @@ -0,0 +1,27 @@ +{ + "content": [ + { + "content": [ + { + "text": "Before.", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "type": "rule" + }, + { + "content": [ + { + "text": "After.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/rule.md b/corpus/canonical-form/rule.md new file mode 100644 index 0000000..8c2fd78 --- /dev/null +++ b/corpus/canonical-form/rule.md @@ -0,0 +1,5 @@ +Before. + +--- + +After. diff --git a/corpus/canonical-form/text-escaping.json b/corpus/canonical-form/text-escaping.json new file mode 100644 index 0000000..1465d19 --- /dev/null +++ b/corpus/canonical-form/text-escaping.json @@ -0,0 +1,42 @@ +{ + "content": [ + { + "content": [ + { + "text": "# Not a heading", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "- not a bullet", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "2 * 3 * 4 = 24", + "type": "text" + } + ], + "type": "paragraph" + }, + { + "content": [ + { + "text": "snake_case_name", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/canonical-form/text-escaping.md b/corpus/canonical-form/text-escaping.md new file mode 100644 index 0000000..de71c9c --- /dev/null +++ b/corpus/canonical-form/text-escaping.md @@ -0,0 +1,7 @@ +\# Not a heading + +\- not a bullet + +2 * 3 * 4 = 24 + +snake_case_name diff --git a/todo.md b/todo.md index 88c719b..1e57210 100644 --- a/todo.md +++ b/todo.md @@ -21,7 +21,25 @@ detail is settled at its own milestone. payloads for external-URL support — if it exists, revisit the media section's mid-text-image error and its "no slot" ground. - [ ] **1d — Corpus start** (§10): checked-in ADF ↔ canonical-markdown fixture pairs per spec'd - node. + node, in `corpus/`, one directory per `spec/flavour.md` section. + - [ ] **1d1 — Canonical form**: the plain-CommonMark subset — blockquote, bulletList, + codeBlock, heading, orderedList, paragraph, rule, listItem, hardBreak, text, code spans, + and the `code`, `em`, `link`, `strike` and `strong` marks. + - [ ] **1d2 — Block nodes**: panel, expand/nestedExpand, the media family and the CommonMark + image shape, both table forms, task and decision lists, layout, extensions, syncBlock — + with the reserved `marks` attribute and the fence lengths nesting forces. + - [ ] **1d3 — Inline nodes and marks**: date, emoji, inlineCard, mediaInline, mention, status; + border, subsup, textColor, underline; the content slot's `text` attribute and the + `:text{text="…"}` whitespace spelling. + - [ ] **1d4 — Opaque carry** (§3): an unknown node in both positions, the reserved `adf` info + string, and the `codeBlock` whose language is `adf`. + - [ ] **1d5 — Carve-outs and combinations**: the three carve-outs and their escapes, mark + nesting order and the runs a carry breaks, attribute canonicalization, and documents + combining nodes rather than isolating one. + - [ ] **1d6 — Input normalization**: one-way markdown→ADF fixtures, not pairs — setext + headings, indented code, loose lists, `*`/`+` bullets, entity references, soft wraps. + - [ ] **1d7 — Error input**: also one-way, a markdown input per named error. Waits on milestone + 3 naming them; 1d's pairs are valid documents only. - [ ] **2 — `adfToMarkdown`.** First real code — decide here where §10's coverage check lives. - [ ] **3 — `markdownToAdf`.** The CommonMark parser is the largest single component. The raw-HTML element mapping is empty until milestone 6, so at `0.1.0` every raw-HTML construct in input