Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 66 additions & 32 deletions src/syntax/inline-props.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type MarkdownIt from 'markdown-it'
import type Renderer from 'markdown-it/lib/renderer.mjs'
import type Token from 'markdown-it/lib/token.mjs'

import TokenClass from 'markdown-it/lib/token.mjs'
import { searchProps } from '../parse/props'
Expand Down Expand Up @@ -64,53 +65,53 @@ export const MarkdownItInlineProps: MarkdownIt.PluginWithOptions<MdcInlinePropsO
return

// list item handling
if (token.hidden && next.type === 'inline')
if (token.hidden && next?.type === 'inline')
token = next

if (
token.type === 'inline'
&& token.children?.length === 2
&& token.children[0].type === 'text'
&& token.children[1].type === 'mdc_inline_props'
) {
const props = token.children[1].attrs
token.children.splice(1, 1)
props?.forEach(([key, value]) => {
if (key === 'class')
prev.attrJoin('class', value)
else
prev.attrSet(key, value)
})
}
if (token.type === 'inline')
applyTrailingPropsToParent(token, prev)
})

// When using `::ul` syntax, to wrap with a list,
// we deduplicate the `ul` tag and hide the original one
// when it's exactly the only child of the `mdc_block_open` token
// When `::ul` or `::ol` wraps markdown list content, markdown-it creates
// a nested list token with the same tag as the outer `mdc_block`.
// Hide that inner list only when it is the block's sole child.

const listTypes = {
bullet_list_open: {
closeType: 'bullet_list_close',
tag: 'ul'
},
ordered_list_open: {
closeType: 'ordered_list_close',
tag: 'ol'
}
} as const

tokens.forEach((tokenOpen, index) => {
if (tokenOpen.type !== 'bullet_list_open')
const list = listTypes[tokenOpen.type as keyof typeof listTypes]
if (!list)
return

const prev = tokens[index - 1]
if (!prev || prev.type !== 'mdc_block_open' || prev.tag !== 'ul')
if (!prev || prev.type !== 'mdc_block_open' || prev.tag !== list.tag)
return

// find the matching close token
let closeIndex = index + 1
while (closeIndex < tokens.length) {
const close = tokens[closeIndex]
if (close.type === 'bullet_list_close' && close.level === tokenOpen.level)
if (close.type === list.closeType && close.level === tokenOpen.level)
break
closeIndex += 1
}
const tokenClose = tokens[closeIndex]
if (tokenClose.type !== 'bullet_list_close')
if (tokenClose.type !== list.closeType)
return

// when prev and next are both `mdc_block` and `ul`,
// we hide the original `ul` token
// When the matching `mdc_block` close token has the same list tag,
// the list tokens are the duplicate wrapper and can be hidden.
const next = tokens[closeIndex + 1]
if (next.type === 'mdc_block_close' && next.tag === 'ul') {
if (next.type === 'mdc_block_close' && next.tag === list.tag) {
tokenOpen.hidden = true
tokenClose.hidden = true
}
Expand All @@ -126,6 +127,44 @@ export const MarkdownItInlineProps: MarkdownIt.PluginWithOptions<MdcInlinePropsO
md.renderer.renderInlineAsync = wrapRenderInline(md.renderer.renderInlineAsync as any)
}

function applyAttrs(token: Token, attrs: [string, string][] | null) {
attrs?.forEach(([key, value]) => {
if (key === 'class')
token.attrJoin('class', value)
else
token.attrSet(key, value)
})
}

function applyTrailingPropsToParent(inline: Token, parentOpen: Token) {
const children = inline.children
if (!children || children.length < 2)
return false

const propsIndex = children.length - 1
const props = children[propsIndex]
const previous = children[propsIndex - 1]

if (
props.type !== 'mdc_inline_props'
|| previous.type !== 'text'
) {
return false
}

applyAttrs(parentOpen, props.attrs)

if (previous.content.trim()) {
previous.content = previous.content.replace(/[ \t]+$/, '')
children.splice(propsIndex, 1)
}
else {
children.splice(propsIndex - 1, 2)
}

return true
}

function wrapRenderInline(renderInline: Renderer['renderInline']): Renderer['renderInline'] {
return function (this: Renderer, tokens, options, env) {
tokens = [...tokens]
Expand Down Expand Up @@ -170,12 +209,7 @@ function wrapRenderInline(renderInline: Renderer['renderInline']): Renderer['ren

// console.log('apply', token.attrs, 'to', prev)

token.attrs?.forEach(([key, value]) => {
if (key === 'class')
prev.attrJoin('class', value)
else
prev.attrSet(key, value)
})
applyAttrs(prev, token.attrs)
}
})
return renderInline.call(this, tokens, options, env)
Expand Down
9 changes: 9 additions & 0 deletions test/input/14.list-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@
- List item
::

::ol{.cool-list}
1. List item{.foo}
2. List item
::

- List item{.foo}
- List item
- List item {.bar}
- List item
::ul{.another-list}
- List item
::
- List item
::ol{.another-list}
1. List item
::

> Foo {.foo}
4 changes: 4 additions & 0 deletions test/input/6.inline-props.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Hello{.text-red}

## h2 with _italic_ {style="color: red"}

## h2 with _italic_{style="color: red"}

Hello World{class="text-green text-xl"}

[Link](https://nuxt.com){class="nuxt"}
Expand Down
6 changes: 6 additions & 0 deletions test/input/8.inline-span.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ Hello [World]{.bg-blue-500}!
Hello [World\] Yes]!

Hello [World]{style=""}!

A paragraph [span] {attr=value}

A paragraph [span] {data-spaces="two"}

A paragraph [span]{attr=value}
10 changes: 10 additions & 0 deletions test/output/14.list-handling.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<li class="foo">List item</li>
<li>List item</li>
</ul>
<ol class="cool-list">
<li class="foo">List item</li>
<li>List item</li>
</ol>
<ul>
<li class="foo">List item</li>
<li>
Expand All @@ -14,6 +18,12 @@
<li>List item</li>
</ul>
</li>
<li>
List item
<ol class="another-list">
<li>List item</li>
</ol>
</li>
</ul>
</li>
</ul>
Expand Down
2 changes: 2 additions & 0 deletions test/output/6.inline-props.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<h1 class="text-red">Hello</h1>
<h2 style="color: red">h2 with <em>italic</em></h2>
<h2>h2 with <em style="color: red">italic</em></h2>
<p class="text-green text-xl">Hello World</p>
<p><a href="https://nuxt.com" class="nuxt">Link</a></p>
<p>
Expand Down
3 changes: 3 additions & 0 deletions test/output/8.inline-span.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
<p>Hello <span class="bg-blue-500">World</span>!</p>
<p>Hello <span>World] Yes</span>!</p>
<p>Hello <span style="">World</span>!</p>
<p attr="value">A paragraph <span>span</span></p>
<p data-spaces="two">A paragraph <span>span</span></p>
<p>A paragraph <span attr="value">span</span></p>