hexo-text-pipeline 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -175,7 +175,7 @@ text_pipeline:
|
|
|
175
175
|
|
|
176
176
|
## Development
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
The only runtime dependency is `hexo-util` (ships with Hexo itself; npm dedupes to the copy your site already has, zero extra install cost). Node >= 16.
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
181
|
npm test # node --test
|
package/README.zh-CN.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { escapeHTML } = require('hexo-util');
|
|
4
|
+
|
|
3
5
|
const DEFAULT_SCRIPT_SRC = 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js';
|
|
4
6
|
const DEFAULT_CLASS = 'mermaid';
|
|
5
7
|
|
|
@@ -11,10 +13,6 @@ const DEFAULT_CLASS = 'mermaid';
|
|
|
11
13
|
* 前端脚本默认按需注入(页面没有 .mermaid 元素时不加载 CDN):
|
|
12
14
|
* converters.mermaid 子配置:class / inject_script / script_src / theme。
|
|
13
15
|
*/
|
|
14
|
-
function escapeHtml(text) {
|
|
15
|
-
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
16
|
function transformMermaidBlocks(content, className) {
|
|
19
17
|
const lines = content.split('\n');
|
|
20
18
|
const out = [];
|
|
@@ -42,7 +40,7 @@ function transformMermaidBlocks(content, className) {
|
|
|
42
40
|
body.push(lines[j]);
|
|
43
41
|
}
|
|
44
42
|
if (closed) {
|
|
45
|
-
out.push('<pre class="' + className + '">' +
|
|
43
|
+
out.push('<pre class="' + className + '">' + escapeHTML(body.join('\n')) + '</pre>');
|
|
46
44
|
i = j;
|
|
47
45
|
continue;
|
|
48
46
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { slugize } = require('hexo-util');
|
|
3
4
|
const { replaceOutsideCode } = require('../../../../core/markdown-guard');
|
|
4
5
|
const { normalizeBase } = require('../../../../core/config');
|
|
5
6
|
const { getPostIndex, resolvePostByTarget, buildPostHref } = require('../../post-index');
|
|
@@ -11,6 +12,8 @@ const { getPostIndex, resolvePostByTarget, buildPostHref } = require('../../post
|
|
|
11
12
|
* - ![[...]] 是嵌入,归 embed node 管,这里用 lookbehind 跳过
|
|
12
13
|
* - #^block-id 块引用锚点在渲染后的 HTML 里不存在,丢弃锚点只留文章链接
|
|
13
14
|
* - [[#标题]](无 target 的同页链接)改写为页内锚点
|
|
15
|
+
* - 锚点用 hexo-util 的 slugize 生成——与 hexo-renderer-marked 给标题生成 id
|
|
16
|
+
* 的是同一个函数(含 marked.modifyAnchors 配置),保证页内跳转不断
|
|
14
17
|
*/
|
|
15
18
|
function parseWikiLink(raw) {
|
|
16
19
|
const firstPipe = raw.indexOf('|');
|
|
@@ -24,7 +27,14 @@ function parseWikiLink(raw) {
|
|
|
24
27
|
return { target, anchor, alias };
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
// 与 hexo-renderer-marked 的标题 id 同源:slugize + modifyAnchors(1 小写 / 2 大写)。
|
|
31
|
+
// slug 再过 encodeURIComponent 保证 markdown 链接语法安全(中文等百分号编码后,
|
|
32
|
+
// 浏览器解码 fragment 仍命中原 id)。
|
|
33
|
+
function anchorToHash(anchor, anchorTransform) {
|
|
34
|
+
return '#' + encodeURIComponent(slugize(anchor, { transform: anchorTransform }));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createWikiLinkReplacer(index, domainPrefix, anchorTransform) {
|
|
28
38
|
return function replaceWikiLinks(segment) {
|
|
29
39
|
return segment.replace(/(?<!!)\[\[([^\]]+)\]\]/g, (full, inner) => {
|
|
30
40
|
const parsed = parseWikiLink(inner);
|
|
@@ -32,7 +42,7 @@ function createWikiLinkReplacer(index, domainPrefix) {
|
|
|
32
42
|
// 同页链接 [[#标题]]:没有 target,只有锚点
|
|
33
43
|
if (!parsed.target) {
|
|
34
44
|
if (!parsed.anchor || parsed.anchor.startsWith('^')) return full;
|
|
35
|
-
return '[' + (parsed.alias || parsed.anchor) + '](
|
|
45
|
+
return '[' + (parsed.alias || parsed.anchor) + '](' + anchorToHash(parsed.anchor, anchorTransform) + ')';
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
const post = resolvePostByTarget(index, parsed.target);
|
|
@@ -42,7 +52,8 @@ function createWikiLinkReplacer(index, domainPrefix) {
|
|
|
42
52
|
if (!href) return full;
|
|
43
53
|
|
|
44
54
|
// 块引用锚点(#^id)在 HTML 里没有对应元素,降级为文章链接
|
|
45
|
-
const anchor =
|
|
55
|
+
const anchor =
|
|
56
|
+
parsed.anchor && !parsed.anchor.startsWith('^') ? anchorToHash(parsed.anchor, anchorTransform) : '';
|
|
46
57
|
const text = parsed.alias || parsed.target;
|
|
47
58
|
|
|
48
59
|
return '[' + text + '](' + href + anchor + ')';
|
|
@@ -58,7 +69,12 @@ module.exports = {
|
|
|
58
69
|
},
|
|
59
70
|
convert(content, ctx) {
|
|
60
71
|
const index = getPostIndex(ctx.hexo);
|
|
61
|
-
const
|
|
72
|
+
const marked = (ctx.hexo.config && ctx.hexo.config.marked) || {};
|
|
73
|
+
const replacer = createWikiLinkReplacer(
|
|
74
|
+
index,
|
|
75
|
+
normalizeBase(ctx.presetConfig.domain_prefix),
|
|
76
|
+
marked.modifyAnchors
|
|
77
|
+
);
|
|
62
78
|
return replaceOutsideCode(content, replacer);
|
|
63
79
|
},
|
|
64
80
|
_internal: { parseWikiLink, createWikiLinkReplacer }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hexo-text-pipeline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A general-purpose hooks bus for Hexo's render pipeline: hang your own scripts/commands on any text stage (text in, text out, edit-and-use), with a checker system as the safety net. Ships an Obsidian Flavored Markdown preset.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -37,5 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=16"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"hexo-util": "^2.7.0 || ^3.0.0 || ^4.0.0"
|
|
40
43
|
}
|
|
41
44
|
}
|