mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Delete the unreached task-marker paragraph in taskBlocks; check the README error tables against the codes
This commit is contained in:
@@ -902,3 +902,20 @@ Read most (opens and searches):
|
||||
- `AGENTS.md` 3
|
||||
- `src/index.ts` 3
|
||||
- `CHANGELOG.md` 2
|
||||
|
||||
## Reading cost, chunk 8d75c35b392b, finished 2026-11-09T07:18:15Z
|
||||
|
||||
92011 new input tokens before the first commit.
|
||||
|
||||
Read most (opens and searches):
|
||||
|
||||
- `todo.md` 6
|
||||
- `src/json-text.test.ts` 3
|
||||
- `ci.sh` 2
|
||||
- `docker-runner.sh` 2
|
||||
- `package.json` 2
|
||||
- `src/adf/document.test.ts` 2
|
||||
- `src/markdown/commonmark/entity-references.ts` 2
|
||||
- `src/markdown/parse/markdown-to-adf.test.ts` 2
|
||||
- `src/result.test.ts` 2
|
||||
- `README.md` 1
|
||||
|
||||
@@ -158,6 +158,30 @@ test('spells a task list as a bullet list whose items lead with their state', ()
|
||||
assert.equal(portable(node('taskList', {}, task('DONE', 'a'), node('taskList', {}, task('TODO', '')))), '- [x] a\n - [ ]\n')
|
||||
})
|
||||
|
||||
test('leaves a block task item only when it opens with a paragraph', () => {
|
||||
const task = (state: string, value: string): AdfNode => node('taskItem', { state }, text(value))
|
||||
const blockTask = (...content: AdfNode[]): AdfNode => node('blockTaskItem', { state: 'TODO' }, ...content)
|
||||
const codeBlock: AdfNode = { content: [text('x')], type: 'codeBlock' }
|
||||
const nested = node('taskList', {}, task('TODO', 'n'))
|
||||
const lists = [
|
||||
node('taskList', {}, blockTask(said('a'), said('b')), blockTask(codeBlock, said('c')), blockTask(bulletList(item(said('d'))), said('e'))),
|
||||
node('taskList', {}, blockTask(nested, said('f')), blockTask(said('g'), codeBlock, said('h')), blockTask(), blockTask(said('i'))),
|
||||
node('taskList', {}, blockTask(said('j'), nested, said('k'), nested), blockTask(said('l'), said('m'), nested)),
|
||||
]
|
||||
const reduced = reduceToPortable(document(...lists))
|
||||
assert.ok(reduced.ok)
|
||||
const pending = [...(reduced.value.content ?? [])]
|
||||
let blockTasks = 0
|
||||
for (let at = pending.pop(); at !== undefined; at = pending.pop()) {
|
||||
for (const child of at.content ?? []) pending.push(child)
|
||||
if (at.type !== 'blockTaskItem') continue
|
||||
blockTasks += 1
|
||||
assert.equal(at.content?.[0]?.type, 'paragraph')
|
||||
assert.ok((at.content ?? []).filter((child) => child.type === 'paragraph').length >= 2)
|
||||
}
|
||||
assert.equal(blockTasks, 2)
|
||||
})
|
||||
|
||||
test('spells a decision list as a plain bullet list', () => {
|
||||
assert.equal(portable(node('decisionList', {}, node('decisionItem', { state: 'DECIDED' }, text('Ship')), said('Stray'))), '- Ship\n- Stray\n')
|
||||
})
|
||||
|
||||
@@ -65,19 +65,17 @@ function placedBlock(node: AdfNode, path: ConvertErrorPath, depth: number, writi
|
||||
return block.ok ? success({ blocks: [{ ...block.value, node }], headroom: block.value.headroom }) : block
|
||||
}
|
||||
|
||||
// The marker leads the first paragraph, or stands as one where the blocks open with another.
|
||||
// A block task item reaches here holding two or more paragraphs, so the marker leads the first (`itemParts` in task-list.ts).
|
||||
function taskBlocks(task: AdfNode, path: ConvertErrorPath, depth: number, writing: Writing): Result<PlacedBlocks> {
|
||||
const marker = taskMarker(nodeAttrs(task)['state'])
|
||||
const markerBlock: PlacedBlock = { node: { type: 'paragraph' }, spelling: 'commonmark', text: blockText(marker) }
|
||||
if (task.type === 'taskItem') {
|
||||
const content = nodeContent(task)
|
||||
const line = content.length === 0 ? success('') : emitInlineLine(content, 'paragraph', path, writing.flavour)
|
||||
if (!line.ok) return line
|
||||
return success({ blocks: [{ ...markerBlock, text: blockText(line.value === '' ? marker : `${marker} ${line.value}`) }], headroom: Number.POSITIVE_INFINITY })
|
||||
return success({ blocks: [{ node: { type: 'paragraph' }, spelling: 'commonmark', text: blockText(line.value === '' ? marker : `${marker} ${line.value}`) }], headroom: Number.POSITIVE_INFINITY })
|
||||
}
|
||||
const walk = walkBlocks(nodeContent(task), path, depth, writing)
|
||||
if (!walk.ok) return walk
|
||||
const [first, ...rest] = walk.value.blocks
|
||||
const blocks = first?.node.type === 'paragraph' ? [{ ...first, text: prefixedText(`${marker} `, '', first.text) }, ...rest] : [markerBlock, ...walk.value.blocks]
|
||||
const blocks = walk.value.blocks.map((block, index) => (index === 0 ? { ...block, text: prefixedText(`${marker} `, '', block.text) } : block))
|
||||
return success({ blocks, headroom: walk.value.headroom })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { AdfDocument, AdfMark } from './adf/document.ts'
|
||||
@@ -44,3 +47,15 @@ for (const [code, cause] of Object.entries(causes)) {
|
||||
assert.equal(!result.ok && result.error.code, code)
|
||||
})
|
||||
}
|
||||
|
||||
// A code added or removed must reach the README's tables, which are the contract a consumer switches on.
|
||||
test('the README error tables list exactly the codes ConvertErrorCode holds', () => {
|
||||
const readme = readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'README.md'), 'utf8')
|
||||
const section = readme.slice(readme.indexOf('\n## The errors\n'))
|
||||
const listed = new Set<string>()
|
||||
for (const line of section.slice(1, section.indexOf('\n## ', 1)).split('\n')) {
|
||||
const code = /^\| `([a-z-]+)` \|/.exec(line)?.[1]
|
||||
if (code !== undefined) listed.add(code)
|
||||
}
|
||||
assert.deepEqual([...listed].sort(), Object.keys(causes).sort())
|
||||
})
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
| ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 153 | 0.4.0 | | question | **Decide whether `src/markdown/` root may import `emit/`, `parse/` or `portable/`, and hold the answer in `.oxlintrc.json` and §The source parts by ADF and format.** | 1 | 2 | 2 | 3 | 2 | code health by 3 | 26.2 |
|
||||
| 159 | 0.4.0 | | | **Delete `taskBlocks`'s branch that writes the task marker as its own paragraph, once a test shows no block task item reaches it.** | 1 | 1 | 1 | 1 | 1 | code health by 1 | 10.5 |
|
||||
| 144 | 0.4.0 | | | **Give every container `docker-runner.sh` and `publish.sh` start a memory limit and an equal swap limit, sized from the largest a gate leg measurably needs.** | 1 | 1 | 1 | 1 | 2 | code health by 1 | 8.7 |
|
||||
| 143 | 0.4.0 | | defect | **Keep, in `adfToPortableMarkdown`, the link around a note, so a linked stored image writes every target.** | 2 | 1 | 3 | 4 | 3 | 7 by 1 | 6.5 |
|
||||
| 169 | 0.4.0 | | owner | **Investigate `kovetskiy/mark` in depth and record in a report how its formats differ from ours, where the two projects overlap and how they compare.** | 1 | 1 | 1 | 1 | 3 | 3 by 4 | 6.3 |
|
||||
@@ -21,7 +20,6 @@
|
||||
| 80 | 0.4.0 | | | **Name the node's path and the attribute key when `not-an-adf-document` refuses a node inside the document.** | 2 | 1 | 2 | 3 | 3 | 6 by 4 | 6.0 |
|
||||
| 171 | 0.4.0 | | principle | **Assert the facts an error message must carry, never its whole sentence, in `markdown-to-adf.test.ts`, `document.test.ts` and `html-to-adf.test.ts`.** | 3 | 1 | 1 | 3 | 6 | code health by 3 | 4.4 |
|
||||
| 173 | 0.4.0 | | principle | **Move each CommonMark spelling's description out of `commonMarkSpellings`, beside the emitter's spelling it describes.** | 2 | 1 | 1 | 2 | 4 | code health by 1 | 4.4 |
|
||||
| 81 | 0.4.0 | | | **Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.** | 1 | 1 | 1 | 1 | 2 | 3 by 2 | 4.2 |
|
||||
| 147 | 0.4.0 | | | **Ship `adfToPlainText`, writing a document's content with no markup, under a README goal for plain text.** | 2 | 2 | 1 | 3 | 4 | 3 by 4 | 4.2 |
|
||||
| 141 | 0.4.0 | | decision | **Write `strike` as HTML in `adfToHtml` and read it back in `htmlToAdf`, in place of the carry element.** | 1 | 1 | 1 | 1 | 2 | 6 by 2 | 4.0 |
|
||||
| 135 | 0.4.0 | | | **Write media, media groups and captions as semantic HTML in `adfToHtml` and read them back in `htmlToAdf`, in place of the carry element.** | 2 | 2 | 2 | 4 | 4 | 6 by 2 | 2.0 |
|
||||
@@ -67,13 +65,6 @@ a paragraph opening with a spellable `!adf:link` (§Nothing recurses unbounded).
|
||||
`src/markdown/**` bans only `html/`; banning `emit/`, `parse/` and `portable/` for the root's own
|
||||
files, with its `ci.sh` probe, extends the decision.
|
||||
|
||||
### 159. Delete `taskBlocks`'s branch that writes the task marker as its own paragraph, once a test shows no block task item reaches it.
|
||||
|
||||
`taskBlocks` (`portable/block-spelling.ts`) writes the marker as a paragraph of its own where a
|
||||
block task item's content opens with another block. Mutation testing finds that branch unreached:
|
||||
a block task item is left only when it holds two or more paragraphs. Found by the stability review
|
||||
of item 33, 2026-10-29.
|
||||
|
||||
### 144. Give every container `docker-runner.sh` and `publish.sh` start a memory limit and an equal swap limit, sized from the largest a gate leg measurably needs.
|
||||
|
||||
`in_image`, `with_firefox` and `publish.sh` run `docker run` with neither `--memory` nor
|
||||
@@ -151,13 +142,6 @@ export it through `emit/spelling-questions.ts`, the one import `parse/` may take
|
||||
drop the comment at `markdown-to-adf.ts:145` that keeps them apart.
|
||||
Technical principle: DRY. Found by the principles audit of the release review of 0.3.0, 2026-11-08.
|
||||
|
||||
### 81. Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.
|
||||
|
||||
Nothing checks the README's tables against the codes: `src/result.test.ts` holds a refusal per code,
|
||||
but a code added or removed leaves the tables a false contract under a green gate. Assert that the
|
||||
tables list exactly the codes `causes` holds. Found by the technical-principles audit of item 78,
|
||||
2026-10-05.
|
||||
|
||||
### 147. Ship `adfToPlainText`, writing a document's content with no markup, under a README goal for plain text.
|
||||
|
||||
Asked on the Atlassian developer community for search indexing, notifications, emails and CSV
|
||||
|
||||
Reference in New Issue
Block a user