From 440dab5430812eeea15327aa376206589f73609f Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Thu, 3 Sep 2026 19:33:25 +0200 Subject: [PATCH] 5b3: surface the refusal an inline directive body holds --- spec/flavour.md | 2 +- src/markdown/parse/markdown-to-adf.test.ts | 8 ++++++++ src/markdown/parse/markdown-to-adf.ts | 1 + src/markdown/pipe-table-syntax.ts | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/flavour.md b/spec/flavour.md index d7aa560..a4dd3a1 100644 --- a/spec/flavour.md +++ b/spec/flavour.md @@ -2,7 +2,7 @@ The grammar of the extended markdown `adfToMarkdown` emits and `markdownToAdf` parses. Plain CommonMark is a subset with three carve-outs: literal text that matches directive syntax below or -shapes a pipe table is claimed by the flavour, and a matched `~~` pair spells `strike` (escape the +reads as a pipe table is claimed by the flavour, and a matched `~~` pair spells `strike` (escape the `:`, `|` or `~` to keep it literal) โ€” and one gap: a CommonMark image fits only as its own title-less paragraph โ€” mid-text and titled images are named errors. The emitted form is contract (AGENTS.md ยง8). Per-node syntaxes build on this grammar in the sections below. diff --git a/src/markdown/parse/markdown-to-adf.test.ts b/src/markdown/parse/markdown-to-adf.test.ts index 75c5306..475c372 100644 --- a/src/markdown/parse/markdown-to-adf.test.ts +++ b/src/markdown/parse/markdown-to-adf.test.ts @@ -203,6 +203,14 @@ test('names the pipe table whose rows open with no pipe', () => { assert.deepEqual(path(markdownToAdf('Part.\n\na | b\n--- | ---\n')), ['content', 1]) }) +test('gives back the refusal an inline body holds, never the shape check above it', () => { + const bare = 'malformed-pipe-table: a pipe table opens every row with `|`: this one does not; \\| keeps a pipe literal text' + assert.equal(content(markdownToAdf(':::caption\na | b\n--- | ---\n:::\n')), bare) + assert.deepEqual(position(markdownToAdf(':::caption\na | b\n--- | ---\n:::\n')), { line: 2, offset: 11 }) + assert.equal(code(markdownToAdf(':::caption\n| a |\n:::\n')), 'malformed-pipe-table') + assert.equal(content(markdownToAdf(':::caption\n- a\n:::\n')), 'unsupported-node-shape: caption takes one paragraph as its body: this body is not one') +}) + test('reads the separator that parts two adjacent lists of one kind', () => { const parted = [bulletList(item(paragraph('a'))), bulletList(item(paragraph('b')))] assert.deepEqual(content(markdownToAdf('- a\n\n::listBreak\n\n- b\n')), parted) diff --git a/src/markdown/parse/markdown-to-adf.ts b/src/markdown/parse/markdown-to-adf.ts index 721a129..8f2461a 100644 --- a/src/markdown/parse/markdown-to-adf.ts +++ b/src/markdown/parse/markdown-to-adf.ts @@ -134,6 +134,7 @@ function tableNode(rows: readonly string[][], definitions: LinkDefinitions, path function inlineBodyNode(node: AdfNode, blocks: readonly Block[], definitions: LinkDefinitions, path: ConvertErrorPath): Result { if (blocks.length === 0) return failure('unsupported-node-shape', `an empty ${node.type} takes the leaf form, ::, never an empty container`, path) const only = blocks.length === 1 ? blocks[0] : undefined + if (only?.kind === 'fault') return positioned(faulted(only.fault, path), only.position) if (only?.kind !== 'paragraph') return failure('unsupported-node-shape', `${node.type} takes one paragraph as its body: this body is not one`, path) return positioned(contentNode(node, only.text, definitions, path), only.position) } diff --git a/src/markdown/pipe-table-syntax.ts b/src/markdown/pipe-table-syntax.ts index 026e33a..b8f365b 100644 --- a/src/markdown/pipe-table-syntax.ts +++ b/src/markdown/pipe-table-syntax.ts @@ -4,7 +4,7 @@ import { backslashEscape, claimsPipeLine, trimSpace } from './commonmark-grammar const alignmentCell = /^:-+:?$|^-+:$/ const delimiterCell = /^-+$/ -// spec/flavour.md, Tables: the cells of a row the leading `|` no claim read, GFM's form without it. +// spec/flavour.md, Tables: the cells of a row no leading `|` claimed โ€” GFM's form without the outer pipes. export function barePipeCells(line: string): string[] | undefined { const cells = splitPipeCells(line, 0) return cells.length > 1 ? cells : undefined