diff --git a/packages/text-markdown/src/__tests__/markdown.test.ts b/packages/text-markdown/src/__tests__/markdown.test.ts
index 0e78d8b5e2..782f5da32a 100644
--- a/packages/text-markdown/src/__tests__/markdown.test.ts
+++ b/packages/text-markdown/src/__tests__/markdown.test.ts
@@ -36,8 +36,8 @@ expect.extend({
return {
message: () =>
pass
- ? `Expected markdown strings NOT to be equal:\n Received: ${received}\n Expected: ${expected}`
- : `Expected markdown strings to be equal:\n Received: ${received}\n Expected: ${expected}`,
+ ? `Expected markdown strings NOT to be equal:\n Received:\n${received}\n Expected:\n${expected}`
+ : `Expected markdown strings to be equal:\n Received:\n${received}\n Expected:\n${expected}`,
pass
}
}
@@ -1033,6 +1033,15 @@ describe('markdownToMarkup -> markupToMarkdown', () => {
name: 'Image',
markdown: '
'
},
+ {
+ name: 'Images',
+ markdown: `
+
+
+
+
+
`
+ },
{
name: 'Image with multiline alt',
markdown: ''
diff --git a/packages/text-markdown/src/compare.ts b/packages/text-markdown/src/compare.ts
index 40c2438345..136da3ea13 100644
--- a/packages/text-markdown/src/compare.ts
+++ b/packages/text-markdown/src/compare.ts
@@ -49,20 +49,71 @@ export function calcSørensenDiceCoefficient (a: string, b: string): number {
* Perform markdown diff/comparison to understand do we have a major differences.
*/
export function isMarkdownsEquals (source1: string, source2: string): boolean {
- const normalizeLineEndings = (str: string): string => str.replace(/\r?\n/g, '\n')
-
- const excludeBlankLines = (str: string): string =>
- str
- .split('\n')
- .map((it) => it.trimEnd())
- .filter((it) => it.length > 0)
- .join('\n')
-
- const norm1 = normalizeLineEndings(source1 ?? '')
- const lines1 = excludeBlankLines(norm1)
-
- const norm2 = normalizeLineEndings(source2 ?? '')
- const lines2 = excludeBlankLines(norm2)
-
- return lines1 === lines2
+ const normalized1 = normalizeMarkdown(source1)
+ const normalized2 = normalizeMarkdown(source2)
+ return normalized1 === normalized2
+}
+
+export function normalizeMarkdown (source: string): string {
+ const tagRegex = /<(\w+)([^>]*?)(\/?)>/g
+ const attrRegex = /(\w+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g
+
+ // Normalize line endings to LF
+ source = source.replace(/\r?\n/g, '\n')
+
+ // Remove extra blank lines
+ source = source
+ .split('\n')
+ .map((it) => it.trimEnd())
+ .filter((it) => it.length > 0)
+ .join('\n')
+
+ // Normalize HTML tags
+ source = source.replace(tagRegex, (match, tagName, attributes) => {
+ const attrs: Record = {}
+
+ let attrMatch = attrRegex.exec(attributes)
+ while (attrMatch !== null) {
+ const attrName = attrMatch[1]
+ const attrValue = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? ''
+ attrs[attrName] = attrValue
+ attrMatch = attrRegex.exec(attributes)
+ }
+
+ // Sort attributes by name for consistent order
+ const sortedAttrs = Object.keys(attrs)
+ .sort()
+ .map((key) => {
+ const value = attrs[key]
+ return value !== '' ? `${key}="${value}"` : key
+ })
+ .join(' ')
+
+ // Normalize to self-closing format for void elements
+ const voidElements = [
+ 'img',
+ 'br',
+ 'hr',
+ 'input',
+ 'meta',
+ 'area',
+ 'base',
+ 'col',
+ 'embed',
+ 'link',
+ 'param',
+ 'source',
+ 'track',
+ 'wbr'
+ ]
+ const isVoidElement = voidElements.includes(tagName.toLowerCase())
+
+ if (sortedAttrs !== '') {
+ return isVoidElement ? `<${tagName} ${sortedAttrs} />` : `<${tagName} ${sortedAttrs}>`
+ } else {
+ return isVoidElement ? `<${tagName} />` : `<${tagName}>`
+ }
+ })
+
+ return source
}
diff --git a/packages/text-markdown/src/serializer.ts b/packages/text-markdown/src/serializer.ts
index 4d13a7222c..ff9e326ac6 100644
--- a/packages/text-markdown/src/serializer.ts
+++ b/packages/text-markdown/src/serializer.ts
@@ -204,7 +204,6 @@ export const storeNodes: Record = {
)
} else {
if (attrs.width != null || attrs.height != null) {
- // state.write(`
`)
state.write(
'![]()