{"id":18157135,"url":"https://github.com/richie-south/marked","last_synced_at":"2025-05-06T21:08:28.108Z","repository":{"id":257821768,"uuid":"871044846","full_name":"richie-south/marked","owner":"richie-south","description":"A ≈1kb markdown parser with tree output and Typescript typings","archived":false,"fork":false,"pushed_at":"2024-11-19T10:01:36.000Z","size":508,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-06T21:08:20.612Z","etag":null,"topics":["markdown","markdown-parser","parser"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/tiny-marked","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/richie-south.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-11T06:53:20.000Z","updated_at":"2025-04-24T13:53:53.000Z","dependencies_parsed_at":"2024-10-17T18:27:22.035Z","dependency_job_id":null,"html_url":"https://github.com/richie-south/marked","commit_stats":null,"previous_names":["richie-south/marked"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richie-south%2Fmarked","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richie-south%2Fmarked/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richie-south%2Fmarked/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richie-south%2Fmarked/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/richie-south","download_url":"https://codeload.github.com/richie-south/marked/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252769423,"owners_count":21801378,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["markdown","markdown-parser","parser"],"created_at":"2024-11-02T06:05:28.154Z","updated_at":"2025-05-06T21:08:28.086Z","avatar_url":"https://github.com/richie-south.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tiny-marked\n\nA tiny markdown subset parser that is ≈ 1kb in size.\nOutputs a tree structure instead of completed string. This way you can handle that logic your self and render it in any way.\nRun it on frontend with react or other types of components, use Nodejs to prerender / analyze content.\n\n**Can handle:**\n\n\u003e - Blockquote\n\u003e - Bold\n\u003e - Breaklines\n\u003e - Email\n\u003e - Heading\n\u003e - Images\n\u003e - Italic\n\u003e - Links\n\u003e - Unordered lists\n\u003e - footnote\n\u003e - strikethrough\n\u003e - block\n\n**Install**\n\n```\n$ npm i tiny-marked\n```\n\n**Usage example**\n\n```typescript\nimport {parse} from 'tiny-marked'\nimport {blockquoteParser} from 'tiny-marked/lib/parsers/blockquote-parser'\nimport {boldItalicParser} from 'tiny-marked/lib/parsers/bold-italic-parser'\nimport {breaklinesParser} from 'tiny-marked/lib/parsers/breaklines-parser'\nimport {headingParser} from 'tiny-marked/lib/parsers/heading-parser'\nimport {linkParser} from 'tiny-marked/lib/parsers/link-parser'\nimport {listParser} from 'tiny-marked/lib/parsers/list-parser'\nimport {footnoteParser} from 'tiny-marked/lib/parsers/footnote-parser'\nimport {strikeParser} from 'tiny-marked/lib/parsers/strike-parser'\nimport {blockParser} from 'tiny-marked/lib/parsers/block-parser'\n\nconst result = parse('**[text](https://example.com)**', [\n  blockquoteParser,\n  listParser,\n  headingParser,\n  linkParser,\n  boldItalicParser,\n  breaklinesParser,\n  footnoteParser,\n  strikeParser,\n  blockParser,\n])\n\n/* result\n  [\n    {\n      type: 'strong',\n      value: [\n        {\n          type: 'a',\n          value: ['text'],\n          attributes: {\n            href: 'https://example.com',\n          }\n        },\n      ],\n    },\n  ]\n*/\n\nconst result = parse('**bold** *italic* [text](https://example.com)', [\n  blockquoteParser,\n  listParser,\n  headingParser,\n  linkParser,\n  boldItalicParser,\n  breaklinesParser,\n  footnoteParser,\n  strikeParser,\n  blockParser,\n])\n\n/* result\n  [\n    {\n      type: 'strong',\n      value: ['bold'],\n    },\n    ' ',\n    {\n      type: 'em',\n      value: ['italic'],\n    },\n    ' ',\n    {\n      type: 'a',\n      value: ['text'],\n      attributes: {\n        href: 'https://example.com',\n      }\n    },\n  ]\n*/\n```\n\n## Built in parsers\n\n### blockquoteParser\n\nParses blocks quotes.\n\nimport:\n\n```typescript\nimport {blockquoteParser} from 'tiny-marked/lib/parsers/blockquote-parser'\n```\n\nExample:\n\n```\n\u003e Hello\n```\n\nResult:\n\n```typescript\n// \u003e block\n{\n  type: 'blockquote',\n  value: ['block'],\n  attributes: {},\n}\n```\n\n### listParser\n\nParses unordered lists\n\nimport:\n\n```typescript\nimport {listParser} from 'tiny-marked/lib/parsers/list-parser'\n```\n\nExample:\n\n```\n  * Hello\n  * hello\n```\n\nResult:\n\n```typescript\n// * item\n{\n  type: 'li',\n  value: ['item'],\n  attributes: {},\n}\n```\n\n### headingParser\n\nParses headings 1-6\n\nimport:\n\n```typescript\nimport {headingParser} from 'tiny-marked/lib/parsers/heading-parser'\n```\n\nExample:\n\n```\n# Hello\n###### Hello\n```\n\nResult:\n\n```typescript\n// # title\n{\n  type: 'h1',\n  value: ['title'],\n  attributes: {},\n}\n```\n\n### linkParser\n\nParses links, images, emails\n\nimport:\n\n```typescript\nimport {linkParser} from 'tiny-marked/lib/parsers/link-parser'\n```\n\nExample:\n\n```\n[link](https//example.com)\n![image alt text](https//image-link.com)\n```\n\nResult:\n\n```typescript\n// '[text](https://example.com)'\n{\n  type: 'a',\n  value: ['text'],\n  attributes: {href: 'https://example.com'},\n}\n```\n\n### boldItalicParser\n\nParses bold and italic\n\nimport:\n\n```typescript\nimport {boldItalicParser} from 'tiny-marked/lib/parsers/bold-italic-parser'\n```\n\nExample:\n\n```\n**This text is bold**\n*Italic text*\n```\n\nResult:\n\n```typescript\n// **title**\n{\n  type: 'strong',\n  value: ['title'],\n  attributes: {},\n}\n```\n\n### breaklinesParser\n\nParses break lines\n\nimport:\n\n```typescript\nimport {breaklinesParser} from 'tiny-marked/lib/parsers/breaklines-parser'\n```\n\nExample:\n\n```\n\\n\n\\r\n\u003cbr /\u003e\n```\n\nResult:\n\n```typescript\n// \u003cbr/\u003e\n{\n  type: 'br',\n  value: ['\u003cbr/\u003e'],\n  attributes: {},\n}\n```\n\n### footnoteParser\n\nParses footnotes / references\n\nimport:\n\n```typescript\nimport {footnoteParser} from 'tiny-marked/lib/parsers/footnote-parser'\n```\n\nExample:\n\n```\nthis is a footnote [^1]\n\n\n[^1]: it references this\n```\n\nResult:\n\n```typescript\n// [^note]\n{\n  type: \"footnote\",\n  value: [\"note\"],\n  attributes: {}\n}\n\n// [^note]: result`\n{\n  type: \"footnote\",\n  value: [\"note\"],\n  attributes: {\"end\": true}\n}\n\n```\n\n### strikethroughParser\n\nParses strikethroughs\n\nimport:\n\n```typescript\nimport {strikeParser} from 'tiny-marked/lib/parsers/strike-parser'\n```\n\nExample:\n\n```\n~~text~~\n```\n\nResult:\n\n```typescript\n// ~~Striked~~\n{\n  type: 's',\n  value: ['Striked'],\n  attributes: {},\n}\n```\n\n### blockParser\n\nParses blocks / codeblocks\n\nimport:\n\n```typescript\nimport {blockParser} from 'tiny-marked/lib/parsers/block-parser'\n```\n\nExample:\n\n````\n\\```\ncontent\n\\```\n````\n\nResult:\n\n```typescript\n// `block`\n{\n  type: 'code',\n  value: ['block'],\n  attributes: {},\n}\n```\n\n# Build string or components\n\nIf you want to go through the result and build components, strings, analyze etc, i have two examples provided here.\n\n**First**\n\nUsing recursive map, simply run a map function on every child value. This would fit almost every usecase the best.\n\n```typescript\nfunction build(data) {\n  if (typeof data === 'string') {\n    return data\n  }\n\n  if (data.type === 'strong') {\n    const components = data.value.map(build).join('')\n    return `\u003cstrong\u003e${components}\u003c/strong\u003e`\n  }\n\n  if (data.type === 'em') {\n    const components = data.value.map(build).join('')\n    return `\u003cem\u003e${components}\u003c/em\u003e`\n  }\n\n  if (data.type === 'a') {\n    const components = data.value.map(build).join('')\n    return `\u003ca href=\"${data.attributes.href}\"\u003e${components}\u003c/a\u003e`\n  }\n\n  return data.match\n}\n\nconst data = [\n  {\n    type: 'strong',\n    value: [\n      {\n        type: 'a',\n        value: ['bold link'],\n        attributes: {\n          href: 'https://example.com',\n        },\n      },\n    ],\n  },\n  ' ',\n  {\n    type: 'em',\n    value: ['italic'],\n  },\n]\n\nconst components = data.map(build)\n\nconsole.log(components.join(''))\n/* result\n  \u003cstrong\u003e\u003ca href=\"https://example.com\"\u003ebold link\u003c/a\u003e\u003c/strong\u003e \u003cem\u003eitalic\u003c/em\u003e\n*/\n```\n\n**Second**\n\nUsing an iterator to flatten the list and give action on when to open or close elements\n\n```typescript\nfunction* traverse(list: Array\u003cMatch | string\u003e) {\n  let index = 0\n  while (list[index] !== undefined) {\n    const item = list[index]\n    index += 1\n\n    if (typeof item === 'string') {\n      yield {type: 'string', value: item}\n\n      continue\n    }\n\n    yield {type: item.type, action: 'open'}\n\n    const iterator = traverse(item.value)\n    let message = iterator.next()\n    while (!message.done) {\n      yield message.value\n\n      message = iterator.next()\n    }\n\n    yield {type: item.type, action: 'close'}\n  }\n\n  return\n}\n\nfunction build(list) {\n  const iterator = traverse(list)\n  let str = ''\n\n  let message = iterator.next()\n  while (!message.done) {\n    const item = message.value\n\n    switch (item.type) {\n      case 'string':\n        str += item.value\n        break\n      case 'strong':\n        if (item.action === 'open') {\n          str += `\u003cstrong\u003e`\n        } else {\n          str += '\u003c/strong\u003e'\n        }\n\n        break\n      case 'a':\n        if (item.action === 'open') {\n          str += `\u003ca href=\"${item.attributes.href}\"\u003e`\n        } else {\n          str += '\u003c/a\u003e'\n        }\n\n        break\n      case 'em':\n        if (item.action === 'open') {\n          str += `\u003cem\u003e`\n        } else {\n          str += '\u003c/em\u003e'\n        }\n\n        break\n    }\n\n    message = iterator.next()\n  }\n\n  return str\n}\n\nconst data = [\n  {\n    type: 'strong',\n    value: [\n      {\n        type: 'a',\n        value: ['bold link'],\n        attributes: {\n          href: 'https://example.com',\n        },\n      },\n    ],\n  },\n  ' ',\n  {\n    type: 'em',\n    value: ['italic'],\n  },\n]\n\nconsole.log(build(data))\n\n/** result\n  \u003cstrong\u003e\u003ca href=\"https://example.com\"\u003ebold link\u003c/a\u003e\u003c/strong\u003e \u003cem\u003eitalic\u003c/em\u003e\n*/\n```\n\n## Create your own parser\n\nCreate a function that implements the `Parser` interface.\n\n**Example**\n\n```\nwoowHello\n```\n\n```typescript\nimport {createElement} from 'tiny-marked'\n\n// parser type Parser\u003c'woow'\u003e should match createElement('woow', ..)\nconst woowParser: Parser\u003c'woow'\u003e = ({parseElements}) =\u003e {\n  return {\n    /**\n     * Required\n     * Add a regex\n     * This example matches on \"woow\" then everything until a space or enter.\n     **/\n    regex: /(woow[a-z\\d-]+)/gim,\n\n    /**\n     * Optional\n     * Ignore nested parsing by adding name of parsers.\n     * example: adding 'boldItalicParser' for linkParser removes abillity of bolded link: **[text](example.se)**\n     **/\n    ingore: []\n\n    /**\n     * Required\n     * Add a replacer function, runs on regex match\n     * The first param will be id (not an uniq id!)\n     * Then matching results from your reges, could be multible match params depending on your regex.\n     **/\n    replacer: (id, match) =\u003e {\n      const content = match.slice(4) // get everything after \"woow\"\n\n      // create your element, this one is called woow\n      // pass your content with the matcher removed (remove \"wooow\")\n      // this content can be parsed again so its importat to remove the matcher or endless recursion will occur.\n      // You can also use the `ignore` property to prevent self reccursion if needed.\n      return createElement('woow', id, parseElements(content), {\n        /* place custom attributes here, etc href link */\n        custom: 'example',\n      })\n    },\n  }\n}\n\n/**\n * match result from woowParser with input \"woowHello\"\n * [{type: 'woow', value: ['Hello'], attributes: { custom: 'example' } }]\n **/\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichie-south%2Fmarked","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frichie-south%2Fmarked","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichie-south%2Fmarked/lists"}