diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index 9f196d87036b..a44636f701a9 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-project
+ * SPDX-FileCopyrightText: syuilo and misskey-project, Type4ny-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -7,19 +7,16 @@ import { URL } from 'node:url';
import { Inject, Injectable } from '@nestjs/common';
import * as parse5 from 'parse5';
import { JSDOM } from 'jsdom';
-import serialize from 'w3c-xmlserializer';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { intersperse } from '@/misc/prelude/array.js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import type { IMentionedRemoteUsers } from '@/models/Note.js';
import { bindThis } from '@/decorators.js';
-import type { DefaultTreeAdapterMap } from 'parse5';
+import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js';
import type * as mfm from 'mfm-js';
-const treeAdapter = parse5.defaultTreeAdapter;
-type Node = DefaultTreeAdapterMap['node'];
-type ChildNode = DefaultTreeAdapterMap['childNode'];
+const treeAdapter = TreeAdapter.defaultTreeAdapter;
const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
@@ -49,7 +46,7 @@ export class MfmService {
return text.trim();
- function getText(node: Node): string {
+ function getText(node: TreeAdapter.Node): string {
if (treeAdapter.isTextNode(node)) return node.value;
if (!treeAdapter.isElementNode(node)) return '';
if (node.nodeName === 'br') return '\n';
@@ -61,7 +58,7 @@ export class MfmService {
return '';
}
- function appendChildren(childNodes: ChildNode[]): void {
+ function appendChildren(childNodes: TreeAdapter.ChildNode[]): void {
if (childNodes) {
for (const n of childNodes) {
analyze(n);
@@ -69,16 +66,14 @@ export class MfmService {
}
}
- function analyze(node: Node) {
+ function analyze(node: TreeAdapter.Node) {
if (treeAdapter.isTextNode(node)) {
text += node.value;
return;
}
// Skip comment or document type node
- if (!treeAdapter.isElementNode(node)) {
- return;
- }
+ if (!treeAdapter.isElementNode(node)) return;
switch (node.nodeName) {
case 'br': {
@@ -86,7 +81,8 @@ export class MfmService {
break;
}
- case 'a': {
+ case 'a':
+ {
const txt = getText(node);
const rel = node.attrs.find(x => x.name === 'rel');
const href = node.attrs.find(x => x.name === 'href');
@@ -134,7 +130,8 @@ export class MfmService {
break;
}
- case 'h1': {
+ case 'h1':
+ {
text += '【';
appendChildren(node.childNodes);
text += '】\n';
@@ -142,14 +139,16 @@ export class MfmService {
}
case 'b':
- case 'strong': {
+ case 'strong':
+ {
text += '**';
appendChildren(node.childNodes);
text += '**';
break;
}
- case 'small': {
+ case 'small':
+ {
text += '';
appendChildren(node.childNodes);
text += '';
@@ -157,7 +156,8 @@ export class MfmService {
}
case 's':
- case 'del': {
+ case 'del':
+ {
text += '~~';
appendChildren(node.childNodes);
text += '~~';
@@ -165,7 +165,8 @@ export class MfmService {
}
case 'i':
- case 'em': {
+ case 'em':
+ {
text += '';
appendChildren(node.childNodes);
text += '';
@@ -206,7 +207,8 @@ export class MfmService {
case 'h3':
case 'h4':
case 'h5':
- case 'h6': {
+ case 'h6':
+ {
text += '\n\n';
appendChildren(node.childNodes);
break;
@@ -219,7 +221,8 @@ export class MfmService {
case 'article':
case 'li':
case 'dt':
- case 'dd': {
+ case 'dd':
+ {
text += '\n';
appendChildren(node.childNodes);
break;
@@ -240,9 +243,9 @@ export class MfmService {
return null;
}
- const { window } = new JSDOM() as unknown as { window: Window };
+ const fragment = JSDOM.fragment('');
- const doc = window.document;
+ const doc = fragment.ownerDocument;
const body = doc.createElement('p');
@@ -458,6 +461,6 @@ export class MfmService {
appendChildren(nodes, body);
- return serialize(body);
+ return body.outerHTML;
}
}