From bfaa00e5f5bd35ea880eda519e5a4b8fd9fefe15 Mon Sep 17 00:00:00 2001 From: jocs Date: Sat, 12 Oct 2024 21:20:37 +0800 Subject: [PATCH 1/3] fix: undo error when input # --- examples/package.json | 4 +- packages/core/src/editor/index.ts | 19 +++---- packages/core/src/history/index.ts | 88 +++++++++++++++--------------- packages/core/src/muya.ts | 2 +- packages/core/src/state/index.ts | 4 ++ 5 files changed, 61 insertions(+), 56 deletions(-) diff --git a/examples/package.json b/examples/package.json index 9e0f330..17bc4af 100644 --- a/examples/package.json +++ b/examples/package.json @@ -2,14 +2,14 @@ "name": "muya-examples", "version": "0.0.1", "description": "Muya vanilla ts demo project", + "author": "jocs ", "scripts": { "dev:demo": "vite" }, "keywords": [], - "author": "jocs ", "license": "MIT", "dependencies": { "@muyajs/core": "workspace:*", "intl-segmenter-polyfill": "^0.4.4" } -} +} \ No newline at end of file diff --git a/packages/core/src/editor/index.ts b/packages/core/src/editor/index.ts index a2d536b..a522b10 100644 --- a/packages/core/src/editor/index.ts +++ b/packages/core/src/editor/index.ts @@ -19,14 +19,15 @@ import { registerBlocks } from '../block'; const debug = logger('editor:'); -class Editor { - public jsonState: JSONState; - public inlineRenderer: InlineRenderer; - public selection: Selection; - public searchModule: Search; - public clipboard: Clipboard; - public history: History; - public scrollPage: Nullable = null; +export class Editor { + jsonState: JSONState; + inlineRenderer: InlineRenderer; + selection: Selection; + searchModule: Search; + clipboard: Clipboard; + history: History; + scrollPage: Nullable = null; + private _activeContentBlock: Nullable = null; constructor(public muya: Muya) { @@ -334,5 +335,3 @@ class Editor { this.focus(); } } - -export default Editor; diff --git a/packages/core/src/history/index.ts b/packages/core/src/history/index.ts index e64722a..f8bb3fd 100644 --- a/packages/core/src/history/index.ts +++ b/packages/core/src/history/index.ts @@ -21,7 +21,10 @@ interface IStack { redo: IOperation[]; } -type HistoryAction = 'undo' | 'redo'; +enum HistoryAction { + UNDO = 'undo', + REDO = 'redo', +} const DEFAULT_OPTIONS = { delay: 1000, @@ -30,10 +33,10 @@ const DEFAULT_OPTIONS = { }; class History { - private lastRecorded: number = 0; - private ignoreChange: boolean = false; - private selectionStack: (Nullable)[] = []; - private stack: IStack = { + private _lastRecorded: number = 0; + private _ignoreChange: boolean = false; + private _selectionStack: (Nullable)[] = []; + private _stack: IStack = { undo: [], redo: [], }; @@ -43,121 +46,120 @@ class History { } constructor(public muya: Muya, private options: IOptions = DEFAULT_OPTIONS) { - this.listen(); + this._listen(); } - listen() { + private _listen() { this.muya.eventCenter.on( 'json-change', ({ op, source, - doc, + prevDoc, }: { op: JSONOpList; source: string; + prevDoc: TState[]; doc: TState[]; }) => { - if (this.ignoreChange) + if (this._ignoreChange) return; if (!this.options.userOnly || source === 'user') - this.record(op, doc); + this._record(op, prevDoc); else this.transform(op); }, ); } - change(source: HistoryAction, dest: HistoryAction) { - if (this.stack[source].length === 0) + private _change(source: HistoryAction, dest: HistoryAction) { + if (this._stack[source].length === 0) return; - const { operation, selection } = this.stack[source].pop()!; + const { operation, selection } = this._stack[source].pop()!; const inverseOperation = json1.type.invert(operation); - this.stack[dest].push({ + this._stack[dest].push({ operation: inverseOperation as JSONOpList, selection: this.selection.getSelection(), }); - this.lastRecorded = 0; - this.ignoreChange = true; + this._lastRecorded = 0; + this._ignoreChange = true; this.muya.editor.updateContents(operation, selection, 'user'); - this.ignoreChange = false; + this._ignoreChange = false; this.getLastSelection(); } clear() { - this.stack = { undo: [], redo: [] }; + this._stack = { undo: [], redo: [] }; } cutoff() { - this.lastRecorded = 0; + this._lastRecorded = 0; } getLastSelection() { - this.selectionStack.push(this.selection.getSelection()); + this._selectionStack.push(this.selection.getSelection()); - if (this.selectionStack.length > 2) - this.selectionStack.shift(); + if (this._selectionStack.length > 2) + this._selectionStack.shift(); - return this.selectionStack.length === 2 ? this.selectionStack[0] : null; + return this._selectionStack.length === 2 ? this._selectionStack[0] : null; } - record(op: JSONOpList, doc: TState[]) { + private _record(op: JSONOpList, doc: TState[]) { if (op.length === 0) return; let selection = this.getLastSelection(); - this.stack.redo = []; - let undoOperation = json1.type.invert(op); + this._stack.redo = []; + let undoOperation = json1.type.invertWithDoc(op, doc as unknown as Doc); + const timestamp = Date.now(); if ( - this.lastRecorded + this.options.delay > timestamp - && this.stack.undo.length > 0 + this._lastRecorded + this.options.delay > timestamp + && this._stack.undo.length > 0 ) { const { operation: lastOperation, selection: lastSelection } - = this.stack.undo.pop()!; + = this._stack.undo.pop()!; selection = lastSelection; - undoOperation = json1.type.makeInvertible( - json1.type.compose(undoOperation, lastOperation), - doc as unknown as Doc, - ); + undoOperation = json1.type.compose(undoOperation, lastOperation); } else { - this.lastRecorded = timestamp; + this._lastRecorded = timestamp; } if (!undoOperation || undoOperation.length === 0) return; - this.stack.undo.push({ operation: undoOperation, selection }); + this._stack.undo.push({ operation: undoOperation, selection }); - if (this.stack.undo.length > this.options.maxStack) - this.stack.undo.shift(); + if (this._stack.undo.length > this.options.maxStack) + this._stack.undo.shift(); } canRedo() { - return this.stack.redo.length > 0; + return this._stack.redo.length > 0; } redo() { - this.change('redo', 'undo'); + this._change(HistoryAction.REDO, HistoryAction.UNDO); } transform(op: JSONOpList) { - transformStack(this.stack.undo, op); - transformStack(this.stack.redo, op); + transformStack(this._stack.undo, op); + transformStack(this._stack.redo, op); } canUndo() { - return this.stack.undo.length > 0; + return this._stack.undo.length > 0; } undo() { - this.change('undo', 'redo'); + this._change(HistoryAction.UNDO, HistoryAction.REDO); } } diff --git a/packages/core/src/muya.ts b/packages/core/src/muya.ts index db39dee..b5c21b9 100644 --- a/packages/core/src/muya.ts +++ b/packages/core/src/muya.ts @@ -2,7 +2,7 @@ import { CLASS_NAMES, MUYA_DEFAULT_OPTIONS, } from './config/index'; -import Editor from './editor/index'; +import { Editor } from './editor/index'; import EventCenter from './event/index'; import I18n from './i18n/index'; import { Ui } from './ui/ui'; diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index 1bde24f..af18448 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -107,6 +107,7 @@ class JSONState { } dispatch(op: JSONOpList, source = 'user' /* user, api */) { + const prevDoc = this.getState(); this.apply(op); // TODO: remove doc in future const doc = this.getState(); @@ -114,6 +115,7 @@ class JSONState { this.muya.eventCenter.emit('json-change', { op, source, + prevDoc, doc, }); } @@ -137,12 +139,14 @@ class JSONState { requestAnimationFrame(() => { const op = this._operationCache.reduce(json1.type.compose as any); + const prevDoc = this.getState(); this.apply(op); // TODO: remove doc in future const doc = this.getState(); this.muya.eventCenter.emit('json-change', { op, source: 'user', + prevDoc, doc, }); this._operationCache = []; From 2d9c12828f0e8592fa1c02cc80b4b8f133ff6e70 Mon Sep 17 00:00:00 2001 From: jocs Date: Sat, 12 Oct 2024 21:28:29 +0800 Subject: [PATCH 2/3] fix: undo error when input # --- packages/core/src/block/base/content.ts | 27 +++++++------- packages/core/src/block/base/format.ts | 35 ++++++++++++------- .../src/block/commonMark/html/htmlPreview.ts | 2 +- .../block/content/paragraphContent/index.ts | 13 ++++--- packages/core/src/clipboard/index.ts | 6 ++-- packages/core/src/config/index.ts | 2 +- packages/core/src/event/index.ts | 3 +- packages/core/src/inlineRenderer/lexer.ts | 7 ++-- .../core/src/inlineRenderer/renderer/image.ts | 8 +++-- .../inlineRenderer/renderer/loadImageAsync.ts | 2 +- packages/core/src/inlineRenderer/rules.ts | 34 +++++++++--------- packages/core/src/inlineRenderer/utils.ts | 16 +++++---- packages/core/src/selection/dom.ts | 3 +- packages/core/src/state/markdownToState.ts | 3 +- packages/core/src/state/stateToMarkdown.ts | 3 +- packages/core/src/ui/baseFloat/index.ts | 3 +- packages/core/src/ui/imageResizeBar/index.ts | 3 +- .../core/src/ui/inlineFormatToolbar/index.ts | 3 +- .../core/src/ui/paragraphFrontButton/index.ts | 3 +- .../core/src/ui/paragraphFrontMenu/index.ts | 8 +++-- packages/core/src/ui/tableDragBar/index.ts | 18 ++++++---- packages/core/src/utils/image.ts | 7 ++-- packages/core/src/utils/index.ts | 6 ++-- packages/core/src/utils/logger.ts | 5 +-- .../core/src/utils/marked/extensions/emoji.ts | 2 +- .../core/src/utils/marked/extensions/math.ts | 2 +- packages/core/src/utils/marked/frontMatter.ts | 2 +- packages/core/src/utils/paste.ts | 2 +- packages/core/src/utils/search.ts | 2 +- .../core/src/utils/turndownService/index.ts | 2 +- packages/core/src/utils/url.ts | 3 +- 31 files changed, 139 insertions(+), 96 deletions(-) diff --git a/packages/core/src/block/base/content.ts b/packages/core/src/block/base/content.ts index 502e6db..79600ed 100644 --- a/packages/core/src/block/base/content.ts +++ b/packages/core/src/block/base/content.ts @@ -144,8 +144,9 @@ class Content extends TreeNode { if ( (event.key === EVENT_KEYS.ArrowUp && topOffset > 0) || (event.key === EVENT_KEYS.ArrowDown && bottomOffset > 0) - ) + ) { return; + } const { muya } = this; let cursorBlock = null; @@ -328,13 +329,13 @@ class Content extends TreeNode { else if ( !event.inputType.includes('delete') && inputChar === postInputChar - && ((autoPairQuote && /[']{1}/.test(inputChar)) - || (autoPairQuote && /["]{1}/.test(inputChar)) - || (autoPairBracket && /[}\])]{1}/.test(inputChar)) - || (autoPairMarkdownSyntax && /[$]{1}/.test(inputChar)) + && ((autoPairQuote && /'/.test(inputChar)) + || (autoPairQuote && /"/.test(inputChar)) + || (autoPairBracket && /[}\])]/.test(inputChar)) + || (autoPairMarkdownSyntax && /\$/.test(inputChar)) || (autoPairMarkdownSyntax - && /[*$`~_]{1}/.test(inputChar) - && /[_*~]{1}/.test(prePreInputChar))) + && /[*$`~_]/.test(inputChar) + && /[_*~]/.test(prePreInputChar))) ) { needRender = true; text = text.substring(0, offset) + text.substring(offset + 1); @@ -345,16 +346,16 @@ class Content extends TreeNode { if ( !/\\/.test(preInputChar) && ((autoPairQuote - && /[']{1}/.test(inputChar) - && !/[a-zA-Z\d]{1}/.test(preInputChar)) - || (autoPairQuote && /["]{1}/.test(inputChar)) - || (autoPairBracket && /[{[(]{1}/.test(inputChar)) + && /'/.test(inputChar) + && !/[a-z\d]/i.test(preInputChar)) + || (autoPairQuote && /"/.test(inputChar)) + || (autoPairBracket && /[{[(]/.test(inputChar)) || (type === 'format' && !isInInlineMath && !isInInlineCode && autoPairMarkdownSyntax - && !/[a-z0-9]{1}/i.test(preInputChar) - && /[*$`~_]{1}/.test(inputChar))) + && !/[a-z0-9]/i.test(preInputChar) + && /[*$`~_]/.test(inputChar))) ) { needRender = true; text diff --git a/packages/core/src/block/base/format.ts b/packages/core/src/block/base/format.ts index f9f8b41..faff4f9 100644 --- a/packages/core/src/block/base/format.ts +++ b/packages/core/src/block/base/format.ts @@ -326,8 +326,9 @@ class Format extends Content { [Math.max(0, start - 1), Math.min(textLen, end + 1)], [focusOffset, focusOffset], ) - ) + ) { return true; + } } return false; @@ -567,8 +568,9 @@ class Format extends Content { if ( this.isComposed || /historyUndo|historyRedo/.test((event as InputEvent).inputType) - ) + ) { return; + } const { domNode } = this; const { start, end } = this.getCursor()!; @@ -720,7 +722,7 @@ class Format extends Content { for (const l of lines) { const THEMATIC_BREAK_REG - = / {0,3}(?:\* *\* *\*|- *- *-|_ *_ *_)[ \*\-\_]*$/; + = / {0,3}(?:\* *\* *\*|- *- *-|_ *_ *_)[ *\-_]*$/; if (THEMATIC_BREAK_REG.test(l) && !thematicLineHasPushed) { thematicLine = l; thematicLineHasPushed = true; @@ -844,15 +846,16 @@ class Format extends Content { const { preferLooseListItem } = muya.options; const listItem = parent!.parent!; const list = listItem?.parent as BulletList; - const matches = text.match(/^\[([x ]{1})\] {1,4}([\s\S]*)$/i); + const matches = text.match(/^\[([x ])\] {1,4}([\s\S]*)$/i); if ( !list || list.blockName !== 'bullet-list' || !parent!.isFirstChild() || matches == null - ) + ) { return; + } const listState = { name: 'task-list', @@ -937,8 +940,9 @@ class Format extends Content { if ( this.parent!.blockName === 'atx-heading' && (this.parent as AtxHeading).meta.level === level - ) + ) { return; + } const { hasSelection } = this; const { start, end } = this.getCursor()!; @@ -950,7 +954,7 @@ class Format extends Content { let atxLineHasPushed = false; for (const l of lines) { - if (/^ {0,3}#{1,6}(?=\s{1,}|$)/.test(l) && !atxLineHasPushed) { + if (/^ {0,3}#{1,6}(?=\s+|$)/.test(l) && !atxLineHasPushed) { atxLine = l; atxLineHasPushed = true; } @@ -1017,8 +1021,9 @@ class Format extends Content { if ( this.parent?.blockName === 'setext-heading' && (this.parent as SetextHeading).meta.level === level - ) + ) { return; + } const { hasSelection } = this; const { text, muya } = this; @@ -1028,7 +1033,7 @@ class Format extends Content { let setextLineHasPushed = false; for (const l of lines) { - if (/^ {0,3}(?:={3,}|-{3,})(?= {1,}|$)/.test(l) && !setextLineHasPushed) + if (/^ {0,3}(?:={3,}|-{3,})(?= +|$)/.test(l) && !setextLineHasPushed) setextLineHasPushed = true; else if (!setextLineHasPushed) setextLines.push(l); @@ -1211,8 +1216,9 @@ class Format extends Content { !force && (this.parent!.blockName === 'setext-heading' || this.parent!.blockName === 'paragraph') - ) + ) { return; + } const { text, muya, hasSelection } = this; const { start, end } = this.getCursor()!; @@ -1335,8 +1341,9 @@ class Format extends Content { needRemovedBlock && needRemovedBlock.isOnlyChild() && !needRemovedBlock.isScrollPage - ) + ) { needRemovedBlock = needRemovedBlock.parent; + } this.text = text + nextBlock.text; this.setCursor(start.offset, end.offset, true); @@ -1396,8 +1403,9 @@ class Format extends Content { checkTokenIsInlineFormat(token) && start.offset >= token.range.start && end.offset <= token.range.end - ) + ) { formats.push(token); + } if ( checkTokenIsInlineFormat(token) @@ -1407,8 +1415,9 @@ class Format extends Content { && end.offset <= token.range.end) || (start.offset <= token.range.start && token.range.end <= end.offset)) - ) + ) { neighbors.push(token); + } if ('children' in token && Array.isArray(token.children)) iterator(token.children); diff --git a/packages/core/src/block/commonMark/html/htmlPreview.ts b/packages/core/src/block/commonMark/html/htmlPreview.ts index 3a04ea8..bcafda6 100644 --- a/packages/core/src/block/commonMark/html/htmlPreview.ts +++ b/packages/core/src/block/commonMark/html/htmlPreview.ts @@ -45,7 +45,7 @@ class HTMLPreview extends Parent { const htmlContent = sanitize(html, PREVIEW_DOMPURIFY_CONFIG, disableHtml) as string; // handle empty html bock - if (/^<([a-z][a-z\d]*)[^>]*?>(\s*)<\/\1>$/.test(htmlContent.trim())) { + if (/^<([a-z][a-z\d]*)[^>]*>(\s*)<\/\1>$/.test(htmlContent.trim())) { this.domNode!.innerHTML = '
<Empty HTML Block>
'; } diff --git a/packages/core/src/block/content/paragraphContent/index.ts b/packages/core/src/block/content/paragraphContent/index.ts index 51b0211..0b9ddd0 100644 --- a/packages/core/src/block/content/paragraphContent/index.ts +++ b/packages/core/src/block/content/paragraphContent/index.ts @@ -26,7 +26,7 @@ enum UnindentType { const debug = logger('paragraph:content'); -const HTML_BLOCK_REG = /^<([a-zA-Z\d-]+)(?=\s|>)[^<>]*?>$/; +const HTML_BLOCK_REG = /^<([a-z\d-]+)(?=\s|>)[^<>]*>$/i; const BOTH_SIDES_FORMATS = [ 'strong', @@ -137,6 +137,7 @@ class ParagraphContent extends Format { event.preventDefault(); event.stopPropagation(); + // eslint-disable-next-line regexp/no-super-linear-backtracking const TABLE_BLOCK_REG = /^\|.*?(\\*)\|.*?(\\*)\|/; const MATH_BLOCK_REG = /^\$\$/; const { text } = this; @@ -523,8 +524,9 @@ class ParagraphContent extends Format { listParent && (listParent.blockName === 'list-item' || listParent.blockName === 'task-list-item') - ) + ) { return list.prev ? UnindentType.INDENT : UnindentType.REPLACEMENT; + } return null; } @@ -545,8 +547,9 @@ class ParagraphContent extends Format { (listItem.blockName !== 'list-item' && listItem.blockName !== 'task-list-item') || !this.isCollapsed - ) + ) { return false; + } return list && /ol|ul/.test(list.tagName) && listItem.prev; } @@ -564,8 +567,9 @@ class ParagraphContent extends Format { || list == null || listParent == null || cursor == null - ) + ) { return; + } const { start, end } = cursor; @@ -705,6 +709,7 @@ class ParagraphContent extends Format { }); let result = null; + // eslint-disable-next-line complexity const walkTokens = (ts: Token[]) => { for (const token of ts) { const { type, range } = token; diff --git a/packages/core/src/clipboard/index.ts b/packages/core/src/clipboard/index.ts index 2b3ce63..52b8000 100644 --- a/packages/core/src/clipboard/index.ts +++ b/packages/core/src/clipboard/index.ts @@ -64,8 +64,9 @@ class Clipboard { /Alt|Option|Meta|Shift|CapsLock|ArrowUp|ArrowDown|ArrowLeft|ArrowRight/.test( key, ) - ) + ) { return; + } if (metaKey) return; @@ -606,8 +607,9 @@ class Clipboard { if ( originWrapperBlock?.blockName === 'paragraph' && (originWrapperBlock.getState() as any).text === '' - ) + ) { originWrapperBlock.remove(); + } const cursorBlock = wrapperBlock?.firstContentInDescendant(); const offset = cursorBlock?.text.length; diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index d155921..7fdc86c 100644 --- a/packages/core/src/config/index.ts +++ b/packages/core/src/config/index.ts @@ -380,7 +380,7 @@ export const isWin && /win32|wow32|win64|wow64/i.test(window.navigator.userAgent); // http[s] (domain or IPv4 or localhost or IPv6) [port] /not-white-space export const URL_REG - = /^http(s)?:\/\/([a-z0-9\-._~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(:[0-9]{1,5})?\/[\S]+/i; + = /^http(s)?:\/\/([\w\-.~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(:\d{1,5})?\/\S+/i; export const PREVIEW_DOMPURIFY_CONFIG = { // do not forbid `class` because `code` element use class to present language FORBID_ATTR: ['style', 'contenteditable'], diff --git a/packages/core/src/event/index.ts b/packages/core/src/event/index.ts index 96da309..418838c 100644 --- a/packages/core/src/event/index.ts +++ b/packages/core/src/event/index.ts @@ -141,8 +141,9 @@ class EventCenter { && event === cEvent && listener === cListener && capture === cCapture - ) + ) { return true; + } } return false; diff --git a/packages/core/src/inlineRenderer/lexer.ts b/packages/core/src/inlineRenderer/lexer.ts index a648aa6..c1fd1b2 100644 --- a/packages/core/src/inlineRenderer/lexer.ts +++ b/packages/core/src/inlineRenderer/lexer.ts @@ -20,7 +20,7 @@ import { // const CAN_NEST_RULES = ['strong', 'em', 'link', 'del', 'a_link', 'reference_link', 'html_tag'] // disallowed html tags in https://github.github.com/gfm/#raw-html const disallowedHtmlTag - = /(?:title|textarea|style|xmp|iframe|noembed|noframes|script|plaintext)/i; + = /title|textarea|style|xmp|iframe|noembed|noframes|script|plaintext/i; function tokenizerFac(src: string, beginRules: BeginRules | null, inlineRules: InlineRules, pos = 0, top: boolean, labels: Labels, options: ITokenizerFacOptions) { const originSrc = src; @@ -183,8 +183,9 @@ function tokenizerFac(src: string, beginRules: BeginRules | null, inlineRules: I if ( rule === 'emoji' && !lowerPriority(src, to[0].length, validateRules) - ) + ) { break; + } inChunk = true; pushPending(); const range = { @@ -445,7 +446,7 @@ function tokenizerFac(src: string, beginRules: BeginRules | null, inlineRules: I if ( autoLinkExtTo && top - && (pos === 0 || /[* _~(]{1}/.test(originSrc[pos - 1])) + && (pos === 0 || /[* _~(]/.test(originSrc[pos - 1])) ) { pushPending(); tokens.push({ diff --git a/packages/core/src/inlineRenderer/renderer/image.ts b/packages/core/src/inlineRenderer/renderer/image.ts index 9a5f3cd..6023301 100644 --- a/packages/core/src/inlineRenderer/renderer/image.ts +++ b/packages/core/src/inlineRenderer/renderer/image.ts @@ -93,8 +93,9 @@ export default function image( selectedImage && selectedImage.token.attrs.src === src && selectedImage.imageId !== id - ) + ) { selectedImage.imageId = id; + } src = this.urlMap.get(src)!; isSuccess = true; @@ -128,14 +129,15 @@ export default function image( && SelectedImageBlock === block && selectedToken.range.start === token.range.start && selectedToken.range.end === token.range.end - ) + ) { wrapperSelector += `.${CLASS_NAMES.MU_INLINE_IMAGE_SELECTED}`; + } } const renderImage = () => { const data = { props: { - alt: alt.replace(/[`*{}[\]()#+\-.!_>~:|<>$]/g, ''), + alt: alt.replace(/[`*{}[\]()#+\-.!_>~:|<$]/g, ''), src, title, }, diff --git a/packages/core/src/inlineRenderer/renderer/loadImageAsync.ts b/packages/core/src/inlineRenderer/renderer/loadImageAsync.ts index 93d8142..ae1afb8 100644 --- a/packages/core/src/inlineRenderer/renderer/loadImageAsync.ts +++ b/packages/core/src/inlineRenderer/renderer/loadImageAsync.ts @@ -28,7 +28,7 @@ export default function loadImageAsync( const img = document.createElement('img'); img.src = url; if (attrs.alt) - img.alt = attrs.alt.replace(/[`*{}[\]()#+\-.!_>~:|<>$]/g, ''); + img.alt = attrs.alt.replace(/[`*{}[\]()#+\-.!_>~:|<$]/g, ''); if (attrs.title) img.setAttribute('title', attrs.title); if (attrs.width && typeof attrs.width === 'number') diff --git a/packages/core/src/inlineRenderer/rules.ts b/packages/core/src/inlineRenderer/rules.ts index 9f7cef3..94f05e7 100644 --- a/packages/core/src/inlineRenderer/rules.ts +++ b/packages/core/src/inlineRenderer/rules.ts @@ -1,61 +1,61 @@ import { escapeCharacters } from '../config/escapeCharacter'; export const beginRules = { - hr: /^(\*{3,}$|^\-{3,}$|^\_{3,}$)/, + hr: /^(\*{3,}$|^-{3,}$|^_{3,}$)/, code_fence: /^(`{3,})([^`]*)$/, - header: /(^ {0,3}#{1,6}(\s{1,}|$))/, + header: /(^ {0,3}#{1,6}(\s+|$))/, reference_definition: - /^( {0,3}\[)([^\]]+?)(\\*)(\]: *)(]+)(>?)(?:( +)(["'(]?)([^\n"'\(\)]+)\9)?( *)$/, + /^( {0,3}\[)([^\]]+?)(\\*)(\]: *)(]+)(>?)(?:( +)(["'(]?)([^\n"'()]+)\9)?( *)$/, // extra syntax (not belongs to GFM) multiple_math: /^(\$\$)$/, }; export const endRules = { - tail_header: /^(\s{1,}#{1,})(\s*)$/, + tail_header: /^(\s+#+)(\s*)$/, }; export type BeginRules = typeof beginRules; export const commonMarkRules = { strong: /^(\*\*|__)(?=\S)([\s\S]*?[^\s\\])(\\*)\1(?!(\*|_))/, // can nest - em: /^(\*|_)(?=\S)([\s\S]*?[^\s\*\\])(\\*)\1(?!\1)/, // can nest - inline_code: /^(`{1,3})([^`]+?|.{2,})\1/, - image: /^(\!\[)(.*?)(\\*)\]\((.*)(\\*)\)/, - link: /^(\[)((?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*?)(\\*)\]\((.*)(\\*)\)/, // can nest + em: /^(\*|_)(?=\S)([\s\S]*?[^\s*\\])(\\*)\1(?!\1)/, // can nest + inline_code: /^(`{1,3})([^`]+|.{2,})\1/, + image: /^(!\[)(.*?)(\\*)\]\((.*)(\\*)\)/, + link: /^(\[)((?:\[[^\]]*\]|[^[\]]|\](?=[^[]*\]))*?)(\\*)\]\((.*)(\\*)\)/, // can nest reference_link: /^\[([^\]]+?)(\\*)\](?:\[([^\]]*?)(\\*)\])?/, - reference_image: /^\!\[([^\]]+?)(\\*)\](?:\[([^\]]*?)(\\*)\])?/, + reference_image: /^!\[([^\]]+?)(\\*)\](?:\[([^\]]*?)(\\*)\])?/, html_tag: - /^(|(<([a-zA-Z]{1}[a-zA-Z\d-]*) *[^\n<>]* *(?:\/)?>)(?:([\s\S]*?)(<\/\3 *>))?)/, // raw html + /^(|(<([a-z][a-z\d-]*)[^\n<>]*>)(?:([\s\S]*?)(<\/\3 *>))?)/i, // raw html html_escape: new RegExp(`^(${escapeCharacters.join('|')})`, 'i'), soft_line_break: /^(\n)(?!\n)/, hard_line_break: /^( {2,})(\n)(?!\n)/, // patched math marker `$` - backlash: /^(\\)([\\`*{}\[\]()#+\-.!_>~:\|\<\>$]{1})/, + backlash: /^(\\)([\\`*{}[\]()#+\-.!_>~:|<$])/, }; export type CommonMarkRules = typeof commonMarkRules; export const gfmRules = { - emoji: /^(:)([a-z_\d+-]+?)\1/, + emoji: /^(:)([a-z_\d+-]+)\1/, del: /^(~{2})(?=\S)([\s\S]*?\S)(\\*)\1/, // can nest auto_link: - /^<(?:([a-zA-Z]{1}[a-zA-Z\d\+\.\-]{1,31}:[^ <>]*)|([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*))>/, + /^<(?:([a-z][a-z\d+.\-]{1,31}:[^ <>]*)|([\w.!#$%&'*+/=?^`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*))>/i, // (extended www autolink|extended url autolink|extended email autolink) the email regexp is the same as auto_link. auto_link_extension: - /^(?:(www\.[a-z_-]+\.[a-z]{2,}(?::[0-9]{1,5})?(?:\/[\S]+)?)|(http(?:s)?:\/\/(?:[a-z0-9\-._~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(?::[0-9]{1,5})?(?:\/[\S]+)?)|([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*))(?=\s|$)/, + /^(?:(www\.[a-z_-]+\.[a-z]{2,}(?::\d{1,5})?(?:\/\S+)?)|(https?:\/\/(?:[a-z0-9\-._~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(?::\d{1,5})?(?:\/\S+)?)|([\w.!#$%&'*+/=?^`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*))(?=\s|$)/, }; export type GfmRules = typeof gfmRules; // Markdown extensions (not belongs to GFM and Commonmark) export const inlineExtensionRules = { - inline_math: /^(\$)([^\$]*?[^\$\\])(\\*)\1(?!\1)/, + inline_math: /^(\$)([^$]*?[^$\\])(\\*)\1(?!\1)/, // This is not the best regexp, because it not support `2^2\\^`. - superscript: /^(\^)((?:[^\^\s]|(?<=\\)\1|(?<=\\) )+?)(?', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'] export const PUNCTUATION_REG - = /[!"#$%&'()*+,\-./:;<=>?@\[\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/; + = /[!"#$%&'()*+,\-./:;<=>?@[\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/; // selected from https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes export const WHITELIST_ATTRIBUTES = [ @@ -66,7 +66,7 @@ export const WHITELIST_ATTRIBUTES = [ const UNICODE_WHITESPACE_REG = /^\s/; function validWidthAndHeight(value: string) { - if (!/^\d{1,}$/.test(value)) + if (!/^\d+$/.test(value)) return ''; const num = Number.parseInt(value); @@ -163,8 +163,9 @@ function canOpenEmphasis(src: string, marker: string, pending: string) { UNICODE_WHITESPACE_REG.test(precededChar) || PUNCTUATION_REG.test(precededChar) ) - ) + ) { return false; + } if ( /_/.test(marker) @@ -172,8 +173,9 @@ function canOpenEmphasis(src: string, marker: string, pending: string) { UNICODE_WHITESPACE_REG.test(precededChar) || PUNCTUATION_REG.test(precededChar) ) - ) + ) { return false; + } return true; } @@ -193,8 +195,9 @@ function canCloseEmphasis(src: string, offset: number, marker: string) { UNICODE_WHITESPACE_REG.test(followedChar) || PUNCTUATION_REG.test(followedChar) ) - ) + ) { return false; + } if ( /_/.test(marker) @@ -202,8 +205,9 @@ function canCloseEmphasis(src: string, offset: number, marker: string) { UNICODE_WHITESPACE_REG.test(followedChar) || PUNCTUATION_REG.test(followedChar) ) - ) + ) { return false; + } return true; } diff --git a/packages/core/src/selection/dom.ts b/packages/core/src/selection/dom.ts index 48dce58..77587c2 100644 --- a/packages/core/src/selection/dom.ts +++ b/packages/core/src/selection/dom.ts @@ -41,8 +41,9 @@ export function getTextContent(node: Node, blackList: string[] = []) { && blackList.some( className => node.classList && node.classList.contains(className), ) - ) + ) { return text; + } if (node.nodeType === Node.TEXT_NODE) { text += node.textContent; diff --git a/packages/core/src/state/markdownToState.ts b/packages/core/src/state/markdownToState.ts index a329ab4..0fcd416 100644 --- a/packages/core/src/state/markdownToState.ts +++ b/packages/core/src/state/markdownToState.ts @@ -135,8 +135,9 @@ export class MarkdownToState { if ( trimUnnecessaryCodeBlockEmptyLines && (value.endsWith('\n') || value.startsWith('\n')) - ) + ) { value = value.replace(/\n+$/, '').replace(/^\n+/, ''); + } if (/mermaid|vega-lite|plantuml/.test(lang)) { state = { diff --git a/packages/core/src/state/stateToMarkdown.ts b/packages/core/src/state/stateToMarkdown.ts index d5eb022..a75691e 100644 --- a/packages/core/src/state/stateToMarkdown.ts +++ b/packages/core/src/state/stateToMarkdown.ts @@ -90,8 +90,9 @@ export default class ExportMarkdown { state.name !== 'order-list' && state.name !== 'bullet-list' && state.name !== 'task-list' - ) + ) { lastListBullet = ''; + } switch (state.name) { case 'frontmatter': diff --git a/packages/core/src/ui/baseFloat/index.ts b/packages/core/src/ui/baseFloat/index.ts index 2806496..40dcf7c 100644 --- a/packages/core/src/ui/baseFloat/index.ts +++ b/packages/core/src/ui/baseFloat/index.ts @@ -94,8 +94,9 @@ abstract class BaseFloat { this.status && event.target && Math.abs((event.target as Element).scrollTop - this.lastScrollTop) > 50 - ) + ) { this.hide(); + } }; eventCenter.attachDOMEvent(document, 'click', this.hide.bind(this)); diff --git a/packages/core/src/ui/imageResizeBar/index.ts b/packages/core/src/ui/imageResizeBar/index.ts index d4b0462..c17d6d6 100644 --- a/packages/core/src/ui/imageResizeBar/index.ts +++ b/packages/core/src/ui/imageResizeBar/index.ts @@ -51,8 +51,9 @@ export class ImageResizeBar { !this.resizing && this.status && Math.abs((event.target as HTMLElement).scrollTop - this.lastScrollTop) > 50 - ) + ) { this.hide(); + } }; eventCenter.on('muya-transformer', ({ block, reference, imageInfo }) => { diff --git a/packages/core/src/ui/inlineFormatToolbar/index.ts b/packages/core/src/ui/inlineFormatToolbar/index.ts index fdf35ed..e31b199 100644 --- a/packages/core/src/ui/inlineFormatToolbar/index.ts +++ b/packages/core/src/ui/inlineFormatToolbar/index.ts @@ -133,8 +133,9 @@ export class InlineFormatToolbar extends BaseFloat { f => f.type === i.type || (f.type === 'html_tag' && f.tag === i.type), ) - ) + ) { itemSelector += '.active'; + } return h( itemSelector, diff --git a/packages/core/src/ui/paragraphFrontButton/index.ts b/packages/core/src/ui/paragraphFrontButton/index.ts index 9d0034f..b0c89b7 100644 --- a/packages/core/src/ui/paragraphFrontButton/index.ts +++ b/packages/core/src/ui/paragraphFrontButton/index.ts @@ -219,8 +219,9 @@ export class ParagraphFrontButton { if ( (position === 'down' && block.prev === target) || (position === 'up' && block.next === target) - ) + ) { return; + } if (position === 'up') block.insertInto(block.parent!, target); diff --git a/packages/core/src/ui/paragraphFrontMenu/index.ts b/packages/core/src/ui/paragraphFrontMenu/index.ts index 1ad9559..fbe57cb 100644 --- a/packages/core/src/ui/paragraphFrontMenu/index.ts +++ b/packages/core/src/ui/paragraphFrontMenu/index.ts @@ -103,8 +103,9 @@ export class ParagraphFrontMenu extends BaseFloat { if ( label.startsWith(block.blockName) && label.endsWith(String((block as AtxHeading).meta.level)) - ) + ) { itemSelector += '.active'; + } } else if (label === block?.blockName) { itemSelector += '.active'; @@ -238,14 +239,15 @@ export class ParagraphFrontMenu extends BaseFloat { if ( block.blockName === 'atx-heading' && label.split(' ')[1] === String((oldState as IAtxHeadingState).meta.level) - ) + ) { break; + } const rawText = (oldState as IAtxHeadingState).text; const text = block.blockName === 'paragraph' ? rawText - : rawText.replace(/^ {0,3}#{1,6}(?:\s{1,}|$)/, ''); + : rawText.replace(/^ {0,3}#{1,6}(?:\s+|$)/, ''); replaceBlockByLabel({ block, label, diff --git a/packages/core/src/ui/tableDragBar/index.ts b/packages/core/src/ui/tableDragBar/index.ts index bb31f4c..4985b3c 100644 --- a/packages/core/src/ui/tableDragBar/index.ts +++ b/packages/core/src/ui/tableDragBar/index.ts @@ -449,27 +449,33 @@ export class TableDragBar extends BaseFloat { : 0; if (barType === 'bottom') { cursorRowOffset = rowOffset; - if (columnOffset === index) + if (columnOffset === index) { cursorColumnOffset = curIndex; + } else if ( columnOffset >= Math.min(index, curIndex) && columnOffset <= Math.max(index, curIndex) - ) + ) { cursorColumnOffset = columnOffset + (offset > 0 ? -1 : 1); - else + } + else { cursorColumnOffset = columnOffset; + } } else { cursorColumnOffset = columnOffset; - if (rowOffset === index) + if (rowOffset === index) { cursorRowOffset = curIndex; + } else if ( rowOffset >= Math.min(index, curIndex) && rowOffset <= Math.max(index, curIndex) - ) + ) { cursorRowOffset = rowOffset + (offset > 0 ? -1 : 1); - else + } + else { cursorRowOffset = rowOffset; + } } } diff --git a/packages/core/src/utils/image.ts b/packages/core/src/utils/image.ts index 2e99885..536f4f1 100644 --- a/packages/core/src/utils/image.ts +++ b/packages/core/src/utils/image.ts @@ -29,7 +29,7 @@ export function getImageSrc(src: string) { const EXT_REG = /\.(jpeg|jpg|png|gif|svg|webp)(?=\?|$)/i; // http[s] (domain or IPv4 or localhost or IPv6) [port] /not-white-space const URL_REG - = /^http(s)?:\/\/([a-z0-9\-._~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(:[0-9]{1,5})?\/[\S]+/i; + = /^http(s)?:\/\/([\w\-.~]+\.[a-z]{2,}|[0-9.]+|localhost|\[[a-f0-9.:]+\])(:\d{1,5})?\/\S+/i; const DATA_URL_REG = /^data:image\/[\w+-]+(;[\w-]+=[\w-]+|;base64)*,[a-zA-Z0-9+/]+={0,2}$/; const imageExtension = EXT_REG.test(src); @@ -109,8 +109,9 @@ export async function checkImageContentType(url: string) { contentType && res.status === 200 && /^image\/(?:jpeg|png|gif|svg\+xml|webp)$/.test(contentType) - ) + ) { return true; + } return false; } @@ -122,7 +123,7 @@ export async function checkImageContentType(url: string) { export function correctImageSrc(src: string) { if (src) { // Fix ASCII and UNC paths on Windows (#1997). - if (isWin && /^(?:[a-zA-Z]:\\|[a-zA-Z]:\/).+/.test(src)) { + if (isWin && /^(?:[a-z]:\\|[a-z]:\/).+/i.test(src)) { src = `file:///${src.replace(/\\/g, '/')}`; } else if (isWin && /^\\\\\?\\.+/.test(src)) { diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index b7589f1..2733aca 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -137,7 +137,7 @@ export function escapeHTML(str: string) { export function unescapeHTML(str: string) { return str.replace( - /(?:&|<|>|"|')/g, + /&|<|>|"|'/g, tag => ({ '&': '&', @@ -165,7 +165,7 @@ export function wordCount(markdown: string) { let all = 0; const removedChinese = markdown.replace(/[\u4E00-\u9FA5]/g, ''); - const tokens = removedChinese.split(/[\s\n]+/).filter(t => t); + const tokens = removedChinese.split(/\s+/).filter(t => t); const chineseWordLength = markdown.length - removedChinese.length; word += chineseWordLength + tokens.length; character += tokens.reduce((acc, t) => acc + t.length, 0) + chineseWordLength; @@ -247,7 +247,7 @@ export function adjustOffset(offset: number, block: T, event: block.parent?.blockName === 'atx-heading' && event.key === EVENT_KEYS.ArrowDown ) { - const match = /^\s{0,3}(?:#{1,6})(?:\s{1,}|$)/.exec(block.text); + const match = /^\s{0,3}#{1,6}(?:\s+|$)/.exec(block.text); if (match) return match[0].length; } diff --git a/packages/core/src/utils/logger.ts b/packages/core/src/utils/logger.ts index 362dda1..7d33b46 100644 --- a/packages/core/src/utils/logger.ts +++ b/packages/core/src/utils/logger.ts @@ -10,8 +10,9 @@ function debug(method: TLevel, ...args: unknown[]) { levels.indexOf(method) <= levels.indexOf(level) // eslint-disable-next-line node/prefer-global/process && process.env.NODE_ENV !== 'production' - ) - console[method](...args); // eslint-disable-line no-console + ) { + console[method](...args); + } } function namespace(ns: string): Ilogger { diff --git a/packages/core/src/utils/marked/extensions/emoji.ts b/packages/core/src/utils/marked/extensions/emoji.ts index 6f238cf..80cdf8c 100644 --- a/packages/core/src/utils/marked/extensions/emoji.ts +++ b/packages/core/src/utils/marked/extensions/emoji.ts @@ -1,7 +1,7 @@ import { validEmoji } from '../../../utils/emoji'; const START_REG = /(\s|^):(?!:)/; -const EMOJI_REG = /^(:)([a-z_\d+-]+?)\1/; +const EMOJI_REG = /^(:)([a-z_\d+-]+)\1/; interface IEmojiToken { type: string; diff --git a/packages/core/src/utils/marked/extensions/math.ts b/packages/core/src/utils/marked/extensions/math.ts index 9c9cfed..d9baf57 100644 --- a/packages/core/src/utils/marked/extensions/math.ts +++ b/packages/core/src/utils/marked/extensions/math.ts @@ -16,7 +16,7 @@ interface IOptions { const inlineStartRule = /(\s|^)\${1,2}(?!\$)/; const inlineRule = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n$]))\1(?=[\s?!.,:]|$)/; -const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/; +const blockRule = /^(\${1,2})\n((?:\\[\s\S]|[^\\])+?)\n\1(?:\n|$)/; const DEFAULT_OPTIONS = { throwOnError: false, diff --git a/packages/core/src/utils/marked/frontMatter.ts b/packages/core/src/utils/marked/frontMatter.ts index 7bce603..0820df9 100644 --- a/packages/core/src/utils/marked/frontMatter.ts +++ b/packages/core/src/utils/marked/frontMatter.ts @@ -1,5 +1,5 @@ const FRONT_REG - = /^(?:(?:---\n([\s\S]+?)---)|(?:\+\+\+\n([\s\S]+?)\+\+\+)|(?:;;;\n([\s\S]+?);;;)|(?:\{\n([\s\S]+?)\}))(?:\n{2,}|\n{1,2}$)/; + = /^(?:---\n([\s\S]+?)---|\+\+\+\n([\s\S]+?)\+\+\+|;;;\n([\s\S]+?);;;|\{\n([\s\S]+?)\})(?:\n{2,}|\n{1,2}$)/; const STYLE_LANG = { '-': 'yaml', diff --git a/packages/core/src/utils/paste.ts b/packages/core/src/utils/paste.ts index b28ad86..a2e8c72 100644 --- a/packages/core/src/utils/paste.ts +++ b/packages/core/src/utils/paste.ts @@ -129,7 +129,7 @@ export const getCopyTextType = function ( ) { const getTextType = (text: string) => { const match - = /^<([a-zA-Z\d-]+)(?=\s|>).*?>[\s\S]+?<\/([a-zA-Z\d-]+)>$/.exec( + = /^<([a-z\d-]+)(?=\s|>).*?>[\s\S]+?<\/([a-z\d-]+)>$/i.exec( text.trim(), ); if (match && match[1]) { diff --git a/packages/core/src/utils/search.ts b/packages/core/src/utils/search.ts index 6d161ab..8369ead 100644 --- a/packages/core/src/utils/search.ts +++ b/packages/core/src/utils/search.ts @@ -4,7 +4,7 @@ import type { IMatch, ISearchOption } from '../search/types'; export function matchString(text: string, value: string, options: ISearchOption) { const { isCaseSensitive, isWholeWord, isRegexp } = options; - const SPECIAL_CHAR_REG = /[\[\]\\^$.\|\?\*\+\(\)\/]{1}/g; + const SPECIAL_CHAR_REG = /[[\]\\^$.|?*+()/]/g; let SEARCH_REG = null; let regStr = value; diff --git a/packages/core/src/utils/turndownService/index.ts b/packages/core/src/utils/turndownService/index.ts index ff2b05a..d72a2ba 100644 --- a/packages/core/src/utils/turndownService/index.ts +++ b/packages/core/src/utils/turndownService/index.ts @@ -84,7 +84,7 @@ export function usePluginsAddRules(turndownService: TurndownService) { content = content .replace(/^\n+/, '') // remove leading newlines .replace(/\n+$/, '\n') // replace trailing newlines with just a single one - .replace(/\n/gm, '\n '); // indent + .replace(/\n/g, '\n '); // indent let prefix = `${options.bulletListMarker} `; const parent = node.parentNode as HTMLElement; diff --git a/packages/core/src/utils/url.ts b/packages/core/src/utils/url.ts index c5f61ee..88e01c2 100644 --- a/packages/core/src/utils/url.ts +++ b/packages/core/src/utils/url.ts @@ -5,8 +5,9 @@ export function sanitizeHyperlink(rawLink: string) { rawLink && typeof rawLink === 'string' && isValidAttribute('a', 'href', rawLink) - ) + ) { return rawLink; + } return ''; } From 86f2e141bca4bdc4a8e0c1b93b2dc5107d76ad85 Mon Sep 17 00:00:00 2001 From: jocs Date: Sat, 12 Oct 2024 21:31:29 +0800 Subject: [PATCH 3/3] fix: lint error --- packages/core/src/block/content/paragraphContent/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/block/content/paragraphContent/index.ts b/packages/core/src/block/content/paragraphContent/index.ts index 0b9ddd0..200fa5e 100644 --- a/packages/core/src/block/content/paragraphContent/index.ts +++ b/packages/core/src/block/content/paragraphContent/index.ts @@ -20,8 +20,8 @@ import type { } from '../../../state/types'; enum UnindentType { - INDENT = 'INDENT', - REPLACEMENT = 'REPLACEMENT', + INDENT, + REPLACEMENT, } const debug = logger('paragraph:content');