{"id":19437997,"url":"https://github.com/architect/arcdown","last_synced_at":"2025-04-24T21:32:38.903Z","repository":{"id":40559941,"uuid":"484535990","full_name":"architect/arcdown","owner":"architect","description":"A small stack of Markdown tools configured using some preferred conventions for creating technical content rendered and served from a cloud function.","archived":false,"fork":false,"pushed_at":"2024-02-22T17:17:27.000Z","size":187,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-03T12:02:34.442Z","etag":null,"topics":["markdown"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/arcdown","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/architect.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","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}},"created_at":"2022-04-22T18:35:16.000Z","updated_at":"2024-12-16T09:54:33.000Z","dependencies_parsed_at":"2024-02-20T20:28:11.839Z","dependency_job_id":"7e8cace7-cf30-47fc-a1e2-ef777ba87fdc","html_url":"https://github.com/architect/arcdown","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/architect%2Farcdown","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/architect%2Farcdown/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/architect%2Farcdown/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/architect%2Farcdown/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/architect","download_url":"https://codeload.github.com/architect/arcdown/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250713167,"owners_count":21475140,"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"],"created_at":"2024-11-10T15:16:29.354Z","updated_at":"2025-04-24T21:32:38.634Z","avatar_url":"https://github.com/architect.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/architect/assets.arc.codes/raw/main/public/architect-logo-light-500b%402x.png\"\u003e\n  \u003cimg alt=\"Architect Logo\" width=\"500px\" src=\"https://github.com/architect/assets.arc.codes/raw/main/public/architect-logo-500b%402x.png\"\u003e\n\u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/architect/arcdown/actions?query=workflow%3A%22Node+CI%22\"\u003e\u003cimg src=https://github.com/architect/arcdown/workflows/Node%20CI/badge.svg alt=\"GitHub CI status\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\" alt=\"Apache-2.0 License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Arcdown: [Architect](https://arc.codes)'s Markdown Renderer\n\n\u003e A small stack of Markdown tools (built on `markdown-it`) configured using the Architect team's preferred conventions for creating documentation and articles rendered and served from a cloud function.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n## Contents\n\n\u003c!-- make sure when updating heading text, this outline is also updated --\u003e\n\n1. [Usage](#usage)\n    1. [Installation](#installation)\n    1. [Example](#example)\n    1. [Render Result](#render-result)\n1. [Configuration](#configuration)\n    1. [MarkdownIt Renderer](#markdown-it-config-markdownit)\n    1. [Plugin Overrides](#plugin-overrides-pluginoverrides)\n    1. [User-Provided Plugins](#user-provided-plugins-plugins)\n    1. [Highlight.js Config](#highlightjs-hljs-config-hljs)\n1. [Development \u0026 Contributing](#development--contributing)\n    1. [FAQs \u0026 Decisions](#faqs--decisions)\n    1. [Credits](#credits)\n    1. [Todo](#todo)\n\n\u003c/td\u003e\n\u003ctd width=\"600px\" valign=\"top\"\u003e\n\n## What is this?\n\nArcdown is an opinionated toolchain to create technical content from Markdown source files as quickly as possible to enable **on-the-fly rendering** in a Lambda (or any server) runtime.\n\n### Features\n\n* Document table of contents creator\n* Code syntax highlighter\n* Automatic frontmatter parsing\n* Generated HTML optimizations\n* Helpful return values\n\nAll built-ins are configurable and extensible.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n# Usage\n\n## Installation\n\n```shell\nnpm install arcdown\n```\n\n_ESM only, requires Node.js v14+_\n\n## Example\n\nThe simplest usage is to just pass `Arcdown.render` a string of Markdown:\n\n```javascript\nimport { readFileSync } from 'node:fs'\nimport { Arcdown } from 'arcdown'\n\nconst mdString = `\n---\ntitle: Hello World\ncategory: Examples\n---\n\n## Foo Bar\n\nlorem ipsum _dolor_ sit **amet**\n\n[Architect](https://arc.codes/)\n`.trim()\n\nconst arcdown = new Arcdown()\nconst {\n  frontmatter, // attributes from frontmatter\n  html,        // the good stuff: HTML!\n  slug,        // a URL-friendly slug\n  title,       // document title from the frontmatter\n  tocHtml,     // an HTML table of contents\n} = await arcdown.render(mdString)\n\nconst fromFile = await arcdown.render(readFileSync('../docs/some-markdown.md', 'utf-8'))\n```\n\n\u003e ⚙️  See [below for configuration options](#configuration).\n\n## Render Result\n\n`Arcdown.render` returns a `RenderResult` object with 4 strings plus any document \"frontmatter\".\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `html: string`\n\nThe Markdown document contents as HTML, unmodified, rendered by `markdown-it`.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst { html } = await arcdown.render(mdString)\n\nconst document = `\n\u003chtml\u003e\n\u003cbody\u003e\n  \u003cmain\u003e${html}\u003c/main\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n`\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `tocHtml: string`\n\nThe document's table of contents as HTML (nested unordered lists).\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst { tocHtml, html } = await arcdown.render(mdString)\n\nconst document = `\n\u003chtml\u003e\n\u003cbody\u003e\n  \u003carticle\u003e${html}\u003c/article\u003e\n  \u003caside\u003e${tocHtml}\u003c/aside\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n`\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `title: string`\n\nThe document title, lifted from the document's frontmatter.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst { title } = await arcdown.render(mdString)\n\nconsole.log(`Rendered \"${title}\"`)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `slug: string`\n\nA URL-friendly slug of the title. (possibly empty) Synonymous with links in the table of contents.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst { slug } = await arcdown.render(mdString)\n\nconst docLink = `http://my-site.com/docs/${slug}`\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `frontmatter: object`\n\nAll remaining frontmatter. (possibly empty)\n\nThe document's frontmatter is parsed by [`gray-matter`](https://github.com/jonschlinkert/gray-matter) and directly returned here.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst { frontmatter } = await arcdown.render(file, options)\n\nconst sortedTags = frontmatter.tags.sort()\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n# Configuration\n\nArcdown is set up to be used without any configuration. Out-of-the-box it uses defaults and conventions preferred by the Architect team (Architect project not required).\n\nHowever, the renderer is customizable and extensible with a `RendererOptions` object.\n\n\u003e 🪧  See ./example/ for a kitchen sink demo.\n\n## `markdown-it` Config: `markdownIt`\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `markdownIt`\n\nConfigure the core [`markdown-it` renderer](https://github.com/markdown-it/markdown-it).  \nThis config is passed directly to `new MarkdownIt()`\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  markdownIt: { linkify: false },\n})\n```\n\nBy default, `html`, `linkify`, and `typographer` are enabled.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Plugin Overrides: `pluginOverrides`\n\nThree plugins are provided out-of-the-box and applied in a specific order.\n\nSet configuration for each plugin by passing a keyed `RendererOptions.pluginOverrides` object.\n\n\u003e ⛔️  Disable a plugin by setting its key in `pluginOverrides` to `false`.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `markdownItClass`\n\nApply class names to each generated element based on its tag name. Provide a map of element names to an array of classes to be applied.  \nPerfect for utility class libraries.\n\nThis plugin is disabled unless configuration is provided.\n\n[`markdown-it-class` docs ](https://github.com/HiroshiOkada/markdown-it-class)\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  pluginOverrides: {\n    markdownItClass: {\n      // an element =\u003e class map\n      h2: [ 'title' ],\n      p: [ 'prose' ],\n    }\n  },\n})\n```\n\nFor performance reasons, this plugin was modified and bundled to `./src/vendor/`\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `markdownItExternalAnchor`\n\nMark all external links (links starting with \"http[s]://\") with `target=_blank` and an optional class.\n\n`markdown-it-external-anchor` defaults are used in Arcdown.\n\n[`markdown-it-external-anchor` docs](https://github.com/binyamin/markdown-it-external-anchor)\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  pluginOverrides: {\n    markdownItExternalAnchor: {\n      domain: 'arc.codes',\n      class:'external',\n    },\n  },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `markdownItAnchor`\n\nA markdown-it plugin that adds an id attribute to headings and optionally permalinks.\n\n[`markdown-it-anchor` docs](https://github.com/valeriangalliat/markdown-it-anchor)\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  pluginOverrides: {\n    markdownItAnchor: {\n      tocClassName: 'pageToC',\n    },\n  },\n})\n```\n\n`markdown-it-anchor` is pre-configured with:\n\n```javascript\n{\n  tabIndex: false,\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `markdownItToc`\n\nA table of contents (TOC) plugin for Markdown-it with focus on semantic and security. Made to work gracefully with markdown-it-anchor.\n\n[`markdown-it-toc-done-right` docs](https://github.com/nagaozen/markdown-it-toc-done-right)\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  pluginOverrides: {\n    markdownItToc: {\n      containerClass: 'pageToC',\n    },\n  },\n})\n```\n\n`markdown-it-toc-done-right` enables users to include a copy of the table of contents in their markdown:\n\n```markdown\nMy Table of Contents:\n\n${toc}\n\n# The rest of\n\n## My document\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## User-Provided Plugins: `plugins`\n\nIt is possible to pass additional `markdown-it` plugins to Arcdown's renderer by populating `RendererOptions.plugins`.  \nPlugins can be provided in two ways and will be applied after the default plugins bundled with Arcdown.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `plugins`\n\nThe simplest method for extending `markdown-it` is to import a plugin function and provide it directly.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nimport markdownItAttrs from 'markdown-it-attrs'\n\nconst arcdown = new Arcdown({\n  plugins: { markdownItAttrs },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `plugins` with options\n\nIf a plugin requires options, provide the `markdown-it` plugin as a tuple where the first item is the function and the second is the plugin options.\n\nHere the key name provided does not matter.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nimport markdownItEmoji from 'markdown-it-emoji'\n\nconst arcdown = new Arcdown({\n  plugins: {\n    mdMoji: [\n      markdownItEmoji, // the plugin function\n      { shortcuts: { laughing: ':D' } }, // options\n    ],\n  },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Highlight.js (hljs) Config: `hljs`\n\nA custom `highlight()` method backed by [Highlight.js](https://highlightjs.org/) is provided to the internal `markdown-it` renderer. Arcdown will detect languages used in fenced code blocks in the provided Markdown string and attempt to register _just_ those languages in hljs.\n\n\u003e ⚠️  Currently, shorthand aliases for languages are not supported.  \nFull language names should be used with Markdown code fences. Instead of `js`, use `javascript`\n\nSet Highlight.js configuration by passing a keyed `RendererOptions.hljs` object.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `classString: string`\n\nA string that will be added to each `\u003cpre class=\"\"\u003e` wrapper tag for highlighted code blocks.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  hljs: {\n    classString: 'hljs relative mb-2',\n  },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `ignoreIllegals: boolean`\n\nPassed directly to `hljs.highlight()`. [The docs](https://highlightjs.readthedocs.io/en/latest/api.html#highlight) say:\n\n\u003e when true forces highlighting to finish even in case of detecting illegal syntax for the language[...]\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nconst arcdown = new Arcdown({\n  hljs: {\n    ignoreIllegals: false,\n  },\n})\n```\n\n`ignoreIllegals: true` is the default, but can be set by the user.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `languages: object`\n\nAdditional language syntaxes can be added from third party libraries.  \nIf needed, Highlight.js built-in languages can be disabled by setting their key to `false`.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nimport leanSyntax from 'highlightjs-lean'\n\nconst arcdown = new Arcdown({\n  hljs: {\n    languages: {\n      lean: leanSyntax, // add lean\n      powershell: false, // disallow powershell\n    },\n  },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `sublanguages: object`\n\nDeclare languages that should be registered when a specific language is detected.\n\nA common use-case is registering `'xml'` for `'javascript'` to enable HTML highlighting for `html` string templates.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nimport leanSyntax from 'highlightjs-lean'\n\nconst arcdown = new Arcdown({\n  hljs: {\n    sublanguages: {\n      javascript: [ 'xml' ],\n    },\n  },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"400px\" valign=\"top\"\u003e\n\n### `plugins: object[]`\n\nHighlight.js plugins can be passed to Arcdown's highlighter as an array of objects or class instances with functions keyed as hljs callbacks.\n\nSee [the hljs plugin docs](https://highlightjs.readthedocs.io/en/latest/plugin-api.html) for more info.\n\n\u003c/td\u003e\n\u003ctd width=\"600px\"\u003e\u003cbr\u003e\n\n```javascript\nclass CodeFlipper {\n  constructor(options) {\n    this.token = options.token\n  }\n\n  'after:highlight'(result) {\n    result.value = result.value\n      .split(this.token)\n      .reverse()\n      .join(this.token)\n  }\n}\n\nconst arcdown = new Arcdown({\n  hljs: {\n    plugins: [new CodeFlipper({ token: '\\n' })],\n  },\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n# Development \u0026 Contributing\n\nA couple plugins have been forked and/or vendored locally to this package. This has been done to increase performance and render speed.\n\nArcdown is not attached to any single package, plugin, or even to the core rendering engine, so long as the resulting features are maintained.  \nSuggestions and PRs welcome 🙏\n\n## FAQs \u0026 Decisions\n\n### **Why `markdown-it`?**\nA great balance of speed, stability, adoption, and extensibility.\n\n### **Why Highlight.js?**\nMost syntax highlighters are not fast enough for server-side rendering. hljs was tuned to work on slow client machines and performs well on a server.  \nThat said, [`starry-night`](https://github.com/wooorm/starry-night) is really interesting.\n\n### **Why plugin ___?**\nBecause we used it a lot building docs sites and technical blogs.\n\n## Credits\n\nIn no particular order\n\n* markdown-it and their community for a solid .md ecosystem\n* highlight.js for a battle-tested highlighter\n* Architect and Begin for helping test/break things\n* @galvez for the rad readme.md formatting conventions\n\n## Todo\n\n- [x] additional testing\n- [x] type defs\n- [x] benchmarks (try against remark)\n- [x] look for hljs perf increases\n- [ ] expand typings with definitions from markdown-it\n- [ ] web component enhancements 😏\n- [ ] CLI for static file creation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchitect%2Farcdown","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farchitect%2Farcdown","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchitect%2Farcdown/lists"}