15 - refuse the directive link spelling no href #103
@@ -266,7 +266,9 @@ someone spells it or pins it.
|
||||
## 11. Code rules
|
||||
|
||||
- Two-space indent, strict TypeScript, English everywhere. Alphabetical order wherever order
|
||||
carries no meaning.
|
||||
carries no meaning, keyed on the name a line introduces rather than where it came from: an
|
||||
import sorts on its first binding, type imports ahead of value imports, so moving or renaming a
|
||||
module reorders nothing (the maintainer, 2026-09-18).
|
||||
- Failures are values: everything returns
|
||||
`Result<T>` — `{ ok: true; value } | { ok: false; error: ConvertError }` — nothing throws.
|
||||
`try/catch` only wrapped tightly around a call that genuinely throws, converted to a result on
|
||||
|
||||
@@ -102,7 +102,7 @@ emit refuses:
|
||||
| `unspellable-line-start` | a paragraph line begins with a code span whose backticks would read back as a code fence | put any text before the code span |
|
||||
| `unspellable-whitespace` | an `emoji`, `mention` or `status` holds a newline in the text its inline directive spells in the content slot | replace it with a space — an inline directive never spans lines |
|
||||
| `unsupported-nesting-depth` | blocks, marks, an attribute's JSON or a carried node's JSON nest past 500 levels | keep the ADF and pass the document over, or show it read-only; flatten the input where you are the one who wrote it |
|
||||
| `unsupported-node-shape` | a node carries an attribute, value, argument or body its type does not take — or markdown writes as a directive a node or mark the flavour spells as CommonMark | write the shape the message names; `spec/flavour.md` lists every type's attributes and body |
|
||||
| `unsupported-node-shape` | a node carries an attribute, value, argument or body its type does not take, or lacks one it needs — or markdown writes as a directive a node or mark the flavour spells as CommonMark | write the shape the message names; `spec/flavour.md` lists every type's attributes and body |
|
||||
|
||||
## The guarantees
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import { createServer } from 'node:http'
|
||||
import { extname, join } from 'node:path'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import { toEditorNormal } from '../dist/adf/editor-normal.js'
|
||||
|
||||
const contentTypes = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript' }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
unsupported-node-shape
|
||||
@@ -0,0 +1 @@
|
||||
See !adf:link[the docs]{title="Setup guide"} here.
|
||||
+2
-1
@@ -473,7 +473,8 @@ the inline directive `!adf:link[text]{attrs}` only where CommonMark does not: an
|
||||
`href` and `title`, an `href` or `title` no canonical escape spells (a control character, a
|
||||
backslash, an entity reference, an angle bracket beside a space or opening a bare destination, a
|
||||
newline in the title), or a link opening a paragraph whose markdown spelling would read as a link
|
||||
reference definition. A directive link CommonMark could spell is a named error.
|
||||
reference definition. Every such spelling carries an `href`: a directive link CommonMark could
|
||||
spell is a named error, and so is one spelling none.
|
||||
|
||||
- `border` — Attributes: `color` (string, `#rrggbb` or `#rrggbbaa`), `size` (number, 1–3).
|
||||
- `code`, `em`, `strike`, `strong` — Attributes: none.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fc from 'fast-check'
|
||||
import assert from 'node:assert/strict'
|
||||
import fc from 'fast-check'
|
||||
import test from 'node:test'
|
||||
|
||||
import { adfDocument, propertyRuns, propertyTimeout } from './property-harness.ts'
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { AttributeKind, AttributeVocabulary } from './adf/attribute-vocabulary.ts'
|
||||
import { blockArgument } from './markdown/block-directive-arguments.ts'
|
||||
import { blockDirectives } from './adf/block-directives.ts'
|
||||
import { inlineDirectives } from './adf/inline-directives.ts'
|
||||
import { markAttributes } from './adf/mark-attributes.ts'
|
||||
import { blockArgument } from './markdown/block-directive-arguments.ts'
|
||||
|
||||
type Held = Map<string, Set<AttributeKind>>
|
||||
type Properties = Map<string, SchemaObject[]>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { AdfDocument, AdfNode } from './adf/document.ts'
|
||||
import { adfToMarkdown } from './markdown/emit/adf-to-markdown.ts'
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import { basename, dirname, join, sep } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import { adfToMarkdown } from './markdown/emit/adf-to-markdown.ts'
|
||||
import { isAdfDocument } from './adf/document.ts'
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { AttributeKind, AttributeVocabulary } from './adf/attribute-vocabulary.ts'
|
||||
import { blockDirectives } from './adf/block-directives.ts'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fc from 'fast-check'
|
||||
import assert from 'node:assert/strict'
|
||||
import fc from 'fast-check'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { AdfDocument } from './adf/document.ts'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import { readEntityReference } from './entity-references.ts'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AdfNode } from '../../adf/document.ts'
|
||||
import { carriesOnly, nodeAttrs, nodeContent } from '../../adf/document.ts'
|
||||
import type { ConvertErrorPath } from '../../result.ts'
|
||||
import { carriesOnly, nodeAttrs, nodeContent } from '../../adf/document.ts'
|
||||
import { serializeCanonicalJson } from '../../canonical-json.ts'
|
||||
import { tryImageLine } from './inline-line.ts'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { InlineDirective } from '../../adf/inline-directives.ts'
|
||||
import { assembleInlineLine, isSyntax, type InlineEscaping, type InlineSegment, type LineContainer, type NodeRange } from './line-escaping.ts'
|
||||
import { carriedInline } from '../opaque-carry.ts'
|
||||
import { claimsLine, holdsNullCharacter, trimTrailingSpace } from '../commonmark/grammar.ts'
|
||||
import { commonMarkLink, markSpelling, spellMarkAttributes } from '../mark-spellings.ts'
|
||||
import { commonMarkLink, linkHref, markSpelling, spellMarkAttributes } from '../mark-spellings.ts'
|
||||
import { escapeUnbalanced, spellDestination } from '../commonmark/link-syntax.ts'
|
||||
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
|
||||
import { inlineDirective } from '../../adf/inline-directives.ts'
|
||||
@@ -300,8 +300,8 @@ function needsPadding(text: string): boolean {
|
||||
|
||||
// `undefined` where the link takes the directive form the caller spells.
|
||||
function emitLink(nodes: readonly AdfNode[], mark: AdfMark, depth: number, range: NodeRange, context: InlineContext): Result<Emission> | undefined {
|
||||
const href = nodeAttrs(mark)['href']
|
||||
if (typeof href !== 'string') return success({ carry: range })
|
||||
const href = linkHref(nodeAttrs(mark))
|
||||
if (href === undefined) return success({ carry: range })
|
||||
const opening = depth === 0 && range.first === 0 && context.openingLinkAsDirective
|
||||
const commonMark = opening ? undefined : commonMarkLink(nodeAttrs(mark), href, nodes, depth + 1, context.bracketed)
|
||||
if (commonMark === undefined) return undefined
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { backtickRun, closingBacktickRun } from '../commonmark/backtick-runs.ts'
|
||||
import { delimiterFlags, isWordCharacter, matchEmphasis, runLength } from '../commonmark/emphasis-matching.ts'
|
||||
import { backslashEscape, escapesLineClaim, inlineHtmlConstruct, opensBracketedAutolink, opensEmailAutolink, type LinePosition } from '../commonmark/grammar.ts'
|
||||
import { backtickRun, closingBacktickRun } from '../commonmark/backtick-runs.ts'
|
||||
import { claimsDirectivePrefix } from '../directive-syntax.ts'
|
||||
import { delimiterFlags, isWordCharacter, matchEmphasis, runLength } from '../commonmark/emphasis-matching.ts'
|
||||
import { isBareDelimiterRow } from '../pipe-table-syntax.ts'
|
||||
import { opensLinkDefinition } from '../commonmark/link-reference-definitions.ts'
|
||||
import { readEntityReference } from '../commonmark/entity-references.ts'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { AdfNode } from '../../adf/document.ts'
|
||||
import type { ConvertErrorPath } from '../../result.ts'
|
||||
import { carriesOnly, nodeContent } from '../../adf/document.ts'
|
||||
import { spellPipeDelimiter, spellPipeRow } from '../pipe-table-syntax.ts'
|
||||
import { tryPipeCell } from './inline-line.ts'
|
||||
import type { ConvertErrorPath } from '../../result.ts'
|
||||
|
||||
export function tryPipeTable(node: AdfNode, path: ConvertErrorPath): string | undefined {
|
||||
const rows = pipeRows(node)
|
||||
|
||||
@@ -35,6 +35,11 @@ export function markSpelling(type: string): MarkSpelling | undefined {
|
||||
return { attributes, kind: spelling.kind }
|
||||
}
|
||||
|
||||
export function linkHref(attrs: AdfAttributes): string | undefined {
|
||||
const href = attrs['href']
|
||||
return typeof href === 'string' ? href : undefined
|
||||
}
|
||||
|
||||
// spec/flavour.md, Marks. `marksInside` counts the marks the link's nodes carry within it: the emitter's depth + 1, the parser's 0.
|
||||
export function commonMarkLink(attrs: AdfAttributes, href: string, nodes: readonly AdfNode[], marksInside: number, bracketed: boolean): CommonMarkLink | undefined {
|
||||
if (Object.keys(attrs).some((key) => key !== 'href' && key !== 'title')) return undefined
|
||||
|
||||
@@ -2,9 +2,9 @@ import type { AdfNode } from '../adf/document.ts'
|
||||
import type { DirectiveSpan, Read } from './directive-syntax.ts'
|
||||
import type { JsonSpelling } from '../canonical-json.ts'
|
||||
import { failure, success, type ConvertErrorPath, type Result } from '../result.ts'
|
||||
import { fencedCodeBlock } from './commonmark/backtick-runs.ts'
|
||||
import { isAdfNode } from '../adf/document.ts'
|
||||
import { isJsonValue, nestingDepth, overNested } from '../json-value.ts'
|
||||
import { fencedCodeBlock } from './commonmark/backtick-runs.ts'
|
||||
import { largestNesting } from '../nesting.ts'
|
||||
import { malformedDirective, readSoleStringAttribute, spellAttributes, spellInlineLeafDirective, spellStringAttribute, unsupportedNodeShape } from './directive-syntax.ts'
|
||||
import { serializeCanonicalJson } from '../canonical-json.ts'
|
||||
|
||||
@@ -18,9 +18,9 @@ import {
|
||||
thematicBreakTail,
|
||||
type ThematicBreakTail,
|
||||
} from '../commonmark/grammar.ts'
|
||||
import { barePipeCells, isDelimiterRow, isPipeAlignment, isPipeDelimiter, malformedPipeTable, pipeCells } from '../pipe-table-syntax.ts'
|
||||
import { blockDirectiveForm } from '../block-directive-forms.ts'
|
||||
import { directiveEscape, malformedDirective, readDirectiveLine, spellDirectiveCloser } from '../directive-syntax.ts'
|
||||
import { barePipeCells, isDelimiterRow, isPipeAlignment, isPipeDelimiter, malformedPipeTable, pipeCells } from '../pipe-table-syntax.ts'
|
||||
import { readLinkDefinitions } from '../commonmark/link-reference-definitions.ts'
|
||||
|
||||
export type Block = { position: SourcePosition } & (
|
||||
|
||||
@@ -5,17 +5,17 @@ import type { LineContainer } from '../emit/line-escaping.ts'
|
||||
import type { LinkDefinition } from '../commonmark/link-syntax.ts'
|
||||
import { backslashEscape, decodeTextEscapes, inlineHtmlConstruct, readBracketedAutolink, readEmailAutolink, trimTrailingSpace } from '../commonmark/grammar.ts'
|
||||
import { backtickRun, closingBacktickRun } from '../commonmark/backtick-runs.ts'
|
||||
import { commonMarkLink } from '../mark-spellings.ts'
|
||||
import { commonMarkLink, linkHref } from '../mark-spellings.ts'
|
||||
import { delimiterFlags, matchEmphasis, runLength } from '../commonmark/emphasis-matching.ts'
|
||||
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
|
||||
import { inlineDirective } from '../../adf/inline-directives.ts'
|
||||
import { mergeAdjacentText } from '../../adf/editor-normal.ts'
|
||||
import { noSpans, readInlineDirective } from '../directive-syntax.ts'
|
||||
import { nodeAttrs, nodeMarks } from '../../adf/document.ts'
|
||||
import { normalizeLabel, readInlineTarget, readLabel } from '../commonmark/link-syntax.ts'
|
||||
import { openingLinkTakesDirective } from '../emit/inline-line.ts'
|
||||
import { readCarriedInline } from '../opaque-carry.ts'
|
||||
import { readDirectiveMark } from './directive-marks.ts'
|
||||
import { noSpans, readInlineDirective } from '../directive-syntax.ts'
|
||||
import { readInlineDirectiveNode } from './directive-nodes.ts'
|
||||
import { readTextDirective } from '../text-directive.ts'
|
||||
|
||||
@@ -51,6 +51,7 @@ type Scan = {
|
||||
type SlotContent = { carry: boolean; nodes: AdfNode[] }
|
||||
|
||||
const carriedInMark = 'no mark spelling wraps an opaque carry: the carried node restores exactly, marks included'
|
||||
const hreflessLink = 'the link mark spells its href: this one spells none'
|
||||
const imageAlone = 'an image fits only as a paragraph of its own: this one sits inside other content'
|
||||
const spellableLink = 'link takes the directive form only where CommonMark cannot spell it: this one it can, as [text](url "title") or <url>'
|
||||
|
||||
@@ -208,15 +209,15 @@ function directiveMarkPiece(scan: Scan, name: string, mark: AdfMark, slot: SlotC
|
||||
return failure('unsupported-node-shape', `the ${name} mark wraps the [content] it marks: this one wraps none`, scan.path)
|
||||
}
|
||||
if (slot.carry) return failure('unsupported-node-shape', carriedInMark, scan.path)
|
||||
const refused = mark.type === 'link' ? refuseSpellableLink(scan, mark, slot.nodes, index) : undefined
|
||||
const refused = mark.type === 'link' ? refuseLinkDirective(scan, mark, slot.nodes, index) : undefined
|
||||
if (refused !== undefined) return refused
|
||||
return success({ kind: 'nodes', nodes: applyMark(slot.nodes, mark) })
|
||||
}
|
||||
|
||||
// spec/flavour.md, Marks. A link opening a paragraph may still need the directive form for the line it opens, which `assemble` asks the emitter.
|
||||
function refuseSpellableLink(scan: Scan, mark: AdfMark, nodes: readonly AdfNode[], index: number): Result<Piece> | undefined {
|
||||
const href = nodeAttrs(mark)['href']
|
||||
if (typeof href !== 'string') return undefined
|
||||
function refuseLinkDirective(scan: Scan, mark: AdfMark, nodes: readonly AdfNode[], index: number): Result<Piece> | undefined {
|
||||
const href = linkHref(nodeAttrs(mark))
|
||||
if (href === undefined) return failure('unsupported-node-shape', hreflessLink, scan.path)
|
||||
if (commonMarkLink(nodeAttrs(mark), href, nodes, 0, scan.container === undefined) === undefined) return undefined
|
||||
if (index !== 0 || scan.container !== 'paragraph') return failure('unsupported-node-shape', spellableLink, scan.path)
|
||||
scan.openingSpellableLink = true
|
||||
|
||||
@@ -984,8 +984,6 @@ test('refuses the directive link CommonMark could spell, and reads the one it co
|
||||
assert.equal(content(markdownToAdf('| !adf:link[`]: a`]{href="/u"} |\n| --- |\n')), refused)
|
||||
assert.equal(content(markdownToAdf('!adf:underline[!adf:link[a]{href="/u"}]\n')), refused)
|
||||
assert.deepEqual(path(markdownToAdf('Part.\n\nSee !adf:link[a]{href="/u"}.\n')), ['content', 1])
|
||||
const titled: AdfNode = { marks: [{ attrs: { title: 't' }, type: 'link' }], text: 'a', type: 'text' }
|
||||
assert.deepEqual(content(markdownToAdf('!adf:link[a]{title=t}\n')), [{ content: [titled], type: 'paragraph' }])
|
||||
const opening: AdfNode = { marks: [{ attrs: { href: '/u' }, type: 'link' }, { type: 'code' }], text: ']: a', type: 'text' }
|
||||
assert.deepEqual(content(markdownToAdf('!adf:link[`]: a`]{href="/u"}\n')), [{ content: [opening], type: 'paragraph' }])
|
||||
assert.deepEqual(content(markdownToAdf('!adf:heading {level=1 localId=h}\n!adf:link[`]: a`]{href="/u"}\n!adf:/heading\n')), [
|
||||
@@ -993,6 +991,13 @@ test('refuses the directive link CommonMark could spell, and reads the one it co
|
||||
])
|
||||
})
|
||||
|
||||
test('names the href the directive link spells no value for', () => {
|
||||
const named = 'unsupported-node-shape: the link mark spells its href: this one spells none'
|
||||
assert.equal(content(markdownToAdf('!adf:link[a]\n')), named)
|
||||
assert.equal(content(markdownToAdf('!adf:link[a]{title=t}\n')), named)
|
||||
assert.equal(content(markdownToAdf('See !adf:link[a]{id=01a032c3-7a90-70c9-88f6-c60f710eda07}.\n')), named)
|
||||
})
|
||||
|
||||
test('names the directive mark left without the content it wraps', () => {
|
||||
const named = 'unsupported-node-shape: the underline mark wraps the [content] it marks: this one wraps none'
|
||||
assert.equal(content(markdownToAdf('!adf:underline[]\n')), named)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fc from 'fast-check'
|
||||
import assert from 'node:assert/strict'
|
||||
import { env } from 'node:process'
|
||||
import fc from 'fast-check'
|
||||
|
||||
import type { AdfAttributes, AdfDocument, AdfMark, AdfNode } from './adf/document.ts'
|
||||
import type { Arbitrary } from 'fast-check'
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const sourceRoot = dirname(fileURLToPath(import.meta.url))
|
||||
const union = /export type ConvertErrorCode =\n((?:\s+\| '[a-z-]+'\n)+)/
|
||||
|
||||
@@ -809,6 +809,11 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li
|
||||
and `link-syntax.ts` — leaving the flavour's own constructs at the root, the split
|
||||
`spec/flavour.md` draws between the subset and the flavour (the systems-architect and the
|
||||
maintainer, 2026-09-16).
|
||||
- [x] **15 — The href-less directive link (`0.2.0`).** Refuse `!adf:link[text]` spelling no `href`
|
||||
with `unsupported-node-shape` naming the attribute, so the mark has one spelling: today it
|
||||
parses to a mark the emitter writes back as a carry, while the schema requires `href` and
|
||||
every other directive mark spells without attributes in both directions alike (the
|
||||
stability-reviewer, 2026-09-16; the maintainer, 2026-09-17).
|
||||
|
||||
## 5 — Ship `0.1.0`
|
||||
|
||||
|
||||
@@ -7,16 +7,12 @@ milestone. A done item shrinks to its title here; its full text moves to `todo-h
|
||||
|
||||
Start a session with: `Read AGENTS.md and todo.md, then do what todo.md's "Next session" says.`
|
||||
|
||||
1. The first unchecked item in shipping order, per AGENTS.md §15 — or, where that item has no
|
||||
1. `git fetch origin` first and read this file at `origin/main`, then branch off it, not the
|
||||
worktree left behind: a checkout behind the remote reads a merged item as unchecked.
|
||||
2. The first unchecked item in shipping order, per AGENTS.md §15 — or, where that item has no
|
||||
release, the planning chunk §15 describes.
|
||||
2. In flight: nothing.
|
||||
3. `git fetch origin` and branch off `origin/main`, not the worktree left behind.
|
||||
4. Ask before touching code: §11 sorts alphabetically "wherever order carries no meaning" without
|
||||
naming the key an import line sorts on. The code sorts by first binding, type imports ahead of
|
||||
value imports, so a module rename reorders nothing — 14 leaned on that, and four files have
|
||||
drifted from it. `src/html/` (6, 7) writes the next large body of imports. Candidate §11 rule:
|
||||
an import line sorts on its first binding, type imports ahead of value imports.
|
||||
5. Before stopping, rewrite this section: the in-flight line, and the prompt itself wherever the
|
||||
3. In flight: nothing.
|
||||
4. Before stopping, rewrite this section: the in-flight line, and the prompt itself wherever the
|
||||
session found it wrong or short.
|
||||
|
||||
## Milestones
|
||||
@@ -213,11 +209,7 @@ bundle size and the tagline.
|
||||
- [x] **13a — `rule` and `layoutSection`.**
|
||||
- [x] **13b — The directive link.**
|
||||
- [x] **14 — The CommonMark subset's directory (`0.2.0`).**
|
||||
- [ ] **15 — The href-less directive link (`0.2.0`).** Refuse `!adf:link[text]` spelling no `href`
|
||||
with `unsupported-node-shape` naming the attribute, so the mark has one spelling: today it
|
||||
parses to a mark the emitter writes back as a carry, while the schema requires `href` and
|
||||
every other directive mark spells without attributes in both directions alike (the
|
||||
stability-reviewer, 2026-09-16; the maintainer, 2026-09-17).
|
||||
- [x] **15 — The href-less directive link (`0.2.0`).**
|
||||
- [ ] **16 — The link wrapping a link (`0.2.0`).** Read `[<http://x/>](/v)` and
|
||||
`[!adf:link[a]{href="/u"}](/v)` as `[[a](/u)](/v)` reads — the inner link wins and the outer
|
||||
brackets stay literal text, CommonMark's rule that no link holds another — rather than
|
||||
|
||||
Reference in New Issue
Block a user