marked-directive
TypeScript icon, indicating that this package has built-in type declarations

1.0.7 • Public • Published

marked-directive

A marked extension to support directives syntax.

Install

You can install marked-directive using npm or yarn:

npm i marked-directive
# or
yarn add marked-directive

Usage

Try marked-directive on RunKit

Say we have the following file example.md:

# Example

:::main{#foo .bar class="baz" .qux}

[Directives syntax](https://talk.commonmark.org/t/generic-directives-plugins-syntax/444)

::hr{.border-muted}

You can use :i[CSS] (Cascading Style Sheets) to style your :abbr[HTML]{title="HyperText Markup Language"}.

:::

And our module example.js looks as follows:

import { readFileSync } from 'node:fs'
import { Marked } from 'marked'
import { createDirectives } from 'marked-directive'

const html = new Marked()
  .use(createDirectives())
  .parse(readFileSync('example.md', 'utf8'))

console.log(html)

Now, running node example.js yields:

<h1>Example</h1>
<main id="foo" class="bar baz qux">
  <p>
    <a
      href="https://talk.commonmark.org/t/generic-directives-plugins-syntax/444"
      >Directives syntax</a
    >
  </p>
  <hr class="border-muted" />
  <p>
    You can use <i>CSS</i> (Cascading Style Sheets) to style your
    <abbr title="HyperText Markup Language">HTML</abbr>.
  </p>
</main>

Custom Directives

The marked-directive extension accepts a set of custom configurations for your directives. You can specify how the directives are identified, how they should be rendered, and other behavior. The options include:

  • level: The level of the directive, which can be 'container', 'block', or 'inline'. This determines where the directive can be used.
  • marker: The marker string that identifies the directive in the source text.
  • tag: An optional HTML tag that the directive should be rendered as. If not provided, the default tag is used based on the directive level.
  • renderer: A custom rendering function for the directive. This function can be used to customize how the directive is rendered.

Here's an example of custom options:

Say we have the following markdown code:

Custom directives:

::youtube[Dummy video]{vid="9xwazD5SyVg"}

1. @bent10
2. #markdown
3. :emoji[rocket]{title="Go!"}

And whatever is on your mind 🤯.
import { Marked } from 'marked'
import {
  createDirectives,
  presetDirectiveConfigs,
  type DirectiveConfig
} from 'marked-directive'

// defines `:youtube` directive
const youtubeDirective: DirectiveConfig = {
  level: 'block',
  marker: '::',
  renderer(token) {
    if (token.meta.name === 'youtube') {
      return `<iframe width="560" height="315" src="https://www.youtube.com/embed/${
        token.attrs?.vid || ''
      }" title="${
        token.text
      }" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>`
    }

    return false
  }
}

// defines `@mention` directive
const mentionDirective: DirectiveConfig = {
  level: 'inline',
  marker: '@',
  renderer(token) {
    return `<a class="user-mention notranslate" href="/users/${token.meta.name}">${token.meta.name}</a>`
  }
}

// defines `#hashtag` directive
const hashtagDirective: DirectiveConfig = {
  level: 'inline',
  marker: '#',
  renderer(token) {
    return `<a class="hashtag" href="/tags/${token.meta.name}">${token.meta.name}</a>`
  }
}

// defines `:emoji` directive
const emojis = { rocket: '🚀', 'red-exclamation': '❗' } // mock emoji api
const emojiDirective: DirectiveConfig = {
  level: 'inline',
  marker: ':',
  renderer(token) {
    if (token.meta.name === 'emoji') {
      return `<span ${token.attrs?.toString()}>${emojis[token.text]}</span>`
    }

    return false
  }
}

const html = new Marked()
  .use(
    createDirectives([
      ...presetDirectiveConfigs,
      youtubeDirective,
      mentionDirective,
      hashtagDirective,
      emojiDirective
    ])
  )
  .parse(md)

console.log(html)

Try marked-directive on RunKit

Nested Directives

When working with nested directives, it’s important to ensure proper distinction between different levels of nesting. If both the container and its nested items use the same marker character (e.g., :), the number of marker characters should increment with each level of nesting to avoid conflicts and ensure correct parsing.

For example, in the following Markdown snippet, the .container uses four colons (::::) to define its boundaries, while the nested .item uses three colons (:::):

::::{.container}

:::{.item}
Title
Description

![Image](/image.jpg)
:::

::::

Alternatively, you can also use different markers for each level. For example, the .container uses colons (:::), and the .item uses plus signs (+++):

:::{.container}

+++{.item}

### Title

Content ![Image](/image.jpg), with code:

```python
num1 = 5
num2 = 3
sum = num1 + num2
print(f"The sum of {num1} and {num2} is {sum}")
```

+++

:::

To handle these custom markers, set up the directives like this:

import { Marked } from 'marked'
import { createDirectives, presetDirectiveConfigs } from 'marked-directive'

const md = `` // markdown above
const html = new Marked()
  .use(
    createDirectives([
      ...presetDirectiveConfigs,
      // custom directives
      { level: 'container', marker: '::::' },
      { level: 'container', marker: '\\+\\+\\+' }
    ])
  )
  .parse(md)

console.log(html)

Related

See extensions list.

Contributing

We 💛  issues.

When committing, please conform to the semantic-release commit standards. Please install commitizen and the adapter globally, if you have not already.

npm i -g commitizen cz-conventional-changelog

Now you can use git cz or just cz instead of git commit when committing. You can also use git-cz, which is an alias for cz.

git add . && git cz

License

GitHub

A project by Stilearning © 2023-2024.

Package Sidebar

Install

npm i marked-directive

Weekly Downloads

771

Version

1.0.7

License

MIT

Unpacked Size

38.4 kB

Total Files

11

Last publish

Collaborators

  • bent10