diff --git a/package.json b/package.json index 93c2188f..2f08dd19 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "juice": "^8.0.0", "lint-staged": "~12.5.0", "lodash": "^4.17.20", - "md5": "^2.2.1", "mitt": "^3.0.0", "npm-run-all": "^4.1.5", "openai": "^3.3.0", @@ -150,6 +149,7 @@ "dependencies": { "@types/codemirror": "^0.0.108", "@types/dompurify": "^2.2.3", + "crypto-js": "^4.2.0", "jsdom": "~19.0.0" }, "resolutions": { diff --git a/src/Engine.js b/src/Engine.js index 794e91af..b0296df4 100644 --- a/src/Engine.js +++ b/src/Engine.js @@ -16,7 +16,7 @@ import HookCenter from './core/HookCenter'; import hooksConfig from './core/HooksConfig'; import NestedError, { $expectTarget, $expectInherit, $expectInstance } from './utils/error'; -import md5 from 'md5'; +import CryptoJS from 'crypto-js'; import SyntaxBase from './core/SyntaxBase'; import ParagraphBase from './core/ParagraphBase'; import { PUNCTUATION, imgBase64Reg, imgDrawioXmlReg } from './utils/regexp'; @@ -46,8 +46,8 @@ export default class Engine { this.$configInit(markdownParams); this.hookCenter = new HookCenter(hooksConfig, markdownParams, cherry); this.hooks = this.hookCenter.getHookList(); - this.md5Cache = {}; - this.md5StrMap = {}; + this.hashCache = {}; + this.hashStrMap = {}; this.cachedBigData = {}; this.markdownParams = markdownParams; this.currentStrMd5 = []; @@ -205,23 +205,35 @@ export default class Engine { return $md; } + /** + * @deprecated 已废弃,推荐使用 .hash() + */ md5(str) { - if (!this.md5StrMap[str]) { - this.md5StrMap[str] = md5(str); + return this.hash(str); + } + + /** + * 计算哈希值 + * @param {String} str 被计算的字符串 + * @returns {String} 哈希值 + */ + hash(str) { + if (!this.hashStrMap[str]) { + this.hashStrMap[str] = CryptoJS.SHA256(str).toString(); } - return this.md5StrMap[str]; + return this.hashStrMap[str]; } $checkCache(str, func) { - const sign = this.md5(str); - if (typeof this.md5Cache[sign] === 'undefined') { - this.md5Cache[sign] = func(str); + const sign = this.hash(str); + if (typeof this.hashCache[sign] === 'undefined') { + this.hashCache[sign] = func(str); if (BUILD_ENV !== 'production') { // 生产环境屏蔽 Logger.log('markdown引擎渲染了:', str); } } - return { sign, html: this.md5Cache[sign] }; + return { sign, html: this.hashCache[sign] }; } $dealParagraph(md) { @@ -231,12 +243,12 @@ export default class Engine { // 缓存大文本数据,用以提升渲染性能 $cacheBigData(md) { let $md = md.replace(imgBase64Reg, (whole, m1, m2) => { - const cacheKey = `bigDataBegin${this.md5(m2)}bigDataEnd`; + const cacheKey = `bigDataBegin${this.hash(m2)}bigDataEnd`; this.cachedBigData[cacheKey] = m2; return `${m1}${cacheKey})`; }); $md = $md.replace(imgDrawioXmlReg, (whole, m1, m2) => { - const cacheKey = `bigDataBegin${this.md5(m2)}bigDataEnd`; + const cacheKey = `bigDataBegin${this.hash(m2)}bigDataEnd`; this.cachedBigData[cacheKey] = m2; return `${m1}${cacheKey}}`; }); diff --git a/src/UrlCache.js b/src/UrlCache.js index 639eac8b..fd977cb8 100644 --- a/src/UrlCache.js +++ b/src/UrlCache.js @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import md5 from 'md5'; +import CryptoJS from 'crypto-js'; let urlCache = {}; const cherryInnerLinkRegex = /^cherry-inner:\/\/([0-9a-f]+)$/i; @@ -44,7 +44,7 @@ export default class UrlCache { * @returns */ static set(url) { - const urlSign = md5(url); + const urlSign = CryptoJS.SHA256(url).toString(); urlCache[urlSign] = url; return `cherry-inner://${urlSign}`; } diff --git a/src/core/ParagraphBase.js b/src/core/ParagraphBase.js index bcd65524..d4d13ddf 100644 --- a/src/core/ParagraphBase.js +++ b/src/core/ParagraphBase.js @@ -248,7 +248,7 @@ export default class ParagraphBase extends SyntaxBase { if (!this.needCache) { return; } - const $sign = sign || this.$engine.md5(str); + const $sign = sign || this.$engine.hash(str); const key = `${this.cacheKey}I${$sign}_L${lineCount}$`; this.cache[$sign] = { content: str, @@ -314,7 +314,7 @@ export default class ParagraphBase extends SyntaxBase { * @param {string} wholeMatch whole match */ checkCache(wholeMatch, sentenceMakeFunc, lineCount = 0) { - this.sign = this.$engine.md5(wholeMatch); + this.sign = this.$engine.hash(wholeMatch); // miss cache if (!this.cache[this.sign]) { return this.toHtml(wholeMatch, sentenceMakeFunc); diff --git a/src/core/hooks/Blockquote.js b/src/core/hooks/Blockquote.js index d8df426b..25cdc787 100644 --- a/src/core/hooks/Blockquote.js +++ b/src/core/hooks/Blockquote.js @@ -27,7 +27,7 @@ export default class Blockquote extends ParagraphBase { handleMatch(str, sentenceMakeFunc) { return str.replace(this.RULE.reg, (match, lines, content) => { const lineCount = this.getLineCount(match, lines); // 段落所占行数 - const sign = this.$engine.md5(match); + const sign = this.$engine.hash(match); const testHasCache = this.testHasCache(sign); if (testHasCache !== false) { return this.getCacheWithSpace(testHasCache, match); diff --git a/src/core/hooks/CodeBlock.js b/src/core/hooks/CodeBlock.js index c91861d9..96f506a7 100644 --- a/src/core/hooks/CodeBlock.js +++ b/src/core/hooks/CodeBlock.js @@ -139,7 +139,7 @@ export default class CodeBlock extends ParagraphBase { computeLines(match, leadingContent, code) { const leadingSpaces = leadingContent; const lines = this.getLineCount(match, leadingSpaces); - const sign = this.$engine.md5(match.replace(/^\n+/, '') + lines); + const sign = this.$engine.hash(match.replace(/^\n+/, '') + lines); return { sign, lines, @@ -247,7 +247,7 @@ export default class CodeBlock extends ParagraphBase { } return this.$recoverCodeInIndent(str).replace(this.$getIndentedCodeReg(), (match, code) => { const lineCount = (match.match(/\n/g) || []).length; - const sign = this.$engine.md5(match); + const sign = this.$engine.hash(match); const html = `
${escapeHTMLSpecialChar(
         code.replace(/\n( {4}|\t)/g, '\n'),
       )}
`; @@ -412,7 +412,7 @@ export default class CodeBlock extends ParagraphBase { $code = this.$replaceSpecialChar($code); $code = $code.replace(/\\/g, '\\\\'); const html = `${escapeHTMLSpecialChar($code)}`; - const sign = this.$engine.md5(html); + const sign = this.$engine.hash(html); CodeBlock.inlineCodeCache[sign] = html; return `~~CODE${sign}$`; }); diff --git a/src/core/hooks/Detail.js b/src/core/hooks/Detail.js index d21c7b6f..84763865 100644 --- a/src/core/hooks/Detail.js +++ b/src/core/hooks/Detail.js @@ -38,7 +38,7 @@ export default class Detail extends ParagraphBase { makeHtml(str, sentenceMakeFunc) { return str.replace(this.RULE.reg, (match, preLines, isOpen, title, content) => { const lineCount = this.getLineCount(match, preLines); - const sign = this.$engine.md5(match); + const sign = this.$engine.hash(match); const testHasCache = this.testHasCache(sign); if (testHasCache !== false) { return prependLineFeedForParagraph(match, testHasCache); diff --git a/src/core/hooks/Footnote.js b/src/core/hooks/Footnote.js index 43bbd468..ced48886 100644 --- a/src/core/hooks/Footnote.js +++ b/src/core/hooks/Footnote.js @@ -67,7 +67,7 @@ export default class Footnote extends ParagraphBase { return ''; } let html = footnote.map((note) => `
\n${note.fnref}${note.note}\n
`).join(''); - const sign = this.$engine.md5(html); + const sign = this.$engine.hash(html); html = `
脚注
${html}
`; return html; } diff --git a/src/core/hooks/Header.js b/src/core/hooks/Header.js index 408d8fd1..16772d8b 100644 --- a/src/core/hooks/Header.js +++ b/src/core/hooks/Header.js @@ -112,7 +112,7 @@ export default class Header extends ParagraphBase { anchorID = this.generateIDNoDup(headerTextRaw.replace(replaceFootNote, '')); } const safeAnchorID = `safe_${anchorID}`; // transform header id to avoid being sanitized - const sign = this.$engine.md5(`${level}-${processedText.sign}-${anchorID}-${dataLines}`); + const sign = this.$engine.hash(`${level}-${processedText.sign}-${anchorID}-${dataLines}`); const result = [ ``, this.$getAnchor(anchorID), diff --git a/src/core/hooks/InlineMath.js b/src/core/hooks/InlineMath.js index a52fec02..039018d1 100644 --- a/src/core/hooks/InlineMath.js +++ b/src/core/hooks/InlineMath.js @@ -44,7 +44,7 @@ export default class InlineMath extends ParagraphBase { LoadMathModule.bind(this)('engine'); const linesArr = m1.match(/\n/g); const lines = linesArr ? linesArr.length + 2 : 2; - const sign = this.$engine.md5(wholeMatch); + const sign = this.$engine.hash(wholeMatch); // 既无MathJax又无katex时,原样输出 let result = ''; if (this.engine === 'katex' && this.katex?.renderToString) { diff --git a/src/core/hooks/MathBlock.js b/src/core/hooks/MathBlock.js index b322374f..ad16284d 100644 --- a/src/core/hooks/MathBlock.js +++ b/src/core/hooks/MathBlock.js @@ -43,7 +43,7 @@ export default class MathBlock extends ParagraphBase { const wholeMatchWithoutSpace = wholeMatch.replace(/^[ \f\r\t\v]*/, '').replace(/\s*$/, ''); // 去掉匹配到的第一个换行符 const lineSpaceWithoutPreSpace = lineSpace.replace(/^[ \f\r\t\v]*\n/, ''); - const sign = this.$engine.md5(wholeMatch); + const sign = this.$engine.hash(wholeMatch); let lines = this.getLineCount(wholeMatchWithoutSpace, lineSpaceWithoutPreSpace); // 判断公式是不是新行输入,如果不是新行,则行号减1 if (!/\n/.test(lineSpace)) { diff --git a/src/core/hooks/Panel.js b/src/core/hooks/Panel.js index 559bd023..7a5040b7 100644 --- a/src/core/hooks/Panel.js +++ b/src/core/hooks/Panel.js @@ -41,7 +41,7 @@ export default class Panel extends ParagraphBase { makeHtml(str, sentenceMakeFunc) { return str.replace(this.RULE.reg, (match, preLines, name, content) => { const lineCount = this.getLineCount(match, preLines); - const sign = this.$engine.md5(match); + const sign = this.$engine.hash(match); const testHasCache = this.testHasCache(sign); if (testHasCache !== false) { return prependLineFeedForParagraph(match, testHasCache); diff --git a/src/core/hooks/Table.js b/src/core/hooks/Table.js index ba42723a..943a21d7 100644 --- a/src/core/hooks/Table.js +++ b/src/core/hooks/Table.js @@ -120,7 +120,7 @@ export default class Table extends ParagraphBase { rowLength: rows.length - 2, // 去除表头和控制行 }; const chartOptions = this.$parseChartOptions(rows[0][0]); - const chartOptionsSign = this.$engine.md5(rows[0][0]); + const chartOptionsSign = this.$engine.hash(rows[0][0]); // 如果需要生成图表, if (chartOptions) { rows[0][0] = ''; @@ -196,7 +196,7 @@ export default class Table extends ParagraphBase { ? `~CTHD${tableHeader}~CTHD$~CTBD${tableRows}~CTBD$` : `~CTBD${tableRows}~CTBD$`; const html = cacheSrc; - const sign = this.$engine.md5(html); + const sign = this.$engine.hash(html); const renderHtml = html .replace(/~CTHD\$/g, '') .replace(/~CTHD/g, '') diff --git a/src/core/hooks/Toc.js b/src/core/hooks/Toc.js index f18846ab..46835c11 100644 --- a/src/core/hooks/Toc.js +++ b/src/core/hooks/Toc.js @@ -243,13 +243,13 @@ export default class Toc extends ParagraphBase { let $str = super.afterMakeHtml(str); const headerList = []; const headerRegex = /]*? id="([^"]+?)"[^>]*?>(?:|)(.+?)<\/h\1>/g; - let str2Md5 = ''; + let str2Hash = ''; $str.replace(headerRegex, (match, level, id, text) => { const $text = text.replace(/~fn#[0-9]+#/g, ''); headerList.push({ level: +level, id, text: $text }); - str2Md5 += `${level}${id}`; + str2Hash += `${level}${id}`; }); - str2Md5 = this.$engine.md5(str2Md5); + str2Hash = this.$engine.hash(str2Hash); $str = $str.replace(/(?:^|\n)(\[\[|\[|【【)(toc|TOC)(\]\]|\]|】】)([<~])/, (match) => match.replace(/(\]\]|\]|】】)([<~])/, '$1\n$2'), ); @@ -257,13 +257,13 @@ export default class Toc extends ParagraphBase { // TODO: fix this error // @ts-expect-error $str = $str.replace(this.RULE.extend.reg, (match, preLinesMatch) => - this.$makeToc(headerList, str2Md5, preLinesMatch), + this.$makeToc(headerList, str2Hash, preLinesMatch), ); // 处理标准语法 // TODO: fix this error // @ts-expect-error $str = $str.replace(this.RULE.standard.reg, (match, preLinesMatch) => - this.$makeToc(headerList, str2Md5, preLinesMatch), + this.$makeToc(headerList, str2Hash, preLinesMatch), ); // 重置toc状态 this.isFirstTocToken = true; diff --git a/src/toolbars/Toc.js b/src/toolbars/Toc.js index c4d86c83..ea4a1820 100644 --- a/src/toolbars/Toc.js +++ b/src/toolbars/Toc.js @@ -14,7 +14,6 @@ * limitations under the License. */ import { createElement } from '../utils/dom'; -import md5 from 'md5'; /** * 悬浮目录 */ @@ -197,7 +196,7 @@ export default class Toc { tocStr += item.text; return item; }); - tocStr = md5(tocStr); + tocStr = this.$cherry.engine.hash(tocStr); if (this.tocStr !== tocStr) { this.tocStr = tocStr; let tocHtml = '';