diff --git a/index.d.ts b/index.d.ts index 5ee7327..549e60f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -48,62 +48,12 @@ export interface ContainerDirective extends Parent, DirectiveFields { */ export interface ContainerDirectiveData extends Data {} -/** - * Markdown directive (leaf form). - */ -export interface LeafDirective extends Parent, DirectiveFields { - /** - * Node type of leaf directive. - */ - type: 'leafDirective' - - /** - * Children of leaf directive. - */ - children: PhrasingContent[] - - /** - * Data associated with the mdast leaf directive. - */ - data?: LeafDirectiveData | undefined -} - -/** - * Info associated with mdast leaf directive nodes by the ecosystem. - */ -export interface LeafDirectiveData extends Data {} - -/** - * Markdown directive (text form). - */ -export interface TextDirective extends Parent, DirectiveFields { - /** - * Node type of text directive. - */ - type: 'textDirective' - - /** - * Children of text directive. - */ - children: PhrasingContent[] - - /** - * Data associated with the text leaf directive. - */ - data?: TextDirectiveData | undefined -} - -/** - * Info associated with mdast text directive nodes by the ecosystem. - */ -export interface TextDirectiveData extends Data {} - /** * Union of registered mdast directive nodes. * * It is not possible to register custom mdast directive node types. */ -export type Directives = ContainerDirective | LeafDirective | TextDirective +export type Directives = ContainerDirective // Add custom data tracked to turn markdown into a tree. declare module 'mdast-util-from-markdown' { @@ -140,46 +90,6 @@ declare module 'mdast-util-to-markdown' { * ``` */ containerDirectiveLabel: 'containerDirectiveLabel' - - /** - * Whole leaf directive. - * - * ```markdown - * > | ::a - * ^^^ - * ``` - */ - leafDirective: 'leafDirective' - - /** - * Label of a leaf directive. - * - * ```markdown - * > | ::a[b] - * ^^^ - * ``` - */ - leafDirectiveLabel: 'leafDirectiveLabel' - - /** - * Whole text directive. - * - * ```markdown - * > | :a - * ^^ - * ``` - */ - textDirective: 'textDirective' - - /** - * Label of a text directive. - * - * ```markdown - * > | :a[b] - * ^^^ - * ``` - */ - textDirectiveLabel: 'textDirectiveLabel' } } @@ -191,12 +101,6 @@ declare module 'mdast' { * quotes), which contains further flow content. */ containerDirective: ContainerDirective - - /** - * Directive in flow content (such as in the root document, or block - * quotes), which contains nothing. - */ - leafDirective: LeafDirective } interface ParagraphData { @@ -214,29 +118,11 @@ declare module 'mdast' { directiveLabel?: boolean | null | undefined } - interface PhrasingContentMap { - /** - * Directive in phrasing content (such as in paragraphs, headings). - */ - textDirective: TextDirective - } - interface RootContentMap { /** * Directive in flow content (such as in the root document, or block * quotes), which contains further flow content. */ containerDirective: ContainerDirective - - /** - * Directive in flow content (such as in the root document, or block - * quotes), which contains nothing. - */ - leafDirective: LeafDirective - - /** - * Directive in phrasing content (such as in paragraphs, headings). - */ - textDirective: TextDirective } } diff --git a/lib/index.js b/lib/index.js index d68f9be..187cc5d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,8 +13,6 @@ * @typedef {import('mdast-util-to-markdown').State} State * * @typedef {import('../index.js').Directives} Directives - * @typedef {import('../index.js').LeafDirective} LeafDirective - * @typedef {import('../index.js').TextDirective} TextDirective */ import {ok as assert} from 'devlop' @@ -41,13 +39,7 @@ export function directiveFromMarkdown() { enter: { directiveContainer: enterContainer, directiveContainerAttributes: enterAttributes, - directiveContainerLabel: enterContainerLabel, - - directiveLeaf: enterLeaf, - directiveLeafAttributes: enterAttributes, - - directiveText: enterText, - directiveTextAttributes: enterAttributes + directiveContainerLabel: enterContainerLabel }, exit: { directiveContainer: exit, @@ -57,23 +49,7 @@ export function directiveFromMarkdown() { directiveContainerAttributeValue: exitAttributeValue, directiveContainerAttributes: exitAttributes, directiveContainerLabel: exitContainerLabel, - directiveContainerName: exitName, - - directiveLeaf: exit, - directiveLeafAttributeClassValue: exitAttributeClassValue, - directiveLeafAttributeIdValue: exitAttributeIdValue, - directiveLeafAttributeName: exitAttributeName, - directiveLeafAttributeValue: exitAttributeValue, - directiveLeafAttributes: exitAttributes, - directiveLeafName: exitName, - - directiveText: exit, - directiveTextAttributeClassValue: exitAttributeClassValue, - directiveTextAttributeIdValue: exitAttributeIdValue, - directiveTextAttributeName: exitAttributeName, - directiveTextAttributeValue: exitAttributeValue, - directiveTextAttributes: exitAttributes, - directiveTextName: exitName + directiveContainerName: exitName } } } @@ -90,11 +66,11 @@ export function directiveToMarkdown() { unsafe: [ { character: '\r', - inConstruct: ['leafDirectiveLabel', 'containerDirectiveLabel'] + inConstruct: ['containerDirectiveLabel'] }, { character: '\n', - inConstruct: ['leafDirectiveLabel', 'containerDirectiveLabel'] + inConstruct: ['containerDirectiveLabel'] }, { before: '[^:]', @@ -105,9 +81,7 @@ export function directiveToMarkdown() { {atBreak: true, character: ':', after: ':'} ], handlers: { - containerDirective: handleDirective, - leafDirective: handleDirective, - textDirective: handleDirective + containerDirective: handleDirective } } } @@ -120,22 +94,6 @@ function enterContainer(token) { enter.call(this, 'containerDirective', token) } -/** - * @this {CompileContext} - * @type {FromMarkdownHandle} - */ -function enterLeaf(token) { - enter.call(this, 'leafDirective', token) -} - -/** - * @this {CompileContext} - * @type {FromMarkdownHandle} - */ -function enterText(token) { - enter.call(this, 'textDirective', token) -} - /** * @this {CompileContext} * @param {Directives['type']} type @@ -151,11 +109,7 @@ function enter(type, token) { */ function exitName(token) { const node = this.stack[this.stack.length - 1] - assert( - node.type === 'containerDirective' || - node.type === 'leafDirective' || - node.type === 'textDirective' - ) + assert(node.type === 'containerDirective') node.name = this.sliceSerialize(token) } @@ -266,11 +220,7 @@ function exitAttributes() { this.data.directiveAttributes = undefined this.resume() // Drop EOLs const node = this.stack[this.stack.length - 1] - assert( - node.type === 'containerDirective' || - node.type === 'leafDirective' || - node.type === 'textDirective' - ) + assert(node.type === 'containerDirective') node.attributes = cleaned } @@ -291,15 +241,9 @@ function handleDirective(node, _, state, info) { const sequence = fence(node) const exit = state.enter(node.type) let value = tracker.move(sequence + (node.name || '')) - /** @type {LeafDirective | Paragraph | TextDirective | undefined} */ - let label - if (node.type === 'containerDirective') { - const head = (node.children || [])[0] - label = inlineDirectiveLabel(head) ? head : undefined - } else { - label = node - } + const head = (node.children || [])[0] + const label = inlineDirectiveLabel(head) ? head : undefined if (label && label.children && label.children.length > 0) { const exit = state.enter('label') @@ -308,10 +252,6 @@ function handleDirective(node, _, state, info) { const subexit = state.enter(labelType) value += tracker.move('[') value += tracker.move( - // @ts-expect-error: `containerPhrasing` is typed correctly, but TS - // generates *hardcoded* types, which means that our dynamically added - // directives are not present. - // At some point, TS should fix that, and `from-markdown` should be fine. state.containerPhrasing(label, { ...tracker.current(), before: value, @@ -357,7 +297,7 @@ function peekDirective() { */ function attributes(node, state) { const quote = state.options.quote || '"' - const subset = node.type === 'textDirective' ? [quote] : [quote, '\n', '\r'] + const subset = [quote, '\n', '\r'] const attrs = node.attributes || {} /** @type {Array} */ const values = [] @@ -467,8 +407,6 @@ function fence(node) { } }) size += 3 - } else if (node.type === 'leafDirective') { - size = 2 } else { size = 1 } diff --git a/test.js b/test.js index e37e23e..d4e9f22 100644 --- a/test.js +++ b/test.js @@ -16,87 +16,6 @@ test('core', async function (t) { }) test('directiveFromMarkdown()', async function (t) { - await t.test('should support directives (text)', async function () { - assert.deepEqual( - fromMarkdown('a :b[c]{d} e.', { - extensions: [directive()], - mdastExtensions: [directiveFromMarkdown()] - }).children[0], - { - type: 'paragraph', - children: [ - { - type: 'text', - value: 'a ', - position: { - start: {line: 1, column: 1, offset: 0}, - end: {line: 1, column: 3, offset: 2} - } - }, - { - type: 'textDirective', - name: 'b', - attributes: {d: ''}, - children: [ - { - type: 'text', - value: 'c', - position: { - start: {line: 1, column: 6, offset: 5}, - end: {line: 1, column: 7, offset: 6} - } - } - ], - position: { - start: {line: 1, column: 3, offset: 2}, - end: {line: 1, column: 11, offset: 10} - } - }, - { - type: 'text', - value: ' e.', - position: { - start: {line: 1, column: 11, offset: 10}, - end: {line: 1, column: 14, offset: 13} - } - } - ], - position: { - start: {line: 1, column: 1, offset: 0}, - end: {line: 1, column: 14, offset: 13} - } - } - ) - }) - - await t.test('should support directives (leaf)', async function () { - assert.deepEqual( - fromMarkdown('::a[b]{c}', { - extensions: [directive()], - mdastExtensions: [directiveFromMarkdown()] - }).children[0], - { - type: 'leafDirective', - name: 'a', - attributes: {c: ''}, - children: [ - { - type: 'text', - value: 'b', - position: { - start: {line: 1, column: 5, offset: 4}, - end: {line: 1, column: 6, offset: 5} - } - } - ], - position: { - start: {line: 1, column: 1, offset: 0}, - end: {line: 1, column: 10, offset: 9} - } - } - ) - }) - await t.test('should support directives (container)', async function () { assert.deepEqual( fromMarkdown(':::a[b]{c}\nd', { @@ -152,119 +71,8 @@ test('directiveFromMarkdown()', async function (t) { ) }) - await t.test('should support content in a label', async function () { - const tree = fromMarkdown(':a[b *c*\nd]', { - extensions: [directive()], - mdastExtensions: [directiveFromMarkdown()] - }) - - removePosition(tree, {force: true}) - - assert.deepEqual(tree, { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'textDirective', - name: 'a', - attributes: {}, - children: [ - {type: 'text', value: 'b '}, - {type: 'emphasis', children: [{type: 'text', value: 'c'}]}, - {type: 'text', value: '\nd'} - ] - } - ] - } - ] - }) - }) - - await t.test('should support attributes', async function () { - const tree = fromMarkdown(':a{#b.c.d e=f g="h&i&unknown;j"}', { - extensions: [directive()], - mdastExtensions: [directiveFromMarkdown()] - }) - - removePosition(tree, {force: true}) - - assert.deepEqual(tree, { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'textDirective', - name: 'a', - attributes: {id: 'b', class: 'c d', e: 'f', g: 'h&i&unknown;j'}, - children: [] - } - ] - } - ] - }) - }) - - await t.test( - 'should not support non-terminated character references', - async function () { - const tree = fromMarkdown(':a{b=¶m c="¶m" d=\'¶m\'}', { - extensions: [directive()], - mdastExtensions: [directiveFromMarkdown()] - }) - - removePosition(tree, {force: true}) - - assert.deepEqual(tree, { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'textDirective', - name: 'a', - attributes: {b: '¶m', c: '¶m', d: '¶m'}, - children: [] - } - ] - } - ] - }) - } - ) - - await t.test('should support EOLs in attributes', async function () { - const tree = fromMarkdown(':a{b\nc="d\ne"}', { - extensions: [directive()], - mdastExtensions: [directiveFromMarkdown()] - }) - - removePosition(tree, {force: true}) - - assert.deepEqual(tree, { - type: 'root', - children: [ - { - type: 'paragraph', - children: [ - { - type: 'textDirective', - name: 'a', - attributes: {b: '', c: 'd\ne'}, - children: [] - } - ] - } - ] - }) - }) - await t.test('should support directives in directives', async function () { - const tree = fromMarkdown('::::a\n:::b\n:c\n:::\n::::', { + const tree = fromMarkdown('::::a\n:::b\n:::\n::::', { extensions: [directive()], mdastExtensions: [directiveFromMarkdown()] }) @@ -283,19 +91,7 @@ test('directiveFromMarkdown()', async function (t) { type: 'containerDirective', name: 'b', attributes: {}, - children: [ - { - type: 'paragraph', - children: [ - { - type: 'textDirective', - name: 'c', - attributes: {}, - children: [] - } - ] - } - ] + children: [] } ] } @@ -305,400 +101,6 @@ test('directiveFromMarkdown()', async function (t) { }) test('directiveToMarkdown()', async function (t) { - await t.test( - 'should try to serialize a directive (text) w/o `name`', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - // @ts-expect-error: check how the runtime handles `children`, `name` missing. - {type: 'textDirective'}, - {type: 'text', value: ' b.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a : b.\n' - ) - } - ) - - await t.test( - 'should serialize a directive (text) w/ `name`', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - // @ts-expect-error: check how the runtime handles `children` missing. - {type: 'textDirective', name: 'b'}, - {type: 'text', value: ' c.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b c.\n' - ) - } - ) - - await t.test( - 'should serialize a directive (text) w/ `children`', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - children: [{type: 'text', value: 'c'}] - }, - {type: 'text', value: ' d.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b[c] d.\n' - ) - } - ) - - await t.test( - 'should escape brackets in a directive (text) label', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - children: [{type: 'text', value: 'c[d]e'}] - }, - {type: 'text', value: ' f.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b[c\\[d\\]e] f.\n' - ) - } - ) - - await t.test( - 'should support EOLs in a directive (text) label', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - children: [{type: 'text', value: 'c\nd'}] - }, - {type: 'text', value: ' e.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b[c\nd] e.\n' - ) - } - ) - - await t.test( - 'should serialize a directive (text) w/ `attributes`', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - attributes: { - c: 'd', - e: 'f', - g: '', - h: null, - i: undefined, - // @ts-expect-error: check how the runtime handles `number`s - j: 2 - }, - children: [] - }, - {type: 'text', value: ' k.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b{c="d" e="f" g j="2"} k.\n' - ) - } - ) - - await t.test( - 'should serialize a directive (text) w/ `id`, `class` attributes', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - attributes: {class: 'a b\nc', id: 'd', key: 'value'}, - children: [] - }, - {type: 'text', value: ' k.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b{#d .a.b.c key="value"} k.\n' - ) - } - ) - - await t.test( - 'should encode the quote in an attribute value (text)', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - attributes: {x: 'y"\'\r\nz'}, - children: [] - }, - {type: 'text', value: ' k.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b{x="y"\'\r\nz"} k.\n' - ) - } - ) - - await t.test( - 'should encode the quote in an attribute value (text)', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - attributes: {x: 'y"\'\r\nz'}, - children: [] - }, - {type: 'text', value: ' k.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b{x="y"\'\r\nz"} k.\n' - ) - } - ) - - await t.test( - 'should not use the `id` shortcut if impossible characters exist', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - attributes: {id: 'c#d'}, - children: [] - }, - {type: 'text', value: ' e.'} - ] - }, - {extensions: [directiveToMarkdown()]} - ), - 'a :b{id="c#d"} e.\n' - ) - } - ) - - await t.test( - 'should not use the `class` shortcut if impossible characters exist', - async function () { - assert.deepEqual( - toMarkdown( - { - type: 'paragraph', - children: [ - {type: 'text', value: 'a '}, - { - type: 'textDirective', - name: 'b', - attributes: {class: 'c.d e