您当前的位置:首页 > 计算机 > 服务器 > 万维网络 > 中间件

Markdoc 新一代Markdown文档内容发布框架

时间:12-17来源:作者:点击数:

今天给大家介绍的主题是 Markdoc,即由 Stripe 开发的一种基于 Markdown 的文档格式和内容发布框架。

Markdoc官网:https://markdoc.dev/

Markdoc 新一代Markdown文档内容发布框架

什么是 Markdoc

Markdoc 是一种基于 Markdown 的文档格式和内容发布框架, 它由是 Stripe 内部设计的,以满足面向用户的产品文档的需求。 Markdoc 使用标签和注释的自定义语法扩展了 Markdown,提供了一种为个人用户定制内容并引入交互元素的方法。

可以通过如下方式快速使用 Markdoc:

npm install @markdoc/markdoc
// 或者
yarn add @markdoc/markdoc

安装后就可以在代码中直接引用:

const Markdoc = require('@markdoc/markdoc');
//或者
import Markdoc from '@markdoc/markdoc';

然后调用 parse, transform 和 render 函数来渲染内容。

const source = '# Markdoc';
const ast = Markdoc.parse(source);
const content = Markdoc.transform(ast, /* config */);
const html = Markdoc.renderers.html(content);

目前 Markdoc 在 Github 上有 6.2k 的 star、150+ 的 fork、超过 1k 的项目依赖它,是一个值得长期关注的前端项目。

Markdoc 如何工作

Markdoc 的三大渲染器

按照设计,Markdoc 不是一种成熟的模板语言,并且不允许混合任意代码和内容。 然而,它是一种完全声明的格式,从上到下都是机器可读的:它解析为可以遍历的数据结构,以支持强大的静态分析、验证和程序化内容转换。

Markdoc 渲染器解释自定义标签和节点定义,将文档数据结构转换为可渲染节点树,最终转换为所需的输出格式。 Markdoc 框架目前包括三个渲染器:

  • 一个 HTML 字符串渲染器
  • 一个将文档转换为 JavaScript 代码的静态 React 渲染器
  • 一个将可渲染树节点直接转换为 React 元素的动态 React 渲染器

Markdoc 的 React 渲染器使在 Markdown 内容中使用 React 组件成为可能,其支持选项卡切换器和可折叠部分等交互功能。 可以实现引入对其他输出格式和客户端框架的支持的自定义渲染器。

解析器生成器 PEG.js

PEG.js 是一个简单的 JavaScript 解析器生成器,可生成具有出色错误报告的快速解析器。开发者可以使用它来处理复杂的数据或计算机语言,并轻松构建转换器、解释器、编译器和其他工具。

Markdoc 新一代Markdown文档内容发布框架

PEG.js 具有以下显著特征:

  • 简单而富有表现力的语法
  • 集成词法和句法分析
  • 解析器具有开箱即用的出色错误报告
  • 基于解析表达式语法形式主义——比传统的 LL(k) 和 LR(k) 解析器更强大
  • 可从浏览器、命令行或通过 JavaScript API 使用

目前 PEG.js 在 Github 上有 4.6k 的 star、450+ 的 fork、191k 的项目依赖它。可以使用下面的示例快速使用 PEG.js:

npm install -g pegjs
$ pegjs -o arithmetics-parser.js arithmetics.pegjs

markdown-it 的助力

Markdoc 的解析器建立在一个名为 markdown-it 的流行开源 Markdown 库之上。 Markdoc 使用 markdown-it 作为标记器,从 markdown-it 输出的标记数组构建抽象语法树 (AST)。

Markdown-it 解析器的特性包括:具有 100% CommonMark 支持、 扩展支持、语法插件、安全和极致的速度。目前 Markdown-it 在 Github 上有 15.2k 的 star、1.6k 的 fork、432k 的项目依赖它。可以使用下面的示例快速使用 markdown-it:

// node.js经典方式
var MarkdownIt = require('markdown-it'),
  md = new MarkdownIt();
var result = md.render('# markdown-it rulezz!');

// node.js的语法糖
var md = require('markdown-it')();
var result = md.render('# markdown-it rulezz!');

// 没有 AMD 的浏览器,在脚本加载时添加到 window
// 注意,“markdownit”中没有破折号。
var md = window.markdownit();
var result = md.render('# markdown-it rulezz!');

Markdoc 的自定义标记语法在 markdown-it 插件中实现, 解析标记语法的逻辑是从 peg.js 语法生成的。 Markdoc 有自己专用的渲染架构,而不是依赖 markdown-it 来生成它的输出。 为了处理 Markdoc 的自定义标签和支持多种输出格式,其开发了一个独立的渲染系统。

Markdoc 在 Markdown 中添加标记

Markdoc 选择 Markdown 作为起点,因为它易于阅读和推理,许多工程师和技术作家已经非常熟悉,并且得到众多现有工具的大型生态系统的广泛支持。 然而,Markdown 本身并不适合编写复杂的、高度结构化的内容,如文档等等。

Markdoc 提供了一个可扩展的系统,用于定义可以在 Markdown 内容中无缝使用的自定义标签。 使用自定义标记语法,开发者能够表达更精细的文档层次结构,插入交互式组件,并支持条件内容、内容包含和变量插值等功能。 Markdoc 对 Markdown 语法的扩展被设计为可组合且侵入性最小,在不影响可读性的情况下提供关键功能。

比如下面示例使用 .my-class-name 和 #my-id 作为 class=my-class-name 和 id=my-id 的简写。

# Examples {% #examples %}

{% table .striped #exampletable %}
- One
- Two
- Three
{% /table %}

Markdoc 也允许开发者为每个标签配置自定义属性类型,比如以下示例定义了 Callout 标记的属性。 默认情况下,该属性设置为注意并根据匹配数组进行验证。

{
  render: 'Callout',
  children: ['paragraph', 'tag', 'list'],
  attributes: {
    type: {
      type: String,
      default: 'note',
      required: true,
      matches: ['caution', 'check', 'note', 'warning'],
      errorLevel: 'critical',
    },
  }
};

在 React 中使用 Markdoc

Markdoc 支持使用 React 开箱即用地渲染 Markdoc 语法。按照以下步骤使用 create-react-app 和 express 构建 Markdoc 应用程序。

按照 create-react-app 入门步骤创建初始应用程序

设置 Markdoc 架构

schema/
├── Callout.markdoc.js
└── heading.markdoc.js

schema/Callout.markdoc.js 的内容如下:

// schema/Callout.markdoc.js

module.exports = {
  render: 'Callout',
  children: ['paragraph', 'tag', 'list'],
  attributes: {
    type: {
      type: String,
      default: 'note',
      matches: ['check', 'error', 'note', 'warning'],
    },
  },
};

schema/heading.markdoc.js 的内容如下:

// schema/heading.markdoc.js

const { nodes } = require('@markdoc/markdoc');

function generateID(children, attributes) {
  if (attributes.id && typeof attributes.id === 'string') {
    return attributes.id;
  }
  return children
    .filter((child) => typeof child === 'string')
    .join(' ')
    .replace(/[?]/g, '')
    .replace(/\s+/g, '-')
    .toLowerCase();
}

module.exports = {
  ...nodes.heading,
  transform(node, config) {
    const base = nodes.heading.transform(node, config);
    base.attributes.id = generateID(base.children, base.attributes);
    return base;
  },
};

解析服务器上的 Markdoc 文档

// ...
const rawText = fs.readFileSync(file, 'utf-8');
const ast = Markdoc.parse(rawText);

在服务端调用 Markdoc.transform

// server.js

const express = require('express');

const app = express();

const callout = require('./schema/callout.markdoc');
const heading = require('./schema/heading.markdoc');
// ...

app.get('/markdoc', (req, res) => {
  const ast = contentManifest[req.query.path];

  const config = {
    tags: {
      callout,
    },
    nodes: {
      heading,
    },
    variables: {},
  };

  const content = Markdoc.transform(ast, config);

  return res.json(content);
});

app.listen(4242, () => {
  console.log(`Example app listening on port ${4242}`);
});

在客户端调用 Markdoc.renderers.react

// src/App.js

import React from 'react';
import Markdoc from '@markdoc/markdoc';

import { Callout } from './Callout';

export default function App() {
  const [content, setContent] = React.useState(null);

  React.useEffect(() => {
    (async () => {
      const response = await fetch(
        `/markdoc?` + new URLSearchParams({ path: window.location.pathname }),
        { headers: { Accept: 'application/json' } }
      );

      if (response.status === 404) {
        setContent('404');
        return;
      }

      const content = await response.json();
      setContent(content);
    })();
  }, []);

  if (content === '404') {
    return <p>Page not found.</p>;
  }

  if (!content) {
    return <p>Loading...</p>;
  }

  const components = {
    Callout,
  };

  return Markdoc.renderers.react(content, React, { components });
}

启动客户端和服务器

npm run start:client
npm run start:server

除了与React集成外,Markdoc还支持与HTML、Next.js等集成。

参考资料

https://markdoc.dev/docs/overview

https://github.com/markdown-it/markdown-it

https://pegjs.org/

https://github.com/pegjs/pegjs

https://markdoc.dev/docs/attributes

https://stripe.com/blog/markdoc

https://markdoc.dev/docs/examples/react#setup

https://transloadit.com/blog/2022/06/devtimes-055/

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门