0.0.11 • Published 11 months ago

tiny-marked v0.0.11

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

tiny-marked

A tiny markdown subset parser that is ≈ 1kb in size

Can handle:

  • Blockquote
  • Bold
  • Breaklines
  • Email
  • Heading
  • Images
  • Italic
  • Links
  • Unordered lists

Install

$ npm i tiny-marked

Usage example

import {parse} from 'tiny-marked'
import {blockquoteParser} from 'tiny-marked/lib/parsers/blockquote-parser'
import {boldItalicParser} from 'tiny-marked/lib/parsers/bold-italic-parser'
import {breaklinesParser} from 'tiny-marked/lib/parsers/breaklines-parser'
import {headingParser} from 'tiny-marked/lib/parsers/heading-parser'
import {linkParser} from 'tiny-marked/lib/parsers/link-parser'
import {listParser} from 'tiny-marked/lib/parsers/list-parser'

const result = parse('**[text](https://example.com)**', [
  blockquoteParser,
  listParser,
  headingParser,
  linkParser,
  boldItalicParser,
  breaklinesParser,
])

/* result
  [
    {
      type: 'strong',
      value: [
        {
          type: 'a',
          value: ['text'],
          href: 'https://example.com',
        },
      ],
    },
  ]
*/

const result = parse('**bold** *italic* [text](https://example.com)', [
  blockquoteParser,
  listParser,
  headingParser,
  linkParser,
  boldItalicParser,
  breaklinesParser,
])

/* result
  [
    {
      type: 'strong',
      value: ['bold'],
    },
    ' ',
    {
      type: 'em',
      value: ['italic'],
    },
    ' ',
    {
      type: 'a',
      value: ['text'],
      href: 'https://example.com',
    },
  ]
*/

Built in parsers

blockquoteParser

Parses blocks quotes.

import:

import {blockquoteParser} from 'tiny-marked/lib/parsers/blockquote-parser'

Example:

> Hello

listParser

Parses unordered lists

import:

import {listParser} from 'tiny-marked/lib/parsers/list-parser'

Example:

  * Hello
  * hello

headingParser

Parses headings 1-6

import:

import {headingParser} from 'tiny-marked/lib/parsers/heading-parser'

Example:

# Hello
###### Hello

linkParser

Parses links, images, emails

import:

import {linkParser} from 'tiny-marked/lib/parsers/link-parser'

Example:

[link](https//example.com)
![image alt text](https//image-link.com)

boldItalicParser

Parses bold and italic

import:

import {boldItalicParser} from 'tiny-marked/lib/parsers/bold-italic-parser'

Example:

**This text is bold**
*Italic text*

breaklinesParser

Parses break lines

import:

import {breaklinesParser} from 'tiny-marked/lib/parsers/breaklines-parser'

Example:

\n
\r
<br />

Build string or components

Some alternatives to building component or string from parse result.

First

Using recursive map

function build(data) {
  if (typeof data === 'string') {
    return data
  }

  if (data.type === 'strong') {
    const components = data.value.map(build).join('')
    return `<strong>${components}</strong>`
  }

  if (data.type === 'em') {
    const components = data.value.map(build).join('')
    return `<em>${components}</em>`
  }

  if (data.type === 'a') {
    const components = data.value.map(build).join('')
    return `<a href="${data.href}">${components}</a>`
  }

  return data.match
}

const data = [
  {
    type: 'strong',
    value: [
      {
        type: 'a',
        value: ['bold link'],
        href: 'https://example.com',
      },
    ],
  },
  ' ',
  {
    type: 'em',
    value: ['italic'],
  },
]

const components = data.map(build)

console.log(components.join(''))
/* result
  <strong><a href="https://example.com">bold link</a></strong> <em>italic</em>
*/

Second

Using an iterator to flatten the list and give action on when to open or close elements

function* traverse(list: Array<Match | string>) {
  let index = 0
  while (list[index] !== undefined) {
    const item = list[index]
    index += 1

    if (typeof item === 'string') {
      yield {type: 'string', value: item}

      continue
    }

    yield {type: item.type, action: 'open'}

    const iterator = traverse(item.value)
    let message = iterator.next()
    while (!message.done) {
      yield message.value

      message = iterator.next()
    }

    yield {type: item.type, action: 'close'}
  }

  return
}

function build(list) {
  const iterator = traverse(list)
  let str = ''

  let message = iterator.next()
  while (!message.done) {
    const item = message.value

    switch (item.type) {
      case 'string':
        str += item.value
        break
      case 'strong':
        if (item.action === 'open') {
          str += `<strong>`
        } else {
          str += '</strong>'
        }

        break
      case 'a':
        if (item.action === 'open') {
          str += `<a href="${item.href}">`
        } else {
          str += '</a>'
        }

        break
      case 'em':
        if (item.action === 'open') {
          str += `<em>`
        } else {
          str += '</em>'
        }

        break
    }

    message = iterator.next()
  }

  return str
}

const data = [
  {
    type: 'strong',
    value: [
      {
        type: 'a',
        value: ['bold link'],
        href: 'https://example.com',
      },
    ],
  },
  ' ',
  {
    type: 'em',
    value: ['italic'],
  },
]

console.log(build(data))

/** result
  <strong><a href="https://example.com">bold link</a></strong> <em>italic</em>
*/

Create your own parser

Create a function that implements the Parser interface.

Example

woowHello
import {createElement} from 'tiny-marked'

// parser type Parser<'woow'> should match createElement('woow'
const woowParser: Parser<'woow'> = ({parseElements}) => {
  return {
    /**
     * Add a regex
     * This example matches on "woow" then everything until a space or enter
     **/
    regex: /(woow[a-z\d-]+)/gim,

    /**
     * Add a replacer function,
     * The first param will be id (not an uniq id!)
     * Then matching results from your reges, could be multible match params depending on your regex
     **/
    replacer: (id, match) => {
      const content = match.slice(4) // get everything after "woow"

      // create your element, this one is called woow
      // pass your content with the matcher removed (remove "wooow")
      // this content can be parsed again so its importat to remove the matcher or endless recursion will occur
      return createElement('woow', parseElements(content), id)
    },
  }
}

/**
 * match result from woowParser with input "woowHello"
 * [{type: 'woow', value: ['Hello']}]
 **/
0.0.11

11 months ago

0.0.10

11 months ago

0.0.9

11 months ago

0.0.8

11 months ago

0.0.7

11 months ago

0.0.6

11 months ago

0.0.5

11 months ago

0.0.4

11 months ago

0.0.3

11 months ago

0.0.2

11 months ago

0.0.1

11 months ago