Repository: nhn/tui.editor (ts)

Issue:
Bold and Italic intersection incorrect Markdown
## Describe the bug
If you use bold and italic at the intersection of a word, then the output markdown will be incorrect

## To Reproduce
Steps to reproduce the behavior:
1. Go to "Wysiwyg" tab
2. Enter text "boldanditalic"
3. Make phrase "boldand" bold, then phrase "anditalic" italic
4. Go to "Markdown" tab. 
5. Incorrect result is preview. Extra asterisks appeared.

![image](https://user-images.githubusercontent.com/8930952/134575623-3b97fe88-4753-4deb-93dd-244d00fb75bd.png)


## Expected behavior
Markdown will be `**bold*****and***&#8203;*italic*` or `**bold*****and***_italic_` and preview:
![image](https://user-images.githubusercontent.com/8930952/134576328-831ca41b-449f-491f-bd8b-e642e4f869f6.png)

Editor version: 3.1.0

Pull request:
fix: revert #2572 (fix #1806 #2708)
see #2742

Patch:
diff --git a/apps/editor/package.json b/apps/editor/package.json
index 83416097..66d4075c 100644
--- a/apps/editor/package.json
+++ b/apps/editor/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@toast-ui/editor",
-  "version": "3.2.0",
+  "version": "3.2.1",
   "description": "GFM  Markdown Wysiwyg Editor - Productive and Extensible",
   "keywords": [
     "nhn",
diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts
index 86fd701a..9fe47a35 100644
--- a/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts
+++ b/apps/editor/src/convertors/toMarkdown/toMdConvertorState.ts
@@ -1,6 +1,6 @@
 import { Node, Mark } from 'prosemirror-model';
 
-import { includes, escape, last, isEndWithSpace, isStartWithSpace } from '@/utils/common';
+import { includes, escape, last } from '@/utils/common';
 
 import { WwNodeType, WwMarkType } from '@t/wysiwyg';
 import {
@@ -10,7 +10,6 @@ import {
   FirstDelimFn,
   InfoForPosSync,
 } from '@t/convertor';
-import { DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE } from '@/utils/constants';
 
 export default class ToMdConvertorState {
   private readonly nodeTypeConvertors: ToMdNodeTypeConvertorMap;
@@ -50,27 +49,11 @@ export default class ToMdConvertorState {
     return /(^|\n)$/.test(this.result);
   }
 
-  private isBetweenSpaces(parent: Node, index: number) {
-    const { content } = parent;
-
-    const isFrontNodeEndWithSpace =
-      index === 0 ||
-      isEndWithSpace(content.child(index - 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE);
-
-    const isRearNodeStartWithSpace =
-      index >= content.childCount - 1 ||
-      isStartWithSpace(content.child(index + 1).text ?? DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE);
-
-    return isFrontNodeEndWithSpace && isRearNodeStartWithSpace;
-  }
-
   private markText(mark: Mark, entering: boolean, parent: Node, index: number) {
     const convertor = this.getMarkConvertor(mark);
 
     if (convertor) {
-      const betweenSpace = this.isBetweenSpaces(parent, entering ? index : index - 1);
-
-      const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering, betweenSpace);
+      const { delim, rawHTML } = convertor({ node: mark, parent, index }, entering);
 
       return (rawHTML as string) || (delim as string);
     }
diff --git a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts
index 8f1a1904..71066e7b 100644
--- a/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts
+++ b/apps/editor/src/convertors/toMarkdown/toMdConvertors.ts
@@ -208,44 +208,29 @@ export const toMdConvertors: ToMdConvertorMap = {
     };
   },
 
-  strong({ node }, { entering }, betweenSpace) {
+  strong({ node }, { entering }) {
     const { rawHTML } = node.attrs;
-    let delim = '**';
-
-    if (!betweenSpace) {
-      delim = entering ? '<strong>' : '</strong>';
-    }
 
     return {
-      delim,
+      delim: '**',
       rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML),
     };
   },
 
-  emph({ node }, { entering }, betweenSpace) {
+  emph({ node }, { entering }) {
     const { rawHTML } = node.attrs;
-    let delim = '*';
-
-    if (!betweenSpace) {
-      delim = entering ? '<em>' : '</em>';
-    }
 
     return {
-      delim,
+      delim: '*',
       rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML),
     };
   },
 
-  strike({ node }, { entering }, betweenSpace) {
+  strike({ node }, { entering }) {
     const { rawHTML } = node.attrs;
-    let delim = '~~';
-
-    if (!betweenSpace) {
-      delim = entering ? '<del>' : '</del>';
-    }
 
     return {
-      delim,
+      delim: '~~',
       rawHTML: entering ? getOpenRawHTML(rawHTML) : getCloseRawHTML(rawHTML),
     };
   },
@@ -368,7 +353,7 @@ function createMarkTypeConvertors(convertors: ToMdConvertorMap) {
   const markTypes = Object.keys(markTypeOptions) as WwMarkType[];
 
   markTypes.forEach((type) => {
-    markTypeConvertors[type] = (nodeInfo, entering, betweenSpace) => {
+    markTypeConvertors[type] = (nodeInfo, entering) => {
       const markOption = markTypeOptions[type];
       const convertor = convertors[type];
 
@@ -377,9 +362,7 @@ function createMarkTypeConvertors(convertors: ToMdConvertorMap) {
       // When calling the converter without using `delim` and `rawHTML` values,
       // the converter is called without parameters.
       const runConvertor = convertor && nodeInfo && !isUndefined(entering);
-      const params = runConvertor
-        ? convertor!(nodeInfo as MarkInfo, { entering }, betweenSpace)
-        : {};
+      const params = runConvertor ? convertor!(nodeInfo as MarkInfo, { entering }) : {};
 
       return { ...params, ...markOption };
     };
diff --git a/apps/editor/src/css/editor.css b/apps/editor/src/css/editor.css
index 0930ce21..abbd2b7b 100644
--- a/apps/editor/src/css/editor.css
+++ b/apps/editor/src/css/editor.css
@@ -1,4 +1,3 @@
-@charset "utf-8";
 /* height */
 .auto-height,
 .auto-height .toastui-editor-defaultUI {
@@ -887,4 +886,4 @@
   .toastui-editor-dropdown-toolbar {
     max-width: none;
   }
-}
\ No newline at end of file
+}
diff --git a/apps/editor/src/utils/common.ts b/apps/editor/src/utils/common.ts
index 37542192..642b012c 100644
--- a/apps/editor/src/utils/common.ts
+++ b/apps/editor/src/utils/common.ts
@@ -263,15 +263,3 @@ export function assign(targetObj: Record<string, any>, obj: Record<string, any>
 export function getSortedNumPair(valueA: number, valueB: number) {
   return valueA > valueB ? [valueB, valueA] : [valueA, valueB];
 }
-
-export function isStartWithSpace(text: string) {
-  const reStartWithSpace = /^\s(\S*)/g;
-
-  return reStartWithSpace.test(text);
-}
-
-export function isEndWithSpace(text: string) {
-  const reEndWithSpace = /(\S*)\s$/g;
-
-  return reEndWithSpace.test(text);
-}
diff --git a/apps/editor/src/utils/constants.ts b/apps/editor/src/utils/constants.ts
index 78d5adf3..2d6cd549 100644
--- a/apps/editor/src/utils/constants.ts
+++ b/apps/editor/src/utils/constants.ts
@@ -20,5 +20,3 @@ export const reBR = /<br\s*\/*>/i;
 export const reHTMLComment = /<! ---->|<!--(?:-?[^>-])(?:-?[^-])*-->/;
 
 export const ALTERNATIVE_TAG_FOR_BR = '</p><p>';
-
-export const DEFAULT_TEXT_NOT_START_OR_END_WITH_SPACE = 'a';
diff --git a/apps/editor/types/convertor.d.ts b/apps/editor/types/convertor.d.ts
index 718c1c7a..2ab9c939 100644
--- a/apps/editor/types/convertor.d.ts
+++ b/apps/editor/types/convertor.d.ts
@@ -113,8 +113,7 @@ export type ToMdNodeTypeConvertorMap = Partial<Record<WwNodeType, ToMdNodeTypeCo
 
 type ToMdMarkTypeConvertor = (
   nodeInfo?: MarkInfo,
-  entering?: boolean,
-  betweenSpace?: boolean
+  entering?: boolean
 ) => ToMdConvertorReturnValues & ToMdMarkTypeOption;
 
 export type ToMdMarkTypeConvertorMap = Partial<Record<WwMarkType, ToMdMarkTypeConvertor>>;
@@ -127,8 +126,7 @@ interface ToMdConvertorContext {
 
 type ToMdConvertor = (
   nodeInfo: NodeInfo | MarkInfo,
-  context: ToMdConvertorContext,
-  betweenSpace?: boolean
+  context: ToMdConvertorContext
 ) => ToMdConvertorReturnValues;
 
 export type ToMdConvertorMap = Partial<Record<WwNodeType | MdNodeType, ToMdConvertor>>;
diff --git a/apps/editor/types/index.d.ts b/apps/editor/types/index.d.ts
index 5a747561..28f6f7cf 100644
--- a/apps/editor/types/index.d.ts
+++ b/apps/editor/types/index.d.ts
@@ -1,4 +1,4 @@
-// Type definitions for TOAST UI Editor v3.2.0
+// Type definitions for TOAST UI Editor v3.2.1
 // TypeScript Version: 4.2.3
 import {
   EditorCore,
diff --git a/apps/react-editor/README.md b/apps/react-editor/README.md
index 0e5269ac..f768e669 100644
--- a/apps/react-editor/README.md
+++ b/apps/react-editor/README.md
@@ -84,7 +84,7 @@ class MyComponent extends React.Component {
   editorRef = React.createRef();
 
   handleClick = () => {
-    this.editorRef.current.getInstance().exec('Bold');
+    this.editorRef.current.getInstance().exec('bold');
   };
 
   render() {
diff --git a/apps/react-editor/package.json b/apps/react-editor/package.json
index 6fa4c0ad..055e1f5f 100644
--- a/apps/react-editor/package.json
+++ b/apps/react-editor/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@toast-ui/react-editor",
-  "version": "3.2.0",
+  "version": "3.2.2",
   "description": "TOAST UI Editor for React",
   "files": [
     "dist",
@@ -35,6 +35,6 @@
     "react-dom": "^17.0.1"
   },
   "dependencies": {
-    "@toast-ui/editor": "^3.2.0"
+    "@toast-ui/editor": "^3.2.1"
   }
 }
diff --git a/apps/vue-editor/package.json b/apps/vue-editor/package.json
index 8ed1dc59..bf0efad5 100644
--- a/apps/vue-editor/package.json
+++ b/apps/vue-editor/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@toast-ui/vue-editor",
-  "version": "3.2.0",
+  "version": "3.2.2",
   "description": "TOAST UI Editor for Vue",
   "main": "dist/toastui-vue-editor.js",
   "module": "dist/esm/index.js",
@@ -34,6 +34,6 @@
     "vue": "^2.5.0"
   },
   "dependencies": {
-    "@toast-ui/editor": "^3.2.0"
+    "@toast-ui/editor": "^3.2.1"
   }
 }
diff --git a/lerna.json b/lerna.json
index 0fec12cf..fa06cc87 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,4 +1,4 @@
 {
   "packages": ["apps/*", "plugins/*", "libs/*"],
-  "version": "3.2.0"
+  "version": "3.2.1"
 }
diff --git a/package-lock.json b/package-lock.json
index 1c6ce789..ed4c4aa8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -79,7 +79,7 @@
     },
     "apps/editor": {
       "name": "@toast-ui/editor",
-      "version": "3.2.0",
+      "version": "3.2.1",
       "license": "MIT",
       "dependencies": {
         "dompurify": "^2.3.3",
@@ -99,10 +99,10 @@
     },
     "apps/react-editor": {
       "name": "@toast-ui/react-editor",
-      "version": "3.2.0",
+      "version": "3.2.2",
       "license": "MIT",
       "dependencies": {
-        "@toast-ui/editor": "^3.2.0"
+        "@toast-ui/editor": "^3.2.1"
       },
       "devDependencies": {
         "@types/react": "^17.0.3",
@@ -115,10 +115,10 @@
     },
     "apps/vue-editor": {
       "name": "@toast-ui/vue-editor",
-      "version": "3.2.0",
+      "version": "3.2.2",
       "license": "MIT",
       "dependencies": {
-        "@toast-ui/editor": "^3.2.0"
+        "@toast-ui/editor": "^3.2.1"
       },
       "devDependencies": {
         "@morgul/snowpack-plugin-vue2": "^0.4.0",
@@ -33420,7 +33420,7 @@
     "@toast-ui/react-editor": {
       "version": "file:apps/react-editor",
       "requires": {
-        "@toast-ui/editor": "^3.2.0",
+        "@toast-ui/editor": "^3.2.1",
         "@types/react": "^17.0.3",
         "react": "^17.0.1",
         "react-dom": "^17.0.1"
@@ -33458,7 +33458,7 @@
       "version": "file:apps/vue-editor",
       "requires": {
         "@morgul/snowpack-plugin-vue2": "^0.4.0",
-        "@toast-ui/editor": "^3.2.0",
+        "@toast-ui/editor": "^3.2.1",
         "vue": "^2.5.0",
         "vue-loader": "^15.9.8",
         "vue-template-compiler": "^2.6.12"


Test patch:
diff --git a/apps/editor/src/__test__/unit/convertor.spec.ts b/apps/editor/src/__test__/unit/convertor.spec.ts
index 2ed8cdd3..6bd84dd1 100644
--- a/apps/editor/src/__test__/unit/convertor.spec.ts
+++ b/apps/editor/src/__test__/unit/convertor.spec.ts
@@ -595,20 +595,7 @@ describe('Convertor', () => {
         | ![altText](imgUrl) **mixed**<ul><li>[linkText](linkUrl) mixed</li></ul> |
       `;
 
-      const expected = source`
-        | thead |
-        | ----- |
-        | <ul><li>bullet</li></ul> |
-        | <ol><li>ordered</li></ol> |
-        | <ul><li>nested<ul><li>nested</li></ul></li></ul> |
-        | <ul><li>nested<ul><li>nested</li><li>nested</li></ul></li></ul> |
-        | <ol><li>mix<strong>ed</strong><ul><li><strong>mix</strong>ed</li></ul></li></ol> |
-        | <ol><li>mix<i>ed</i><ul><li><strong>mix</strong>ed</li></ul></li></ol> |
-        | foo<ul><li>bar</li></ul>baz |
-        | ![altText](imgUrl) **mixed**<ul><li>[linkText](linkUrl) mixed</li></ul> |
-      `;
-
-      assertConverting(markdown, `${expected}\n`);
+      assertConverting(markdown, `${markdown}\n`);
     });
 
     it('table with unmatched html list', () => {
@@ -1075,30 +1062,6 @@ describe('Convertor', () => {
     });
   });
 
-  it('should convert by using HTML tag when delimiter is not preceded an alphanumeric', () => {
-    const wwNodeJson = {
-      type: 'doc',
-      content: [
-        {
-          type: 'paragraph',
-          content: [
-            {
-              type: 'text',
-              marks: [{ type: 'strong' }],
-              text: '"test"',
-            },
-            { type: 'text', text: 'a' },
-          ],
-        },
-      ],
-    };
-    const wwNode = Node.fromJSON(schema, wwNodeJson);
-
-    const result = convertor.toMarkdownText(wwNode);
-
-    expect(result).toBe(`<strong>"test"</strong>a`);
-  });
-
   it('should convert empty line between lists of wysiwig to <br>', () => {
     const wwNodeJson = {
       type: 'doc',