-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ignore exceptions caused by URL parsing errors (#34)
* fix markdown parsing * make prettier happy
- Loading branch information
Showing
6 changed files
with
829 additions
and
288 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* @luogu-discussion-archive/remark-lda-lfm | ||
* Copyright (c) 2025 Luogu Discussion Archive Project | ||
* | ||
* Licensed under GNU Affero General Public License version 3 or later. | ||
* See the index.js file for details. | ||
* | ||
* @license AGPL-3.0-or-later | ||
*/ | ||
|
||
import "mdast"; | ||
|
||
declare module "mdast" { | ||
interface UserMention extends Parent { | ||
type: "userMention"; | ||
uid: number; | ||
children: PhrasingContent[]; | ||
} | ||
|
||
interface BilibiliVideo extends Node { | ||
type: "bilibiliVideo"; | ||
videoId: string; | ||
} | ||
|
||
interface PhrasingContentMap { | ||
userMention: UserMention; | ||
bilibiliVideo: BilibiliVideo; | ||
} | ||
|
||
interface RootContentMap { | ||
userMention: UserMention; | ||
bilibiliVideo: BilibiliVideo; | ||
} | ||
} | ||
|
||
export default function remarkLuoguFlavor(): ( | ||
tree: import("mdast").Root, | ||
) => void; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
/** | ||
* @luogu-discussion-archive/remark-lda-lfm | ||
* Copyright (c) 2025 Luogu Discussion Archive Project | ||
* See AUTHORS.txt in the project root for the full list of contributors. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Please notice that 「洛谷」 (also known as "Luogu") is a registered trademark of | ||
* Shanghai Luogu Network Technology Co., Ltd (上海洛谷网络科技有限公司). | ||
* | ||
* @license AGPL-3.0-or-later | ||
*/ | ||
|
||
/// <reference types="remark-parse" /> | ||
/// <reference types="remark-stringify" /> | ||
/// <reference types="mdast" /> | ||
/// <reference path="./index.d.ts" /> | ||
|
||
/** | ||
* @typedef {import('mdast').Root} Root | ||
* @typedef {import('vfile').VFile} VFile | ||
* @typedef {import('unified').Processor<Root>} Processor | ||
*/ | ||
|
||
/** | ||
* @typedef Options | ||
* Configuration. | ||
* @property {RegExp[] | null} [linkRootToLuoguWhiteList] | ||
* URL patterns in list will not point to https://www.luogu.com.cn/, | ||
* except /user/${uid} (userMention). (optional). | ||
* @property {boolean | null} [userLinkPointToLuogu] | ||
* /user/${uid} (userMention) point to luogu or not. Default true. (optional) | ||
*/ | ||
|
||
import { visit } from "unist-util-visit"; | ||
|
||
import { gfmFootnote } from "micromark-extension-gfm-footnote"; | ||
import { gfmStrikethrough } from "micromark-extension-gfm-strikethrough"; | ||
import { gfmTable } from "micromark-extension-gfm-table"; | ||
import { gfmAutolinkLiteral } from "micromark-extension-gfm-autolink-literal"; | ||
|
||
import { | ||
gfmAutolinkLiteralFromMarkdown, | ||
gfmAutolinkLiteralToMarkdown, | ||
} from "mdast-util-gfm-autolink-literal"; | ||
import { gfmTableFromMarkdown, gfmTableToMarkdown } from "mdast-util-gfm-table"; | ||
import { | ||
gfmStrikethroughFromMarkdown, | ||
gfmStrikethroughToMarkdown, | ||
} from "mdast-util-gfm-strikethrough"; | ||
import { | ||
gfmFootnoteFromMarkdown, | ||
gfmFootnoteToMarkdown, | ||
} from "mdast-util-gfm-footnote"; | ||
|
||
const mentionReg = /^\/user\/(\d+)$/; | ||
const legacyMentionReg = /^\/space\/show\?uid=(\d+)$/; | ||
|
||
/** @type {Options} */ | ||
const emptyOptions = {}; | ||
|
||
/** | ||
* remark-luogu-flavor plugin. | ||
* | ||
* @param {Options | null | undefined} [options] | ||
* Configuration (optional). | ||
* @this {Processor} | ||
*/ | ||
export default function remarkLuoguFlavor(options) { | ||
const self = this; | ||
const settings = options || emptyOptions; | ||
const data = self.data(); | ||
|
||
const linkWhiteList = settings.linkRootToLuoguWhiteList ?? []; | ||
const userLinkPointToLuogu = settings.userLinkPointToLuogu ?? true; | ||
|
||
const micromarkExtensions = | ||
data.micromarkExtensions || (data.micromarkExtensions = []); | ||
const fromMarkdownExtensions = | ||
data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []); | ||
const toMarkdownExtensions = | ||
data.toMarkdownExtensions || (data.toMarkdownExtensions = []); | ||
|
||
micromarkExtensions.push( | ||
gfmFootnote(), | ||
gfmStrikethrough({ singleTilde: false, ...settings }), | ||
gfmTable(), | ||
gfmAutolinkLiteral(), | ||
); | ||
|
||
fromMarkdownExtensions.push( | ||
gfmFootnoteFromMarkdown(), | ||
gfmStrikethroughFromMarkdown(), | ||
gfmTableFromMarkdown(), | ||
gfmAutolinkLiteralFromMarkdown(), | ||
); | ||
|
||
toMarkdownExtensions.push( | ||
gfmFootnoteToMarkdown(), | ||
gfmTableToMarkdown(), | ||
gfmStrikethroughToMarkdown(), | ||
gfmAutolinkLiteralToMarkdown(), | ||
); | ||
|
||
/** | ||
* Transform. | ||
* | ||
* @param {Root} tree | ||
* Tree. | ||
* @returns {undefined} | ||
* Nothing. | ||
*/ | ||
return (tree) => { | ||
visit(tree, "paragraph", (node) => { | ||
const childNode = node.children; | ||
childNode.forEach((child, index) => { | ||
const lastNode = childNode[index - 1]; | ||
if ( | ||
child.type === "link" && | ||
index >= 1 && | ||
lastNode.type === "text" && | ||
lastNode.value.endsWith("@") | ||
) { | ||
const match = | ||
mentionReg.exec(child.url) ?? legacyMentionReg.exec(child.url); | ||
if (!match) return; | ||
/** @type {import("mdast").UserMention} */ | ||
const newNode = { | ||
type: "userMention", | ||
children: child.children, | ||
uid: parseInt(match[1]), | ||
data: { | ||
hName: "a", | ||
hProperties: { | ||
href: userLinkPointToLuogu | ||
? `https://www.luogu.com.cn/user/${match[1]}` | ||
: `/user/${match[1]}`, | ||
"data-uid": match[1], | ||
class: "lfm-user-mention", | ||
}, | ||
}, | ||
}; | ||
childNode[index] = newNode; | ||
} | ||
if (child.type === "image" && child.url.startsWith("bilibili:")) { | ||
let videoId = child.url.replace("bilibili:", ""); | ||
if (videoId.match(/^[0-9]/)) videoId = "av" + videoId; | ||
/** @type {import("mdast").BilibiliVideo} */ | ||
const newNode = { | ||
type: "bilibiliVideo", | ||
videoId, | ||
data: { | ||
hName: "iframe", | ||
hProperties: { | ||
scrolling: "no", | ||
allowfullscreen: "true", | ||
class: "lfm-bilibili-video", | ||
src: | ||
"https://www.bilibili.com/blackboard/webplayer/embed-old.html?bvid=" + | ||
videoId.replace(/[\?&]/g, "&"), | ||
}, | ||
}, | ||
}; | ||
childNode[index] = newNode; | ||
} | ||
}); | ||
}); | ||
visit(tree, "link", (node) => { | ||
if (!linkWhiteList.some((reg) => reg.test(node.url))) { | ||
try { | ||
node.url = new URL(node.url, "https://www.luogu.com.cn/").href; | ||
} catch (_) { | ||
// ignore | ||
} | ||
} | ||
}); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "@luogu-discussion-archive/remark-lda-lfm", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "AGPL-3.0-or-later", | ||
"type": "module", | ||
"devDependencies": { | ||
"@types/mdast": "^4.0.4", | ||
"@types/node": "^20.17.13", | ||
"mdast-util-from-markdown": "^2.0.2", | ||
"rehype-sanitize": "^6.0.0", | ||
"rehype-stringify": "^10.0.1", | ||
"remark": "^15.0.1", | ||
"remark-parse": "^11.0.0", | ||
"remark-rehype": "^11.1.1", | ||
"remark-stringify": "^11.0.0", | ||
"typescript": "^5.7.3", | ||
"unified": "^11.0.5", | ||
"vfile": "^6.0.3" | ||
}, | ||
"dependencies": { | ||
"mdast-util-gfm-autolink-literal": "^2.0.1", | ||
"mdast-util-gfm-footnote": "^2.0.0", | ||
"mdast-util-gfm-strikethrough": "^2.0.0", | ||
"mdast-util-gfm-table": "^2.0.0", | ||
"micromark-extension-gfm-autolink-literal": "^2.1.0", | ||
"micromark-extension-gfm-footnote": "^2.1.0", | ||
"micromark-extension-gfm-strikethrough": "^2.1.0", | ||
"micromark-extension-gfm-table": "^2.1.0", | ||
"unist-util-visit": "^5.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.