在Hexo中使用Asciidoc格式编写文章

在Hexo中使用Asciidoc格式编写文章

前言

虽然markdown简单易懂, 但是其功能有时非常有限, 例如自定义的表格, mark等, 需要使用三方语法甚至内嵌html来实现, 于是打算使用asciidoc来编写文章, 但是hexo默认不支持asciidoc, 本文记录hexo适配asciidoc的过程.

安装asciidoc

虽然已经有了 hexo-renderer-asciidoc 这个插件, 但是有几点会导致这个插件并不好用

  1. 此插件使用的代码高亮是 hexo-utils 提供的, 但是它的代码高亮并不会读配置文件, 于是会和主题配置格格不入

  2. asciidoc需要引入额外的css, 但是这个插件并未限定作用域. 如果导入asciidoc的css会导致全局生效, 影响其他md的文章

所需要依赖

json
{
    "asciidoctor": "^3.0.4",
    "cheerio": "1.0.0-rc.12",
}
Tip
请酌情对依赖进行升级

编写renderer

在hexo的根目录下创建一个 scripts 文件夹, 并创建一个 asciidoc-renderer.js 文件, 内容如下

javascript
const asciidoctor = require("asciidoctor")({
    runtime: {
      platform: "node",
      engine: "v8",
    }
  });
  const cheerio = require("cheerio");
  const p = require("path");
  const fs = require("fs");
  
  function wrapContainer(html) {
    return `<div class="asciidoc-container">${html}</div>`;
  }
  
  function highlight(code, lang) {
    return hexo.extend.highlight.exec(hexo.config.syntax_highlighter, {
      context: hexo,
      args: [code, {
        lang,
        lines_length: code.split("\n").length,
      }],
    });
  }
  
  
  class HexoSyntaxHighlighter {
    constructor(name, backend, opts) {
      this.name = name
    }
  
    format(node, lang, opts) {
      return highlight(node.getSource(), lang);
    }
  }
  
  
  function renderer(data, options) {
    // 注册高亮
    asciidoctor.SyntaxHighlighter.register("hexo-highlighter", HexoSyntaxHighlighter);
  
    // 修正include
    asciidoctor.Extensions.register(function () {
      this.includeProcessor(function () {
        const self = this;
        self.handles(function (target) {
          return true;
        });
        self.process(function (doc, reader, target, attrs) {
          const postDir = p.dirname(data.path);
          const includePath = p.join(postDir, target);
          const content = fs.readFileSync(includePath, "utf8");
          return reader.pushInclude(content, target, target, 1, attrs);
        })
      })
    });
  
    const html = asciidoctor.convert(data.text, {
      doctype: "article",
      safe: "server",
      attributes: {
        "source-highlighter": "hexo-highlighter",
        "table-grid": "rows",
      },
    });
  
    const $ = cheerio.load(wrapContainer(html), {
      decodeEntities: false,
    });
  
    // 修正图片路径
    if (hexo.config.post_asset_folder) {
      const blogName = p.basename(data.path, p.extname(data.path));
      $("img").each((_, elem) => {
        let src = $(elem).attr("src");
        if (!src.startsWith(blogName)) {
          return;
        }
        src = p.basename(src);
        $(elem).attr("src", src);
      });
    }
  
    return $.html();
  }
  
  hexo.extend.renderer.register("ad", "html", renderer, true);
  hexo.extend.renderer.register("adoc", "html", renderer, true);
  hexo.extend.renderer.register("asciidoc", "html", renderer, true);
  
  const css = hexo.extend.helper.get("css").bind(hexo);
  hexo.extend.injector.register("body_end", () => {
    return css("/css/asciidoc-renderer.css");
  });
  

编写css

由于asciidoc有一些样式, 这些样式需要引入 (当然不引入也可以)

  1. @asciidoctor/core 下的 asciidoctor.css 复制到 source/css

  2. source/css 下创建一个 asciidoc-renderer.less 文件, 内容如下

  3. 安装 hexo-renderer-less 插件

  4. 注入 asciidoc-renderer.css 到主题中

less
.asciidoc-container {
  @import (less) "asciidoctor.css";
}

asciidoc-renderer.js 下面添加

javascript
const css = hexo.extend.helper.get("css").bind(hexo)
hexo.extend.injector.register('body_end', () => {
  return css("/css/asciidoc-renderer.css")
})

asciidoctor.css里有些主题和博客里的主题对不上, 我删除了部分样式例如

  • 所有的 font-family

  • 所有的 font-color

  • 大部分 background-color

魔改Hexo

Warning
Hexo可能已经修复此问题, 请追踪Issue: #5523

由于Hexo的 这行代码, 导致在开启 post_asset_folder 的情况下, 只能渲染一种格式的文章, 于是需要魔改Hexo

在对应的代码处修改为

javascript
if (result.renderable && ctx.config.post_asset_folder) {
  result.renderable = (extname(ctx.config.new_post_name) === extname(path))
  if (!Array.isArray(ctx.config.post_extension) || ctx.config.post_extension.length === 0) {
    ctx.log.debug('post_asset_folder is set, but no post_extension is defined. Defaulting to new_post_name extension.')
    result.renderable = (extname(ctx.config.new_post_name) === extname(path))
  } else {
    result.renderable = ctx.config.post_extension.includes(extname(path).slice(1))
  }
}

_config.yml 中添加

yaml
post_extension: [ 'md', "adoc" ]

即可支持md和adoc的混合渲染


在Hexo中使用Asciidoc格式编写文章
https://simonkimi.githubio.io/2024/07/27/在Hexo中使用Asciidoc格式编写文章/
作者
simonkimi
发布于
2024年7月27日
许可协议