mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 19:58:28 +01:00
Keep the hard breaks a link text holds alone, after its destination, and move item 89 to 0.2.1
This commit is contained in:
+1
-1
@@ -72,7 +72,7 @@
|
||||
([V8 bug](https://issues.chromium.org/issues/521080746)).
|
||||
- **Breaking:** a hard break inside emphasis, strike, a link or a directive mark reads holding no
|
||||
marks, as Atlassian's schema requires, where every reader gave it the marks around it. A link
|
||||
whose text is only hard breaks reads as an empty link text does, as its destination linked, and
|
||||
whose text is only hard breaks reads as its destination linked, followed by those breaks, and
|
||||
`losslessMarkdownToAdf` refuses a directive mark wrapping only hard breaks as
|
||||
`unsupported-node-shape`. `adfToLosslessMarkdown` writes a hard break holding marks as
|
||||
`!adf:carry{json="…"}`, and both writers spell an emphasis or link across the hard breaks inside
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ Markdown the spelling table leaves alone, which `0.2.0` reads as a different doc
|
||||
| --- | --- | --- |
|
||||
| a link whose text already holds one (`[a<https://example.com/>b](/v)`) | marks every node the inner link does not, splitting the outer link around it | leaves the outer brackets literal text; write the pieces as separate links to keep them |
|
||||
| markdown holding no block (`""`) | `{ type: 'doc', version: 1 }` | `{ content: [], type: 'doc', version: 1 }`; `!adf:doc {content=none}` reads as the former |
|
||||
| a hard break inside a mark's spelling (`*a\` then `b*` on the next line) | the hard break holds the mark | the hard break holds no marks, as Atlassian's schema requires, and the recipe above drops them from stored markdown. A link whose text is only hard breaks reads as its destination linked |
|
||||
| a hard break inside a mark's spelling (`*a\` then `b*` on the next line) | the hard break holds the mark | the hard break holds no marks, as Atlassian's schema requires, and the recipe above drops them from stored markdown. A link whose text is only hard breaks reads as its destination linked, followed by those breaks |
|
||||
| a link whose text is empty (`[](/url)`, `[]()`) | literal text | `/url` linked to `/url`, and nothing where the destination is empty too |
|
||||
| a code fence whose info string opens `adf:` (```` ```adf:x ````) | a `codeBlock` with that language | in `losslessMarkdownToAdf`, the block carry, refusing a body that is not one node's canonical JSON; write `!adf:codeBlock {language="adf:x"}` around a bare fence to keep the code block |
|
||||
|
||||
|
||||
@@ -240,8 +240,8 @@ Serves Goals 1, 4 and 5.
|
||||
canonical spelling, which round-trips byte-identically.
|
||||
- Seven CommonMark spellings parse without an error and build a document the reference
|
||||
implementation renders differently. Four because ADF holds no node for what it renders:
|
||||
- An empty link text, or one holding only hard breaks, reads as its destination, `[](/url)` as
|
||||
`/url` linked to `/url`, and `[]()` as nothing.
|
||||
- An empty link text reads as its destination, `[](/url)` as `/url` linked to `/url`, and `[]()`
|
||||
as nothing. A link text holding only hard breaks reads the same, followed by its hard breaks.
|
||||
- An image reads as an image only alone in its paragraph. Anywhere else — beside other text, in
|
||||
a heading or a table cell, or in the paragraph a task marker opens — it reads as its
|
||||
alt text linked to its URL: `See .` reads as
|
||||
|
||||
+10
-8
@@ -205,18 +205,20 @@ where its reference implementation nests one `<a>` in another.
|
||||
## An image reads as an image only alone in its paragraph, else as its alt text linked; a link text holding nothing a mark rides reads as its destination
|
||||
|
||||
2026-10-04, the maintainer and a writer panel, 3 of 3; `[]()` 2026-10-09, by Goals 4 and 7, as the
|
||||
maintainer's plan item asked; a link text of hard breaks alone 2026-10-09, by Goal 7. Goals 4, 5, 6
|
||||
and 7. Valid while `mediaInline` carries no URL, ADF holds no empty text node and a hard break takes
|
||||
no marks.
|
||||
maintainer's plan item asked; a link text of hard breaks alone 2026-10-09, the maintainer, after a
|
||||
reader panel split 4 to 3. Goals 4, 5, 6 and 7. Valid while `mediaInline` carries no URL, ADF holds
|
||||
no empty text node and a hard break takes no marks.
|
||||
|
||||
Every reader reads every CommonMark image and empty link, as `spec/flavour.md` §The CommonMark image
|
||||
and §Marks spell out. An image alone in its paragraph reads as the image, its title as the caption.
|
||||
Anywhere else it reads as its alt text linked to its URL, titled by its title. An empty alt text
|
||||
reads as the URL, and an empty link text as its destination; so does a link text holding only hard
|
||||
breaks, which take no marks. `[]()` reads as nothing: it renders nothing, so Goal 7 loses no
|
||||
content. A link alone in its paragraph whose whole text is one image reads as that image, with the
|
||||
link marking its `media`. Inside any other link, the alt text is plain text the link marks, and the
|
||||
image's URL and title drop: the panel chose this by Goal 6, which outranks Goal 7.
|
||||
reads as the URL, and an empty link text as its destination; a link text holding only hard breaks,
|
||||
which take no marks, reads as its destination followed by those breaks, which four of seven readers
|
||||
expected over dropping them, and the maintainer kept as an edge case until real reports arrive.
|
||||
`[]()` reads as nothing: it renders nothing, so Goal 7 loses no content. A link alone in its
|
||||
paragraph whose whole text is one image reads as that image, with the link marking its `media`.
|
||||
Inside any other link, the alt text is plain text the link marks, and the image's URL and title
|
||||
drop: the panel chose this by Goal 6, which outranks Goal 7.
|
||||
|
||||
In `portableMarkdownToAdf`, a `[!WORD]` marker with no `+` or `-` stands apart from the lines after
|
||||
it, and so does a `+` or `-` callout's title line, so an image alone on the marker line or after the
|
||||
|
||||
+3
-2
@@ -522,8 +522,9 @@ reference definition. Every such spelling carries an `href`: a directive link Co
|
||||
is a named error, and so is one spelling none. No link wraps a link at any nesting, which is
|
||||
CommonMark's own rule: a `[text]` already holding one leaves the outer brackets literal text, and
|
||||
the directive form, open to no literal reading, is a named error. ADF holds no empty text node, so a
|
||||
link whose text is empty or holds only hard breaks reads as its destination linked, `[](/url)` as
|
||||
`/url`, and one whose destination is empty too, `[]()`, as nothing.
|
||||
link whose text is empty reads as its destination linked, `[](/url)` as `/url`, and one whose
|
||||
destination is empty too, `[]()`, as nothing. A link text holding only hard breaks reads the same,
|
||||
followed by its hard breaks.
|
||||
|
||||
- `border` — Attributes: `color` (string, `#rrggbb` or `#rrggbbaa`), `size` (number, 1–3).
|
||||
- `code`, `em`, `strike`, `strong` — Attributes: none.
|
||||
|
||||
@@ -325,10 +325,10 @@ function linkMark({ destination, title }: LinkDefinition): AdfMark {
|
||||
return { attrs: title === undefined ? { href: destination } : { href: destination, title }, type: 'link' }
|
||||
}
|
||||
|
||||
// ADF holds no empty text node for a link mark to ride, so a link text holding nothing a mark rides reads as its destination, and an empty destination as nothing.
|
||||
// ADF holds no empty text node for a link mark to ride, so a link text holding nothing a mark rides reads as its destination, then the hard breaks it held, and an empty destination as those breaks alone.
|
||||
function linkTo(nodes: readonly Inline[], definition: LinkDefinition): Inline[] {
|
||||
if (!nodes.every(takesNoMarks)) return applyMark(nodes, linkMark(definition))
|
||||
return definition.destination === '' ? [] : [{ marks: [linkMark(definition)], text: definition.destination, type: 'text' }]
|
||||
return definition.destination === '' ? [...nodes] : [{ marks: [linkMark(definition)], text: definition.destination, type: 'text' }, ...nodes]
|
||||
}
|
||||
|
||||
// spec/flavour.md, Inline nodes: the leaf builds no node, so only the pair it parts spells it.
|
||||
|
||||
@@ -840,7 +840,8 @@ test('builds the hard break a mark spelling spans with no marks, in every reader
|
||||
const localId = '01a0431d-201e-7400-a175-9c04d3628f95'
|
||||
assert.deepEqual(content(losslessMarkdownToAdf(`_!adf:hardBreak{localId=${localId}}_\n`)), [{ content: [{ attrs: { localId }, type: 'hardBreak' }], type: 'paragraph' }])
|
||||
for (const read of [commonMarkToAdf, losslessMarkdownToAdf, portableMarkdownToAdf]) {
|
||||
assert.deepEqual(content(read('[\\\n](/u)\n')), [{ content: [marked('/u', link('/u'))], type: 'paragraph' }])
|
||||
assert.deepEqual(content(read('[\\\n](/u)\n')), [{ content: [marked('/u', link('/u')), hardBreak()], type: 'paragraph' }])
|
||||
assert.deepEqual(content(read('a[\\\n]()b\n')), [{ content: [text('a'), hardBreak(), text('b')], type: 'paragraph' }])
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
| 49 | 0.2.0 | | **Read a list whose bullet or ordered delimiter changes as two lists, in every reader, and retire `!adf:listBreak`.** | 4 | 5 | 5 | 8 | 4, 5 | 15.5 |
|
||||
| 61 | 0.2.0 | decision | **Have the emitter ask the inline reader how a line reads back, in place of `line-escaping.ts` predicting it.** | 6 | 8 | 3 | 8 | 1 | 14.0 |
|
||||
| 86 | 0.2.0 | | **Write an image whose `media` only a link marks, and one captioned with plain text, in the CommonMark spelling a writer panel picks, in `adfToLosslessMarkdown`.** | 4 | 4 | 5 | 6 | 4, 6 | 13.0 |
|
||||
| 89 | 0.2.0 | | **Build a `date`, `emoji`, `inlineCard`, `mention` or `status` holding no mark Atlassian's schema withholds from it, in every reader.** | 4 | 4 | 5 | 6 | 4 | 13.0 |
|
||||
| 85 | 0.2.0 | defect | **Write an external image a link marks as `[](href)` in `adfToPortableMarkdown`, keeping the link target.** | 3 | 3 | 6 | 7 | 7 | 12.9 |
|
||||
| 60 | 0.2.0 | decision | **Collect the questions `parse/` asks `emit/` into one named module.** | 3 | 4 | 2 | 6 | 2 | 10.7 |
|
||||
| 59 | 0.2.0 | decision | **Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` and `flavourClaims` to `markdown/flavour.ts`.** | 3 | 5 | 2 | 6 | 2 | 10.4 |
|
||||
| 62 | 0.2.0 | decision | **Move the portable flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/portable/`, so `emit/` no longer imports `portable/`.** | 4 | 5 | 2 | 6 | 2 | 9.4 |
|
||||
| 89 | 0.2.1 | | **Build a `date`, `emoji`, `inlineCard`, `mention` or `status` holding no mark Atlassian's schema withholds from it, in every reader.** | 4 | 4 | 5 | 6 | 4 | 13.0 |
|
||||
| 7 | 0.3.0 | decision | **Ship HTML: `adfToHtml`, `htmlToAdf`, and markdown to and from HTML composed through ADF, each call named by its flavour.** | 6 | 9 | 9 | 9 | 2, 4 | 25.8 |
|
||||
| 45 | 0.3.0 | | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 3 | 6 | 8 | 1 | 25.2 |
|
||||
| 6 | 0.3.0 | decision | **Specify the HTML dialect.** | 2 | 6 | 7 | 8 | 2, 4 | 24.7 |
|
||||
@@ -105,13 +105,6 @@ Every reader reads `[](href)` and `` as these sh
|
||||
(`docs/decisions.md` §The formats are API): changing it inside `0.2.0` spares consumers a second
|
||||
breaking change to stored markdown.
|
||||
|
||||
### 89. Build a `date`, `emoji`, `inlineCard`, `mention` or `status` holding no mark Atlassian's schema withholds from it, in every reader.
|
||||
|
||||
`*!adf:mention[@a]{id=…}*` and `[!adf:status[Done]{…}](/u)` build a node holding `em` or `link`.
|
||||
`full.json` gives these five no `marks`, and `stage-0.json` gives them `annotation` alone, so a site
|
||||
may reject the document. `InlineNodeModel.marks` (`src/adf/inline-nodes.ts`) then names the mark
|
||||
types a node takes, and the emitter carries a node holding another.
|
||||
|
||||
### 85. Write an external image a link marks as `[](href)` in `adfToPortableMarkdown`, keeping the link target.
|
||||
|
||||
Today `[](/v)` reads as the image whose `media` the link marks, and `adfToPortableMarkdown`
|
||||
@@ -140,6 +133,13 @@ Writing: `spellPortableBlock`, `quotedUnder`, `tryTaskList` and `taskBlocks` in
|
||||
`flavourClaims`. Every comprehension reader on 2026-10-03 named the portable flavour's spread across
|
||||
three directories.
|
||||
|
||||
### 89. Build a `date`, `emoji`, `inlineCard`, `mention` or `status` holding no mark Atlassian's schema withholds from it, in every reader.
|
||||
|
||||
`*!adf:mention[@a]{id=…}*` and `[!adf:status[Done]{…}](/u)` build a node holding `em` or `link`.
|
||||
`full.json` gives these five no `marks`, and `stage-0.json` gives them `annotation` alone, so a site
|
||||
may reject the document. `InlineNodeModel.marks` (`src/adf/inline-nodes.ts`) then names the mark
|
||||
types a node takes, and the emitter carries a node holding another.
|
||||
|
||||
### 7. Ship HTML: `adfToHtml`, `htmlToAdf`, and markdown to and from HTML composed through ADF, each call named by its flavour.
|
||||
|
||||
Lands after items 6, 59, 60, 61 and 62. Which flavours get a composed call is this item's to settle;
|
||||
|
||||
Reference in New Issue
Block a user