hexo-obsidian-backlink
Version:
This plugin is for transfer Obsidian-type backlink to standard hexo in-site post link.
293 lines (271 loc) • 9.47 kB
JavaScript
;
const log = require("hexo-log")({
debug: false,
silent: false,
});
const hexoFs = require("hexo-fs");
const fs = require("fs");
const path = require("path");
const priority = 10000;
// 获取所有md文件,并关联到 Hexo 变量中
hexo.extend.filter.register(
"before_post_render",
function (data) {
if (hexo.files != undefined && hexo.files != null) {
hexo.files.set(data.source.replace("_posts/", ""), data);
} else {
hexo.files = new Map();
hexo.files.set(data.source.replace("_posts/", ""), data);
}
},
priority + 10
);
// 获取所有 _posts 文件夹下所有文件,并关联到 Hexo 变量中
hexo.extend.filter.register(
"before_post_render",
function (data) {
if (hexo.allFile == undefined || hexo.allFile == null) {
let allFile = hexoFs
.listDirSync(path.join(hexo.source_dir, "_posts"), {
ignorePattern: /node_modules/,
})
.map((each) => {
let array = (each + "").split(path.sep);
// For Windows
if (path.sep === "\\") {
each = each.replace(new RegExp("\\" + path.sep, "g"), "/");
}
return {
info: hexo.files.get(each),
fileName: array.length === 0 ? "" : array[array.length - 1],
filePath: each,
articleName: each.replace(/\.md$/, ""),
fileExt: each.substr(each.lastIndexOf(".")),
};
});
hexo.allFile = allFile;
}
},
priority + 20
);
// 替换所有 双链的附件为 markdown标准的附件格式
hexo.extend.filter.register(
"before_post_render",
function (data) {
function unique(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
//
const regexHasExtension = /^([^\\]*)\.(\w+)$/;
//
const regexWiki = /\[\[([^\]]+)\]\]/;
//
const regexWikiGlobal = /\[\[([^\]]*)\]\]/g;
// 正则忽略内容
const ignoreReg = /[\r\n]+/g;
let content = data.content;
let wikiMatches = content.match(regexWikiGlobal);
if (wikiMatches && !wikiMatches[0].match(ignoreReg)) {
for (const item of wikiMatches) {
let text = item.match(regexWiki)[1];
const matches = text.match(regexHasExtension);
let newText = text;
if (matches) {
const filename = matches[1];
const extension = matches[2];
} else {
newText = newText + ".md";
}
const encodedText = encodeURI(newText);
let newItem = `[${text}](${encodedText})`;
content = content.replaceAll(item, newItem);
}
}
data.content = content;
},
priority + 30
);
// 将所有 markdown 链接 在转换为 指向具体文件的链接
hexo.extend.filter.register(
"before_post_render",
function (data) {
function unique(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
const allFile = hexo.allFile;
const regexHasExtension = /^([^\\]*)\.(\w+)$/;
const regexParenthesis = /\((.*?)\)/;
const regexMdGlobal = /\[([^\]]*)\]\(([^\(]*)\)/g;
// md 链接 分段
const regexMdLink = /\[([\s\S]*?)\]\(([\s\S]*?)\)/;
let content = data.content;
let mdMatches = content.match(regexMdGlobal);
if (mdMatches) {
mdMatches = unique(mdMatches);
for (const item of mdMatches) {
// 过滤正常链接
const mdLink = regexMdLink.exec(item);
if (!mdLink) {
log.error("%s format err", item);
}
const title = mdLink[1];
if (/^http/.test(mdLink[2])) {
continue;
}
if(!data.source.toLowerCase().endsWith(".md")){
continue;
}
// const title = item.match(regexMdTitle);
let text = item.match(regexParenthesis)[1];
if (text.startsWith("../")) {
text = path
.resolve(data.slug, "../" + text)
.replace(this.base_dir, "");
}
text = decodeURI(text);
let realFilePath = allFile.find((file) => file.filePath === text);
if (!realFilePath) {
realFilePath = allFile.find((file) => file.fileName === text);
}
if(!realFilePath){
realFilePath = allFile.find((file) => path.resolve(data.full_source, "../"+text).indexOf(file.filePath) > 0)
}
if(!realFilePath && data.slug){
realFilePath = allFile.find((file)=>file.filePath === path.resolve(data.slug, "../"+text).replace(this.base_dir, ""))
}
if (!realFilePath) {
log.error("file: %s asset: %s path: %s not found", item, text, data.full_source)
}
if (realFilePath) {
const matches = text.match(regexHasExtension);
if (matches) {
const filename = matches[1];
const extension = matches[2];
if (extension == "md") {
const postLink = `<a href="/${realFilePath.info.path}">${title}</a>`;
content = content
.replaceAll("!" + item, postLink)
.replaceAll(item, postLink);
} else {
// 附件
// const assetLink = `{% asset_link ${filename} %}`
// content = content.replaceAll(item, assetLink);
}
} else {
const postLink = `<a href="/${realFilePath.info.path}">${title}</a>`;
content = content.replaceAll(item, postLink);
}
}
}
}
data.content = content;
},
priority + 40
);
// 接下来所有的 链接文件都为 附件
// 将md的内容复制到 images 文件夹下,并指向这个链接
let assetMaps = new Map();
hexo.extend.filter.register(
"before_post_render",
function (data) {
function unique(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
const assetFile = function (path, title, extension) {
if ([".mp4", ".webm", ".ogg"].includes(extension)) {
return `<img src="www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" alt="${title}">`;
} else {
return `<img src="${path}" alt="${title}">`;
}
};
const allFile = hexo.allFile;
//
const regexParenthesis = /\((.*?)\)/;
// 扩展名
const regexHasExtension = /^([^\\]*)\.(\w+)$/;
// md 链接数据
const regexMdGlobal = /\[([^\]]*)\]\(([^\(]*)\)/g;
// md 链接 分段
const regexMdLink = /\[([\s\S]*?)\]\(([\s\S]*?)\)/;
const dir_source = this.source_dir;
const assetSource = dir_source + "_posts/";
const dir_public = this.public_dir;
const dir_images = path.join(dir_public, data.path, "images");
let content = data.content;
let mdMatches = content.match(regexMdGlobal);
if (mdMatches) {
mdMatches = unique(mdMatches);
for (const item of mdMatches) {
// 过滤正常链接
const mdLink = regexMdLink.exec(item);
if (!mdLink) {
log.error("%s format err", item);
}
const title = mdLink[1];
if (/^http/.test(mdLink[2])) {
continue;
}
if(!data.source.toLowerCase().endsWith(".md")){
continue;
}
let text = item.match(regexParenthesis)[1];
text = decodeURI(text);
let realFilePath = allFile.find((file) => file.filePath === text);
if (!realFilePath) {
realFilePath = allFile.find((file) => file.fileName === text);
}
if(!realFilePath){
realFilePath = allFile.find((file) => path.resolve(data.full_source, "../"+text).indexOf(file.filePath) > 0)
}
if(!realFilePath && data.slug){
realFilePath = allFile.find((file)=>file.filePath === path.resolve(data.slug, "../"+text).replace(this.base_dir, ""))
}
if (!realFilePath) {
log.error("file: %s asset: %s path: %s not found", item, text, data.full_source)
}
if (!assetMaps.has(item)) {
const matches = text.match(regexHasExtension);
if (!matches) {
continue
// log.error("%s %s no extension", text);
}
const extension = matches[2];
if (realFilePath) {
hexoFs.copyFile(
assetSource + realFilePath.filePath,
path.join(dir_images, path.basename(realFilePath.fileName))
);
const href = assetFile(
`/${
data.path +
encodeURI("images/" + path.basename(realFilePath.fileName))
}`,
title,
extension
);
content = content
.replaceAll("!" + item, href)
.replaceAll(item, href);
assetMaps.set(item, href);
// 附件
}
} else {
content = content
.replaceAll("!" + item, assetMaps.get(item))
.replaceAll(item, assetMaps.get(item));
}
}
}
data.content = content;
},
priority + 50
);