3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 19:58:28 +01:00

Answer architecture round 1: 0 🔴, 2 🟡; join empty-keys to the directive grammar and name the readers by direction

This commit is contained in:
2026-10-21 23:49:15 +02:00
committed by Lillem4n
parent b080410852
commit 3f7b3a386e
11 changed files with 46 additions and 28 deletions
+23 -9
View File
@@ -1,17 +1,22 @@
import assert from 'node:assert/strict'
import { dirname, join } from 'node:path'
import { dirname, join, relative } from 'node:path'
import { fileURLToPath } from 'node:url'
import { readdirSync, readFileSync } from 'node:fs'
import { readdirSync, readFileSync, statSync } from 'node:fs'
import test from 'node:test'
const markdown = join(dirname(fileURLToPath(import.meta.url)), '..', 'markdown')
const importSpecifier = /\bfrom '([^']+)'/g
function importsOf(directory: string): { file: string; specifier: string }[] {
const imports: { file: string; specifier: string }[] = []
for (const file of readdirSync(join(markdown, directory)).filter((name) => name.endsWith('.ts') && !name.endsWith('.test.ts'))) {
for (const match of readFileSync(join(markdown, directory, file), 'utf8').matchAll(importSpecifier)) {
imports.push({ file: `${directory}/${file}`, specifier: match[1] ?? '' })
function importsOf(directory: string): { file: string; target: string }[] {
const imports: { file: string; target: string }[] = []
for (const name of readdirSync(join(markdown, directory))) {
const path = join(directory, name)
if (statSync(join(markdown, path)).isDirectory()) {
imports.push(...importsOf(path))
} else if (name.endsWith('.ts') && !name.endsWith('.test.ts')) {
for (const match of readFileSync(join(markdown, path), 'utf8').matchAll(importSpecifier)) {
imports.push({ file: path, target: relative(markdown, join(markdown, directory, match[1] ?? '')) })
}
}
}
return imports
@@ -19,10 +24,19 @@ function importsOf(directory: string): { file: string; specifier: string }[] {
// docs/decisions.md §The source parts by ADF and format
test('parse/ asks emit/ only through the module that names its questions', () => {
const reached = importsOf('parse').filter(({ specifier }) => specifier.startsWith('../emit/'))
const reached = importsOf('parse').filter(({ target }) => target.startsWith('emit/'))
assert.ok(reached.length > 0)
assert.deepEqual(
reached.filter(({ specifier }) => specifier !== '../emit/spelling-questions.ts'),
reached.filter(({ target }) => target !== 'emit/spelling-questions.ts'),
[],
)
})
test('directive/ imports nothing from emit/ or parse/', () => {
const imports = importsOf('directive')
assert.ok(imports.length > 0)
assert.deepEqual(
imports.filter(({ target }) => target.startsWith('emit/') || target.startsWith('parse/')),
[],
)
})
+1 -1
View File
@@ -4,7 +4,7 @@ import { attributeNestingMessage, nodeAttrs, nodeMarks } from '../../adf/documen
import { blockArgument, marksAttribute, spellBlockMarks } from './block.ts'
import { failure, success, type ConvertErrorPath, type Result } from '../../result.ts'
import { isBareToken, spellAttributes, spellDirectiveOpener, spellVocabulary } from './syntax.ts'
import { spellEmptyKeys } from '../empty-keys.ts'
import { spellEmptyKeys } from './empty-keys.ts'
import { vocabularyPairs } from '../../adf/attribute-vocabulary.ts'
export function spellBlockDirectiveOpener(node: AdfNode, model: BlockNodeModel, path: ConvertErrorPath, spelledByBody: readonly string[] = []): Result<string> | undefined {
@@ -1,8 +1,8 @@
import type { DirectiveAttributes, DirectiveValue } from './directive/syntax.ts'
import type { EmptyKey } from '../adf/document.ts'
import type { Read } from '../result.ts'
import { emptyKeys } from '../adf/document.ts'
import { unsupportedNodeShape } from './directive/syntax.ts'
import type { DirectiveAttributes, DirectiveValue } from './syntax.ts'
import type { EmptyKey } from '../../adf/document.ts'
import type { Read } from '../../result.ts'
import { emptyKeys } from '../../adf/document.ts'
import { unsupportedNodeShape } from './syntax.ts'
type EmptyKeysRead = { empty: ReadonlySet<EmptyKey>; rest: Map<string, DirectiveValue> }
+1 -1
View File
@@ -2,7 +2,7 @@ import type { AdfNode } from '../../adf/document.ts'
import type { InlineNodeModel } from '../../adf/inline-nodes.ts'
import { nodeAttrs } from '../../adf/document.ts'
import { spellAttributes, spellVocabulary } from './syntax.ts'
import { spellEmptyKeys } from '../empty-keys.ts'
import { spellEmptyKeys } from './empty-keys.ts'
import { vocabularyPairs } from '../../adf/attribute-vocabulary.ts'
export function spellInlineNodeAttributes(node: AdfNode, model: InlineNodeModel): string | undefined {
@@ -6,8 +6,8 @@ import type { MarkSpelling } from '../mark-spellings.ts'
import { directivePrefix } from './syntax.ts'
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
import { markSpelling } from '../mark-spellings.ts'
import { readEmptyKeys } from '../empty-keys.ts'
import { readVocabulary } from './attributes.ts'
import { readEmptyKeys } from './empty-keys.ts'
import { readVocabulary } from './attribute-reading.ts'
export function readDirectiveMark(name: string, attributes: DirectiveAttributes, path: ConvertErrorPath): Result<AdfMark> | undefined {
const spelling = markSpelling(name)
@@ -2,18 +2,18 @@ import type { AdfAttributes, AdfMark, AdfNode, EmptyKey } from '../../adf/docume
import type { BlockNodeModel } from '../../adf/block-nodes.ts'
import type { ConvertFault } from '../../result.ts'
import type { DirectiveAttributes, DirectiveValue } from './syntax.ts'
import type { Elsewhere } from './attributes.ts'
import type { Elsewhere } from './attribute-reading.ts'
import { attributeNestingMessage, isUnmarkedSpellableText } from '../../adf/document.ts'
import { attributePairCause, attributeValue, directivePrefix, readAttributes, readDirectiveName, unknownDirectiveFault } from './syntax.ts'
import { blockArgument, blockDirectiveForm, marksAttribute, readMarkValues, spellBlockMarks } from './block.ts'
import { blockNodeModel } from '../../adf/block-nodes.ts'
import { carryFencePrefix, carryName } from '../opaque-carry.ts'
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
import { inlineMarkSpellingFault, readMarkAttributes } from './marks.ts'
import { inlineMarkSpellingFault, readMarkAttributes } from './mark-reading.ts'
import { inlineNodeModel } from '../../adf/inline-nodes.ts'
import { markVocabularies } from '../../adf/mark-attributes.ts'
import { readEmptyKeys, spellsEmpty } from '../empty-keys.ts'
import { readVocabulary } from './attributes.ts'
import { readEmptyKeys, spellsEmpty } from './empty-keys.ts'
import { readVocabulary } from './attribute-reading.ts'
import { slotFault } from './syntax.ts'
import { textBreakName } from '../adjacent-text.ts'
import { textDirectiveName } from './text.ts'
+1 -1
View File
@@ -8,7 +8,7 @@ import { holdsDirectivePrefix, spellAttributes, spellVocabulary } from './direct
import { holdsEntityReference } from './commonmark/entity-references.ts'
import { isAutolink } from './commonmark/grammar.ts'
import { isMarkType, markAttributes } from '../adf/mark-attributes.ts'
import { spellEmptyKeys } from './empty-keys.ts'
import { spellEmptyKeys } from './directive/empty-keys.ts'
import { vocabularyPairs } from '../adf/attribute-vocabulary.ts'
type Spelling = { kind: 'code' | 'directive' | 'link'; spelling?: undefined } | { kind: 'emphasis'; spelling: string }
+2 -2
View File
@@ -19,8 +19,8 @@ import { normalizeLabel, readInlineTarget, readLabel } from '../commonmark/link-
import { noSpans, readInlineDirective } from '../directive/syntax.ts'
import { openingLinkTakesDirective } from '../emit/spelling-questions.ts'
import { readCarriedInline } from '../opaque-carry.ts'
import { readDirectiveMark } from '../directive/marks.ts'
import { readInlineDirectiveNode } from '../directive/nodes.ts'
import { readDirectiveMark } from '../directive/mark-reading.ts'
import { readInlineDirectiveNode } from '../directive/node-reading.ts'
import { inlineSource, readInlineToken } from '../inline-tokens.ts'
import { readTextDirective } from '../directive/text.ts'
+3 -3
View File
@@ -1,6 +1,6 @@
import type { AdfDocument, AdfNode } from '../../adf/document.ts'
import type { Block, DirectiveBlock } from './blocks.ts'
import type { BlockDirectiveNode } from '../directive/nodes.ts'
import type { BlockDirectiveNode } from '../directive/node-reading.ts'
import type { ConvertFault } from '../../result.ts'
import type { Flavour } from '../flavour.ts'
import type { LineContainer } from '../line-container.ts'
@@ -17,9 +17,9 @@ import { leadingMarker, readAlertMarker, readTaskMarker } from '../portable/conv
import { mintTaskIds } from '../portable/task-ids.ts'
import { parseBlocks } from './blocks.ts'
import { parseInlineContent } from './inline-content.ts'
import { readBlockDirectiveNode } from '../directive/nodes.ts'
import { readBlockDirectiveNode } from '../directive/node-reading.ts'
import { spellStringAttribute, unsupportedNodeShape } from '../directive/syntax.ts'
import { spellsEmpty } from '../empty-keys.ts'
import { spellsEmpty } from '../directive/empty-keys.ts'
import { splitPlace, splitTaskList } from '../portable/task-list.ts'
import { withinEngineLimits } from '../length-bound.ts'
+4
View File
@@ -225,6 +225,10 @@ documents markdown, its comparison table names HTML, and its tagline and `packag
description and keywords name HTML again. `htmlToAdf` refuses a value that is not a string as
`not-a-string` (`docs/decisions.md` §Which code a cause takes).
`html/` may not import `markdown/`, so it learns here which of the carry (`opaque-carry.ts`) and the
mark spellings (`mark-spellings.ts`) it shares and moves them out of `markdown/`; a composed call
naming a markdown `Flavour` sits above both peers.
### 46. Publish the bundle size in the README, failing the release pipeline when it drifts.
Item 7 changes the size. The quantity is what a consumer downloads and loads: the