Compare commits
6 Commits
eccfd12d2d
...
31d3f53a58
| Author | SHA1 | Date | |
|---|---|---|---|
| 31d3f53a58 | |||
| 80a45b69da | |||
| 8287a483db | |||
| 7b38eba29e | |||
| ee9a7e7562 | |||
| 317262b240 |
+27
-23
@@ -42,8 +42,8 @@ adjacent text nodes, an empty `attrs`, `content` or `marks`, and `-0`. Neither s
|
|||||||
CommonMark's spelling stays wherever a document holds none of those shapes. The plain reader builds
|
CommonMark's spelling stays wherever a document holds none of those shapes. The plain reader builds
|
||||||
what is written, as `markdownToAdf` does. Only the plain writer is lossy: its reduction reads and
|
what is written, as `markdownToAdf` does. Only the plain writer is lossy: its reduction reads and
|
||||||
writes editor-normal ADF — adjacent text nodes of identical marks and no attributes merged, `-0` as
|
writes editor-normal ADF — adjacent text nodes of identical marks and no attributes merged, `-0` as
|
||||||
`0`, and an empty `attrs`, `content` or `marks` the absent key, but the doc's `content` — so two
|
`0`, and an empty `attrs`, `content` or `marks` the absent key, except the doc's `content`, which
|
||||||
documents the editor holds equal write the same plain markdown.
|
ADF's schema requires — so two documents the editor holds equal write the same plain markdown.
|
||||||
|
|
||||||
## `!adf:textBreak{}` parts text CommonMark would join
|
## `!adf:textBreak{}` parts text CommonMark would join
|
||||||
|
|
||||||
@@ -60,16 +60,16 @@ around the leaf. The grammar: `spec/flavour.md` §Inline nodes, **Adjacent text
|
|||||||
spells an empty object or array.
|
spells an empty object or array.
|
||||||
|
|
||||||
An `attrs`, `content` or `marks` key holding an empty object or array is the reserved key with the
|
An `attrs`, `content` or `marks` key holding an empty object or array is the reserved key with the
|
||||||
bare value `empty`, so an empty pair stays the node holding no content key. A writer panel chose
|
bare value `empty`, so a container opener and closer with nothing between them stays the node
|
||||||
the spelling, 5 of 7. The grammar: `spec/flavour.md` §Directives, **Attributes**, and §Marks.
|
holding no `content` key. A writer panel chose the spelling, 5 of 7. The grammar: `spec/flavour.md`
|
||||||
|
§Directives, **Attributes**, and §Marks.
|
||||||
|
|
||||||
## `-0` is spelled `-0`
|
## `-0` is spelled `-0`
|
||||||
|
|
||||||
2026-10-03, the maintainer. Goal 1. Valid while JSON's own serialization writes `-0` as `0`.
|
2026-10-03, the maintainer. Goal 1. Valid while JSON's own serialization writes `-0` as `0`.
|
||||||
|
|
||||||
`-0` is spelled `-0` wherever the flavour writes a number or a JSON value, since JSON's grammar
|
`-0` is spelled `-0` wherever the flavour writes a number or a JSON value, since JSON's grammar
|
||||||
reads it back as `-0` and no list marker spells the sign. The grammar: `spec/flavour.md` §Block
|
reads it back as `-0`. The grammar: `spec/flavour.md` §Block nodes and §The CommonMark blocks.
|
||||||
nodes and §The CommonMark blocks.
|
|
||||||
|
|
||||||
## Empty markdown is a document of no blocks
|
## Empty markdown is a document of no blocks
|
||||||
|
|
||||||
@@ -495,15 +495,18 @@ exits 0 on.
|
|||||||
2026-10-03, the maintainer. KISS, a technical principle, and its comprehension floor of 7. Valid
|
2026-10-03, the maintainer. KISS, a technical principle, and its comprehension floor of 7. Valid
|
||||||
while `todo.md` items 59, 60, 61 and 62 are open.
|
while `todo.md` items 59, 60, 61 and 62 are open.
|
||||||
|
|
||||||
A four-seat comprehension panel scored the project 6 against the floor of 7. The scores are the
|
A four-seat comprehension panel scored the project under the floor of 7. Its round-5 scores
|
||||||
baseline: a later panel may not score lower on any dimension.
|
(2026-10-03) are the baseline: a later panel may not score lower on any dimension.
|
||||||
|
|
||||||
| Seat | Navigation | Locality | Shape | Self-sufficiency | Overall |
|
| Seat | Navigation | Locality | Shape | Self-sufficiency | Overall |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| Junior | 6.5 | 5.5 | 6 | 5.5 | 6 |
|
| Junior | 6 | 5 | 6 | 5 | 5.5 |
|
||||||
| Mid | 7 | 5.5 | 6 | 5 | 5.5 |
|
| Mid | 6.5 | 5 | 6 | 4.5 | 5.5 |
|
||||||
| Senior | 7 | 5.5 | 6.5 | 5.5 | 6 |
|
| Senior | 6.5 | 5.5 | 6.5 | 5.5 | 6 |
|
||||||
| Architect | 6 | 5.5 | 5.5 | 6 | 6 |
|
| Architect | 6.5 | 5.5 | 6 | 6 | 6 |
|
||||||
|
|
||||||
|
Three panels on near-identical code scored overall means of 5.88, 5.75 and 5.75, and a seat moves
|
||||||
|
±0.5 between runs.
|
||||||
|
|
||||||
## Properties on a fixed seed
|
## Properties on a fixed seed
|
||||||
|
|
||||||
@@ -665,14 +668,15 @@ them. A primitive knowing neither ADF nor a format stays at `src/` root. A const
|
|||||||
`spec/flavour.md` draws, and a placement nothing here settles goes beside its only reader, or in
|
`spec/flavour.md` draws, and a placement nothing here settles goes beside its only reader, or in
|
||||||
what both read where there are two.
|
what both read where there are two.
|
||||||
|
|
||||||
A flavour's own code, both directions included, sits in its own directory: `markdown/plain/`.
|
A flavour's own code, both directions included, sits in its own directory: `markdown/plain/`;
|
||||||
Otherwise each format directory parts into `emit/` (ADF→format) and `parse/` (format→ADF), the rest
|
`todo.md` item 62 moves what still sits in `parse/` and `emit/`. Otherwise each format directory
|
||||||
of it holding what both directions read. A construct's reader lives there beside the regex the
|
parts into `emit/` (ADF→format) and `parse/` (format→ADF), the rest of it holding what both
|
||||||
emitter escapes against, so the two cannot drift; a reader with no emit counterpart goes in
|
directions read. A construct's reader lives there beside the regex the emitter escapes against, so
|
||||||
`parse/`, unless it is part of a construct that side already holds — a grammar stays in one file
|
the two cannot drift; a reader with no emit counterpart goes in `parse/`, unless it is part of a
|
||||||
rather than splitting across the seam. A rule both directions must answer alike — whether a list
|
construct that side already holds — a grammar stays in one file rather than splitting across the
|
||||||
marker interrupts a paragraph — is one function there too, never a copy per direction, however
|
seam. A rule both directions must answer alike — whether a list marker interrupts a paragraph — is
|
||||||
conservative the copy would be. Where the rule is the emitter's own choice, input consults it rather
|
one function there too, never a copy per direction, however conservative the copy would be. Where
|
||||||
than restating it, and that is the only value import `parse/` takes from `emit/` —
|
the rule is the emitter's own choice, input consults it rather than restating it, and that is the
|
||||||
`commonMarkSpelling` and `openingLinkTakesDirective` — so no fixture the emitter writes can be
|
only value import `parse/` takes from `emit/` — `commonMarkSpelling` and `openingLinkTakesDirective`
|
||||||
refused, and a spelling the emitter refuses gives its own error rather than a second name for it.
|
— so no fixture the emitter writes can be refused, and a spelling the emitter refuses gives its own
|
||||||
|
error rather than a second name for it.
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ export function emptyKeys(held: { attrs?: AdfAttributes; content?: AdfNode[]; ma
|
|||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
// A text node holding no attrs or content key and no empty marks: a format spells it as text, where anything more rides a carry.
|
// A format spells this node as text; anything more rides a carry, save content, which is refused.
|
||||||
export function isBareText(node: AdfNode): boolean {
|
export function isBareText(node: AdfNode): boolean {
|
||||||
return node.type === 'text' && node.attrs === undefined && node.content === undefined && node.marks?.length !== 0
|
return node.type === 'text' && node.attrs === undefined && node.content === undefined && node.marks?.length !== 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ function isTextBreak(item: Inline): item is TextBreak {
|
|||||||
return 'kind' in item && item.kind === 'textBreak'
|
return 'kind' in item && item.kind === 'textBreak'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The nodes the items hold, own-marks ones unwrapped and text breaks dropped.
|
// The nodes the items hold, with text breaks dropped.
|
||||||
function adfNodes(items: readonly Inline[]): AdfNode[] {
|
function adfNodes(items: readonly Inline[]): AdfNode[] {
|
||||||
const nodes: AdfNode[] = []
|
const nodes: AdfNode[] = []
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
|
|||||||
@@ -344,3 +344,9 @@ test('writes what the editor-normal form of the document writes', () => {
|
|||||||
assert.equal(plain({ content: [], text: 'hi', type: 'futureInline' }), 'hi\n')
|
assert.equal(plain({ content: [], text: 'hi', type: 'futureInline' }), 'hi\n')
|
||||||
assert.equal(plain(paragraph({ marks: [{ attrs: {}, type: 'code' }], text: 'a', type: 'text' }, { marks: [{ type: 'code' }], text: '|b', type: 'text' })), '`a|b`\n')
|
assert.equal(plain(paragraph({ marks: [{ attrs: {}, type: 'code' }], text: 'a', type: 'text' }, { marks: [{ type: 'code' }], text: '|b', type: 'text' })), '`a|b`\n')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('writes a text node holding content the same in either order beside its neighbour', () => {
|
||||||
|
const holding: AdfNode = { content: [text('inner')], text: 'a', type: 'text' }
|
||||||
|
assert.equal(plain(paragraph(holding, text('b'))), 'ab\n')
|
||||||
|
assert.equal(plain(paragraph(text('b'), holding)), 'ba\n')
|
||||||
|
})
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ test('reads negative zero as zero, as JSON does', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('reads an empty attrs object, marks array or content array as the absent key, but on doc', () => {
|
test('reads an empty attrs object, marks array or content array as the absent key, except on doc', () => {
|
||||||
const paragraph: AdfNode = { attrs: {}, content: [{ attrs: {}, marks: [], text: 'a', type: 'text' }, { marks: [{ attrs: {}, type: 'em' }], text: 'b', type: 'text' }], marks: [], type: 'paragraph' }
|
const paragraph: AdfNode = { attrs: {}, content: [{ attrs: {}, marks: [], text: 'a', type: 'text' }, { marks: [{ attrs: {}, type: 'em' }], text: 'b', type: 'text' }], marks: [], type: 'paragraph' }
|
||||||
assert.deepEqual(toEditorNormal({ content: [paragraph, { content: [], type: 'rule' }], type: 'doc', version: 1 }), {
|
assert.deepEqual(toEditorNormal({ content: [paragraph, { content: [], type: 'rule' }], type: 'doc', version: 1 }), {
|
||||||
content: [{ content: [{ text: 'a', type: 'text' }, { marks: [{ type: 'em' }], text: 'b', type: 'text' }], type: 'paragraph' }, { type: 'rule' }],
|
content: [{ content: [{ text: 'a', type: 'text' }, { marks: [{ type: 'em' }], text: 'b', type: 'text' }], type: 'paragraph' }, { type: 'rule' }],
|
||||||
@@ -77,7 +77,9 @@ test('normalizes blocks and mark attributes nesting far past the levels a recurs
|
|||||||
assert.deepEqual(merged.content?.[0]?.content?.map((text) => text.text), ['ab'])
|
assert.deepEqual(merged.content?.[0]?.content?.map((text) => text.text), ['ab'])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('joins a text node holding content to its neighbour, as editor-normal forms hold no content to part them', () => {
|
test('joins a text node holding content to no neighbour, so its content is kept', () => {
|
||||||
const paragraph: AdfNode = { content: [{ content: [{ text: 'lost', type: 'text' }], text: 'a', type: 'text' }, { text: 'b', type: 'text' }], type: 'paragraph' }
|
const holding: AdfNode = { content: [{ text: 'kept', type: 'text' }], text: 'a', type: 'text' }
|
||||||
assert.deepEqual(toEditorNormal({ content: [paragraph], type: 'doc', version: 1 }).content, [{ content: [{ content: [{ text: 'lost', type: 'text' }], text: 'ab', type: 'text' }], type: 'paragraph' }])
|
for (const content of [[holding, { text: 'b', type: 'text' }], [{ text: 'b', type: 'text' }, holding]]) {
|
||||||
|
assert.deepEqual(toEditorNormal({ content: [{ content, type: 'paragraph' }], type: 'doc', version: 1 }).content, [{ content, type: 'paragraph' }])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ type JsonContainer = JsonValue[] | { [key: string]: JsonValue }
|
|||||||
|
|
||||||
type NodeHolder = { content?: AdfNode[] }
|
type NodeHolder = { content?: AdfNode[] }
|
||||||
|
|
||||||
// The editor's rule is the reader's over the pair's editor-normal forms, which hold no content: a text node's content never parts it.
|
// The editor's rule is the reader's over the pair's editor-normal forms; a text node holding content never joins, so its content is kept.
|
||||||
export function joinsWhenEditorNormal(previous: AdfNode, node: AdfNode): boolean {
|
export function joinsWhenEditorNormal(previous: AdfNode, node: AdfNode): boolean {
|
||||||
|
if (previous.type !== 'text' || node.type !== 'text' || nodeContent(previous).length > 0 || nodeContent(node).length > 0) return false
|
||||||
return joinsWhenRead(normalNode(previous), normalNode(node))
|
return joinsWhenRead(normalNode(previous), normalNode(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
|
|
||||||
| ID | Release | Exempt | Item | R | S | A | G | Goals | Score |
|
| ID | Release | Exempt | Item | R | S | A | G | Goals | Score |
|
||||||
|---|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|---|
|
||||||
| 63 | 0.2.0 | defect | **Refuse a cyclic input as `not-an-adf-document` in place of looping forever.** | 2 | 2 | 6 | 9 | 1 | 27.5 |
|
| 63 | 0.2.0 | defect | **Refuse a cyclic input as `not-an-adf-document`.** | 2 | 2 | 6 | 9 | 1 | 27.5 |
|
||||||
| 7 | 0.2.0 | | **Ship HTML: `adfToHtml`, `htmlToAdf`, and `markdownToHtml` / `htmlToMarkdown` composed through ADF.** | 6 | 9 | 9 | 9 | 2, 3 | 25.8 |
|
| 7 | 0.2.0 | | **Ship HTML: `adfToHtml`, `htmlToAdf`, and `markdownToHtml` / `htmlToMarkdown` composed through ADF.** | 6 | 9 | 9 | 9 | 2, 3 | 25.8 |
|
||||||
| 55 | 0.2.0 | defect | **Spell through the carry every document the emitter refuses today for a carriage return, a NUL, a code-span line start, a newline inside an emoji, mention or status, or a text node with empty, missing or content-holding text.** | 5 | 5 | 7 | 9 | 1 | 25.8 |
|
| 55 | 0.2.0 | defect | **Spell through the carry every document the emitter refuses today for a carriage return, a NUL, a code-span line start, a newline inside an emoji, mention or status, or a text node whose text is empty or missing, or which holds content.** | 5 | 5 | 7 | 9 | 1 | 25.8 |
|
||||||
| 45 | 0.2.0 | | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 3 | 6 | 8 | 1 | 25.2 |
|
| 45 | 0.2.0 | | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 3 | 6 | 8 | 1 | 25.2 |
|
||||||
| 6 | 0.2.0 | decision | **Specify the HTML dialect.** | 2 | 6 | 7 | 8 | 2, 3 | 24.7 |
|
| 6 | 0.2.0 | decision | **Specify the HTML dialect.** | 2 | 6 | 7 | 8 | 2, 3 | 24.7 |
|
||||||
| 64 | 0.2.0 | defect | **Read every JSON key the emitter writes back unchanged on V8, with a fixture whose key holds `\`, `"` or a control character.** | 4 | 4 | 6 | 8 | 1, 7 | 23.0 |
|
| 64 | 0.2.0 | defect | **Read back unchanged on V8 every JSON key the emitter writes, with a fixture whose key holds `\`, `"` or a control character.** | 4 | 4 | 6 | 8 | 1, 7 | 23.0 |
|
||||||
| 43 | 0.2.0 | decision | **Give each markdown input its own reader, strict to its own standard.** | 6 | 7 | 8 | 9 | 3, 4 | 22.3 |
|
| 43 | 0.2.0 | decision | **Give each markdown input its own reader, strict to its own standard.** | 6 | 7 | 8 | 9 | 3, 4 | 22.3 |
|
||||||
| 65 | 0.2.0 | defect | **Read a mid-text or titled CommonMark image as Goal 6 allows, in place of refusing it with `unmappable-image`.** | 5 | 5 | 7 | 7 | 3, 4, 6 | 18.7 |
|
| 65 | 0.2.0 | defect | **Read a mid-text or titled CommonMark image as Goal 6 allows.** | 5 | 5 | 7 | 7 | 3, 4, 6 | 18.7 |
|
||||||
| 49 | 0.2.0 | | **Read a list whose bullet or ordered delimiter changes as two lists in the CommonMark reader.** | 4 | 5 | 5 | 8 | 3, 4 | 17.2 |
|
| 49 | 0.2.0 | | **Read a list whose bullet or ordered delimiter changes as two lists in the CommonMark reader.** | 4 | 5 | 5 | 8 | 3, 4 | 17.2 |
|
||||||
| 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 |
|
| 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 |
|
||||||
| 52 | 0.2.0 | | **Spell `colwidth` as a comma list, `colwidth="340,420"`.** | 3 | 3 | 5 | 6 | 5 | 13.0 |
|
| 52 | 0.2.0 | | **Spell `colwidth` as a comma list, `colwidth="340,420"`.** | 3 | 3 | 5 | 6 | 5 | 13.0 |
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
| 60 | 0.2.0 | decision | **Collect the questions `parse/` asks `emit/` into one named module.** | 3 | 4 | 2 | 6 | 2 | 10.7 |
|
| 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` to `markdown/flavour.ts`.** | 3 | 5 | 2 | 6 | 2 | 10.4 |
|
| 59 | 0.2.0 | decision | **Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` to `markdown/flavour.ts`.** | 3 | 5 | 2 | 6 | 2 | 10.4 |
|
||||||
| 50 | 0.2.0 | | **Read `[](/url)` and `[]()` as CommonMark's empty link.** | 4 | 4 | 3 | 6 | 3, 4, 6 | 10.4 |
|
| 50 | 0.2.0 | | **Read `[](/url)` and `[]()` as CommonMark's empty link.** | 4 | 4 | 3 | 6 | 3, 4, 6 | 10.4 |
|
||||||
| 62 | 0.2.0 | decision | **Move the plain flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/plain/`, so `plain/` and `emit/` no longer import each other.** | 4 | 5 | 2 | 6 | 2 | 9.4 |
|
| 62 | 0.2.0 | decision | **Move the plain flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/plain/`, so `emit/` no longer imports `plain/`.** | 4 | 5 | 2 | 6 | 2 | 9.4 |
|
||||||
| 38 | 0.3.0 | | **Spell a lone surrogate in a text node so it survives a UTF-8 encode.** | 2 | 2 | 4 | 7 | 1 | 19.5 |
|
| 38 | 0.3.0 | | **Spell a lone surrogate in a text node so it survives a UTF-8 encode.** | 2 | 2 | 4 | 7 | 1 | 19.5 |
|
||||||
| 47 | 0.3.0 | | **Open the README with what the package is, what it does and for whom.** | 1 | 4 | 7 | 9 | 9 | 14.0 |
|
| 47 | 0.3.0 | | **Open the README with what the package is, what it does and for whom.** | 1 | 4 | 7 | 9 | 9 | 14.0 |
|
||||||
| 34 | 0.3.0 | | **Read emphasis flanking by the whole character beside an astral symbol.** | 2 | 3 | 3 | 6 | 3, 4 | 12.6 |
|
| 34 | 0.3.0 | | **Read emphasis flanking by the whole character beside an astral symbol.** | 2 | 3 | 3 | 6 | 3, 4 | 12.6 |
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
|
||||||
### 63. Refuse a cyclic input as `not-an-adf-document` in place of looping forever.
|
### 63. Refuse a cyclic input as `not-an-adf-document`.
|
||||||
|
|
||||||
`adfDocumentFault` walks with `isNodeArray` (`adf/document.ts`) and `isJsonValue` (`json-value.ts`),
|
`adfDocumentFault` walks with `isNodeArray` (`adf/document.ts`) and `isJsonValue` (`json-value.ts`),
|
||||||
worklists that record no visited object, so a node whose `content` holds itself hangs
|
worklists that record no visited object, so a node whose `content` holds itself hangs
|
||||||
@@ -70,7 +70,7 @@ Lands after items 6, 59, 60, 61 and 62. The CommonMark spec suite also runs agai
|
|||||||
`markdownToHtml`. The README documents HTML as it documents markdown, and its tagline and
|
`markdownToHtml`. The README documents HTML as it documents markdown, and its tagline and
|
||||||
`package.json`'s `description` regain HTML.
|
`package.json`'s `description` regain HTML.
|
||||||
|
|
||||||
### 55. Spell through the carry every document the emitter refuses today for a carriage return, a NUL, a code-span line start, a newline inside an emoji, mention or status, or a text node with empty, missing or content-holding text.
|
### 55. Spell through the carry every document the emitter refuses today for a carriage return, a NUL, a code-span line start, a newline inside an emoji, mention or status, or a text node whose text is empty or missing, or which holds content.
|
||||||
|
|
||||||
`inline-line.ts` refuses a carriage return or NUL in text (`unspellable-character`) and a paragraph
|
`inline-line.ts` refuses a carriage return or NUL in text (`unspellable-character`) and a paragraph
|
||||||
opening with a code-span run (`unspellable-line-start`); `fencedTexts` refuses the same characters
|
opening with a code-span run (`unspellable-line-start`); `fencedTexts` refuses the same characters
|
||||||
@@ -78,8 +78,8 @@ in a code block; `directive-syntax.ts` refuses a newline in an emoji, mention or
|
|||||||
(`unspellable-whitespace`). The inline carry's JSON escapes all of them, so a lossless spelling
|
(`unspellable-whitespace`). The inline carry's JSON escapes all of them, so a lossless spelling
|
||||||
exists, and §The code list says a cause the carry answers gets no code. Fixing it revises §Which
|
exists, and §The code list says a cause the carry answers gets no code. Fixing it revises §Which
|
||||||
code a cause takes and §Markdown in is a canonical fixpoint, which the maintainer decides. Also a
|
code a cause takes and §Markdown in is a canonical fixpoint, which the maintainer decides. Also a
|
||||||
text node whose `text` is empty or missing, or which holds `content`: `inline-line.ts` refuses it
|
text node whose `text` is empty or missing, which `inline-line.ts` and `fencedTexts` refuse, or
|
||||||
inline and `fencedTexts` in a code block. Found by the README-goals audit, 2026-10-03.
|
which holds `content`, which `inline-line.ts` refuses. Found by the README-goals audit, 2026-10-03.
|
||||||
|
|
||||||
### 45. Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.
|
### 45. Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.
|
||||||
|
|
||||||
@@ -93,14 +93,14 @@ Element-by-element mapping, the `data-*` fidelity scheme, the opaque-carry form,
|
|||||||
foreign-element set `htmlToAdf` accepts — the set `markdownToAdf` shares (`spec/flavour.md` §Raw
|
foreign-element set `htmlToAdf` accepts — the set `markdownToAdf` shares (`spec/flavour.md` §Raw
|
||||||
HTML in input). The set sorts per `docs/decisions.md` §Foreign HTML sorts three ways.
|
HTML in input). The set sorts per `docs/decisions.md` §Foreign HTML sorts three ways.
|
||||||
|
|
||||||
### 64. Read every JSON key the emitter writes back unchanged on V8, with a fixture whose key holds `\`, `"` or a control character.
|
### 64. Read back unchanged on V8 every JSON key the emitter writes, with a fixture whose key holds `\`, `"` or a control character.
|
||||||
|
|
||||||
`property-harness.ts` strips `\`, `"` and control characters from generated JSON keys, citing V8's
|
`property-harness.ts` strips `\`, `"` and control characters from generated JSON keys, citing V8's
|
||||||
`JSON.parse` returning a wrong key for an escaped backslash. The library reads `json` attributes
|
`JSON.parse` returning a wrong key for an escaped backslash. The library reads `json` attributes
|
||||||
(`directive-syntax.ts`) and both carries (`opaque-carry.ts`) through that `JSON.parse`, so on Node,
|
(`directive-syntax.ts`) and both carries (`opaque-carry.ts`) through that `JSON.parse`, so on Node,
|
||||||
Deno and Chrome the round-trip would refuse its own output. Confirm with a fixture first; then read
|
Deno and Chrome the round-trip would refuse its own output or read back a different key; the fixture
|
||||||
JSON with our own parser or record the gap with an ending item. Found by the README-goals audit,
|
tells which. Confirm with a fixture first; then read JSON with our own parser or record the gap with
|
||||||
2026-10-03.
|
an ending item. Found by the README-goals audit, 2026-10-03.
|
||||||
|
|
||||||
### 43. Give each markdown input its own reader, strict to its own standard.
|
### 43. Give each markdown input its own reader, strict to its own standard.
|
||||||
|
|
||||||
@@ -110,11 +110,11 @@ a code fence whose info string opens `adf:` becomes the block carry where Common
|
|||||||
caller names the markdown it hands in: CommonMark, read as its spec says, or the lossless flavour,
|
caller names the markdown it hands in: CommonMark, read as its spec says, or the lossless flavour,
|
||||||
read as `spec/flavour.md` says. Breaking: `MIGRATION.md` says which call a caller takes.
|
read as `spec/flavour.md` says. Breaking: `MIGRATION.md` says which call a caller takes.
|
||||||
|
|
||||||
### 65. Read a mid-text or titled CommonMark image as Goal 6 allows, in place of refusing it with `unmappable-image`.
|
### 65. Read a mid-text or titled CommonMark image as Goal 6 allows.
|
||||||
|
|
||||||
§CommonMark is a subset keeps the image gap and no item ends it: a bot's `See  here`
|
A mid-text or titled image, such as a bot's `See  here`, is refused: 14 examples in
|
||||||
or a titled image is refused, 14 examples in `corpus/commonmark-spec/refusals.json`. Settle what such
|
`corpus/commonmark-spec/refusals.json`. Settle what such an image builds. Goal 6 drops form and
|
||||||
an image builds — Goal 6 drops form and keeps the target — and have the decision cite this item.
|
keeps the target.
|
||||||
|
|
||||||
### 49. Read a list whose bullet or ordered delimiter changes as two lists in the CommonMark reader.
|
### 49. Read a list whose bullet or ordered delimiter changes as two lists in the CommonMark reader.
|
||||||
|
|
||||||
@@ -173,12 +173,13 @@ input. Breaking, so it ships beside item 43: `MIGRATION.md`'s Readings table gai
|
|||||||
`pending` exceptions go, and its spelling leaves the README's "Four CommonMark spellings" bullet,
|
`pending` exceptions go, and its spelling leaves the README's "Four CommonMark spellings" bullet,
|
||||||
which counts one fewer.
|
which counts one fewer.
|
||||||
|
|
||||||
### 62. Move the plain flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/plain/`, so `plain/` and `emit/` no longer import each other.
|
### 62. Move the plain flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/plain/`, so `emit/` no longer imports `plain/`.
|
||||||
|
|
||||||
Reading: the alert and task-marker reads in `parse/markdown-to-adf.ts`, the `mintTaskIds` call and
|
Reading: the alert and task-marker reads in `parse/markdown-to-adf.ts`, the `mintTaskIds` call and
|
||||||
the `inlineLeaves` use. Writing: `spellPlainBlock`, `quotedUnder`, `tryTaskList` and `taskBlocks`
|
the `inlineLeaves` use. Writing: `spellPlainBlock`, `quotedUnder`, `tryTaskList` and `taskBlocks` in
|
||||||
in `emit/adf-to-markdown.ts`. Every comprehension seat on 2026-10-03 named the plain flavour's
|
`emit/adf-to-markdown.ts`. And `highlightDelimiter` and `highlightFlanking` move out of
|
||||||
spread across three directories.
|
`plain/conventions.ts`, which `emit/` imports them from; item 59 moves `Flavour`. Every comprehension reader on 2026-10-03
|
||||||
|
named the plain flavour's spread across three directories.
|
||||||
|
|
||||||
### 38. Spell a lone surrogate in a text node so it survives a UTF-8 encode.
|
### 38. Spell a lone surrogate in a text node so it survives a UTF-8 encode.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user