Emitter 2c: the inline node directives, the directive marks and the carried whitespace
CI / gate (push) Successful in 5s

This commit is contained in:
2026-08-26 09:06:13 +02:00
parent c7e51b1731
commit efc267db2b
10 changed files with 355 additions and 121 deletions
+27 -4
View File
@@ -1,13 +1,36 @@
import type { AttributeKind } from './adf-document.ts'
import type { AdfAttributes, AttributeKind } from './adf-document.ts'
import type { JsonValue } from './json-value.ts'
import { failure, type ConvertErrorPath, type Result } from './result.ts'
import { serializeCanonicalJson } from './canonical-json.ts'
export type AttributeFault = { key: string; kind: AttributeKind | undefined }
export type AttributeVocabulary = Readonly<Record<string, AttributeKind>>
const bareToken = /^[A-Za-z0-9_-]+$/
export function isBareToken(text: string): boolean {
return bareToken.test(text)
}
export function attributeFailure<T>(type: string, fault: AttributeFault, path: ConvertErrorPath): Result<T> {
if (fault.kind === undefined) return failure('unspelled-node-attribute', `the ${type} attribute ${fault.key} has no canonical markdown spelling`, path)
return failure('unsupported-node-shape', `the ${type} attribute ${fault.key} holds no ${fault.kind}`, path)
}
export function vocabularyPairs(attrs: AdfAttributes, vocabulary: AttributeVocabulary, slot: string | undefined): AttributeFault | [string, string][] {
const pairs: [string, string][] = []
for (const [key, value] of Object.entries(attrs)) {
if (key === slot) continue
const kind = Object.hasOwn(vocabulary, key) ? vocabulary[key] : undefined
if (kind === undefined) return { key, kind: undefined }
const spelled = spellAttributeValue(value, kind)
if (spelled === undefined) return { key, kind }
pairs.push([key, spelled])
}
return pairs
}
export function spellAttributes(pairs: readonly (readonly [string, string])[]): string {
if (pairs.length === 0) return ''
const spelled = [...pairs].sort(([left], [right]) => (left < right ? -1 : 1)).map(([key, value]) => `${key}=${value}`)
@@ -17,15 +40,15 @@ export function spellAttributes(pairs: readonly (readonly [string, string])[]):
export function spellAttributeValue(value: JsonValue, kind: AttributeKind): string | undefined {
if (kind === 'json') return spellJsonAttribute(value)
if (kind === 'boolean') return typeof value === 'boolean' ? `${value}` : undefined
if (kind === 'number') return typeof value === 'number' ? spell(JSON.stringify(value)) : undefined
return typeof value === 'string' ? spell(value) : undefined
if (kind === 'number') return typeof value === 'number' ? spellStringAttribute(JSON.stringify(value)) : undefined
return typeof value === 'string' ? spellStringAttribute(value) : undefined
}
export function spellJsonAttribute(value: JsonValue): string {
return quote(serializeCanonicalJson(value, 'compact'))
}
function spell(text: string): string {
export function spellStringAttribute(text: string): string {
return isBareToken(text) ? text : quote(text)
}