What’s hexo?

Hexo is a fast, simple and powerful blog framework. You write posts in Markdown (or other markup languages) and Hexo generates static files with a beautiful theme in seconds.

问题

在开始用 Hexo 写博客之后,就一直被它引用图片的方式所困扰。就是必须以 source 为根目录的方式引用,比方说:

1
2
3
4
5
6
7
.
├── source
│   ├── _posts
│ │ └── helloworld.md
│   └── img
│ └── cover.jpg
.

helloworld.md 里需要通过 ![alt txt](/img/cover.jpg) 来引用 cover.jpg,否则编译出的 html 就无法显示图片。而在编辑器内我更习惯 ../img/cover.jpg 的方式饮用图片。

Hexo 的引用方式会导致编辑器内预览时无法显示图片,而且这种引用方式也不符合直觉,不直观。Hexo 引用图片方式还有一个问题就是,以后切换到其它写作平台时可能需要逐一修改图片路径。

Hexo 大概想让用户直接通过 hexo server 预览,其他人不知道,反正我不喜欢预览还要切换到浏览器。

自己动手

现在的问题就是把写作时更加习惯的相对路径,在编译前替换成 Hexo 所支持的绝对路径。在 Github 上找了一圈没有找到支,所以就自己撸一个吧。

Hexo 提供的 Filter 让我们有机会在 Hexo 处理数据前对数据进行修改,这里的数据可能是 md 文件,也可能是其它文件。

不通类型的 Filter 在不同阶段执行,这里可以选择 before_post_render 在 md 文件编译成 html 之前进行处理。

实现

下面直接看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
const path = require('path');
const fs = require('fs');

let imageReg = /!\[(?<alt>[^\]]*)\]\((?<url>.*?)(?=\"|\))(?<title>\".*\")?\)/g;
let cwd = process.cwd();

let hexo_img_locator = function (data) {
let postPath = data.full_source;
if (!postPath.endsWith('.md')) {
printInfo(`skip ${postPath}`);
return data;
}
let mapping = new Map();
let cannotAccess = new Array();

let match = null;
while ((match = imageReg.exec(data.content)) != null) {
let imageUrl = match.groups.url;
// skip online image and local file with absolute path
if (imageUrl.startsWith('http://')
|| imageUrl.startsWith('https://')
|| imageUrl.startsWith('/')) {
continue;
}
let imageAbsPath = path.resolve(path.dirname(postPath), imageUrl);

if (!fs.existsSync(imageAbsPath)) {
cannotAccess.push(imageUrl);
continue;
} else if (cannotAccess.length > 0) {
// no need process any more
continue;
}

let imageAbsPathFromSource = path.sep + path.relative(`${cwd}${path.sep}source`, imageAbsPath);
let imageDirective = match[0];
mapping.set(imageDirective,
imageDirective.replace(imageUrl, imageAbsPathFromSource));
printInfo(`[hexo-img-locator] ${imageUrl} -> ${imageAbsPathFromSource}`);
}

if (cannotAccess.length != 0) {
printError(`[hexo-img-locator] cannot access ${cannotAccess[0]} and other ${cannotAccess.length - 1} from ${path.relative(cwd, data.full_source)}, file not found.`)
process.exit(1)
}
mapping.forEach((v, k, map) => {
data.content = data.content.replace(k, v);
})
return data;
}

hexo.extend.filter.register('before_post_render', hexo_img_locator);

这段代码做了两件事:

  • 使用正则匹配所有图片指令
  • 将图片指令中的路径替换成 Hexo 支持的路径

然后将 Filter 发布到 npm

待改进

正则匹配会无脑匹配,也就是说代码块中的 ![alt](/xxx) 也会被匹配,产生不符合预期的行为。

参考文章

https://hexo.io/api/filter