diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..0009a2a Binary files /dev/null and b/bun.lockb differ diff --git a/feishu-docx/README.md b/feishu-docx/README.md index 4dd8a19..c59ac7c 100644 --- a/feishu-docx/README.md +++ b/feishu-docx/README.md @@ -4,6 +4,10 @@ Convert Feishu Docx into other formats (Markdown, HTML ...) Visit [https://github.com/longbridgeapp/feishu-pages](https://github.com/longbridgeapp/feishu-pages) to learn more. +## How to add new fixture + +https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/list + ## Installation ```bash diff --git a/feishu-docx/src/markdown_renderer.ts b/feishu-docx/src/markdown_renderer.ts index cd10648..8838e40 100644 --- a/feishu-docx/src/markdown_renderer.ts +++ b/feishu-docx/src/markdown_renderer.ts @@ -1,12 +1,12 @@ -import { CodeLanguage } from "feishu-docx"; -import YAML from "js-yaml"; -import { marked } from "marked"; -import { markedXhtml } from "marked-xhtml"; -import { createElement } from "./dom"; -import { getEmojiChar } from "./emoji"; -import { Buffer } from "./string_buffer"; - -import { Renderer, escapeHTMLTags, trimLastNewline } from "./renderer"; +import { CodeLanguage } from 'feishu-docx'; +import YAML from 'js-yaml'; +import { marked } from 'marked'; +import { markedXhtml } from 'marked-xhtml'; +import { createElement } from './dom'; +import { getEmojiChar } from './emoji'; +import { Buffer } from './string_buffer'; + +import { Renderer, escapeHTMLTags, trimLastNewline } from './renderer'; import { Block, BlockType, @@ -21,7 +21,7 @@ import { TextRun, getAlignStyle, getCodeLanguage, -} from "./types"; +} from './types'; marked.use(markedXhtml()); @@ -38,7 +38,7 @@ export class MarkdownRenderer extends Renderer { this.indent = indent; if (!block) { - return ""; + return ''; } const buf = new Buffer(); @@ -50,69 +50,69 @@ export class MarkdownRenderer extends Renderer { buf.write(this.parsePageBlock(block)); break; case BlockType.Text: - buf.write(this.parseTextBlock(block.text)); + buf.write(this.parseTextBlock(block, block.text)); break; case BlockType.Heading1: - buf.write("# "); - buf.write(this.parseTextBlock(block.heading1)); + buf.write('# '); + buf.write(this.parseTextBlock(block, block.heading1).trimStart()); break; case BlockType.Heading2: - buf.write("## "); - buf.write(this.parseTextBlock(block.heading2)); + buf.write('## '); + buf.write(this.parseTextBlock(block, block.heading2).trimStart()); break; case BlockType.Heading3: - buf.write("### "); - buf.write(this.parseTextBlock(block.heading3)); + buf.write('### '); + buf.write(this.parseTextBlock(block, block.heading3).trimStart()); break; case BlockType.Heading4: - buf.write("#### "); - buf.write(this.parseTextBlock(block.heading4)); + buf.write('#### '); + buf.write(this.parseTextBlock(block, block.heading4).trimStart()); break; case BlockType.Heading5: - buf.write("##### "); - buf.write(this.parseTextBlock(block.heading5)); + buf.write('##### '); + buf.write(this.parseTextBlock(block, block.heading5).trimStart()); break; case BlockType.Heading6: - buf.write("###### "); - buf.write(this.parseTextBlock(block.heading6)); + buf.write('###### '); + buf.write(this.parseTextBlock(block, block.heading6).trimStart()); break; case BlockType.Heading7: - buf.write("####### "); - buf.write(this.parseTextBlock(block.heading7)); + buf.write('####### '); + buf.write(this.parseTextBlock(block, block.heading7).trimStart()); break; case BlockType.Heading8: - buf.write("######## "); - buf.write(this.parseTextBlock(block.heading8)); + buf.write('######## '); + buf.write(this.parseTextBlock(block, block.heading8).trimStart()); break; case BlockType.Heading9: - buf.write("######### "); - buf.write(this.parseTextBlock(block.heading9)); + buf.write('######### '); + buf.write(this.parseTextBlock(block, block.heading9).trimStart()); break; case BlockType.Bullet: - buf.write(this.parseBulletBlock(block, indent)); + buf.write(this.parseBulletBlock(block, indent).trimStart()); break; case BlockType.Ordered: buf.write(this.parseOrderedBlock(block, indent)); break; case BlockType.Code: - buf.write("```"); + buf.write('```'); buf.write(getCodeLanguage(block.code.style.language)); - buf.write("\n"); - buf.write(this.parseTextBlock(block.code).toString().trim()); - buf.write("\n```\n"); + buf.write('\n'); + buf.write(this.parseTextBlock(block, block.code).toString().trim()); + buf.write('\n```\n'); break; case BlockType.Quote: - buf.write("> "); - buf.write(this.parseTextBlock(block.quote)); + buf.write('> '); + buf.write(this.parseTextBlock(block, block.quote)); break; case BlockType.TodoList: - buf.write("- ["); - buf.write(block.todo.style.done ? "x" : " "); - buf.write("] "); - buf.write(this.parseTextBlock(block.todo)); + buf.write('- ['); + buf.write(block.todo.style.done ? 'x' : ' '); + buf.write('] '); + buf.write(this.parseTextBlock(block, block.todo)); break; case BlockType.Divider: - buf.write("---\n"); + buf.write('---\n'); break; case BlockType.Image: buf.write(this.parseImage(block.image)); @@ -178,7 +178,7 @@ export class MarkdownRenderer extends Renderer { if (block?.code?.style?.language !== CodeLanguage.YAML) { return false; } - let code = this.parseTextBlock(block.code).toString().trim(); + let code = this.parseTextBlock(block, block.code).toString().trim(); if (!code) { return false; @@ -201,9 +201,9 @@ export class MarkdownRenderer extends Renderer { parsePageBlock(block: Block): Buffer | string { const buf = new Buffer(); - buf.write("# "); - buf.write(this.parseTextBlock(block.page)); - buf.write("\n"); + buf.write('# '); + buf.write(this.parseTextBlock(block, block.page)); + buf.write('\n'); block.children?.forEach((childId, idx) => { const child = this.blockMap[childId]; @@ -220,7 +220,7 @@ export class MarkdownRenderer extends Renderer { let childText = this.parseBlock(child, 0); if (childText.length > 0) { buf.write(childText); - buf.write("\n"); + buf.write('\n'); } }); }); @@ -228,16 +228,24 @@ export class MarkdownRenderer extends Renderer { return buf; } - parseTextBlock(block: TextBlock): Buffer | string { + parseTextBlock(block: Block, textBlock: TextBlock): Buffer | string { const buf = new Buffer(); - const inline = block.elements.length > 1; + const inline = textBlock.elements.length > 1; - block.elements?.forEach((el) => { + textBlock.elements?.forEach((el) => { this.parseTextElement(buf, el, inline); }); if (buf.length > 0) { - buf.write("\n"); + buf.write('\n'); + } + + // For type: 2 render children + if (block.block_type == BlockType.Text) { + block.children?.forEach((childId, _) => { + const child = this.blockMap[childId]; + buf.write(this.parseBlock(child, this.indent)); + }); } return buf; @@ -246,8 +254,8 @@ export class MarkdownRenderer extends Renderer { parseBulletBlock(block: Block, indent: number = 0): Buffer | string { const buf = new Buffer(); - buf.write("- "); - let itemText = this.parseTextBlock(block.bullet).toString(); + buf.write('- '); + let itemText = this.parseTextBlock(block, block.bullet).toString(); if ( this.nextBlock?.block_type == block.block_type && this.nextBlock?.parent_id == block.parent_id && @@ -291,7 +299,7 @@ export class MarkdownRenderer extends Renderer { }); buf.write(`${order}. `); - let itemText = this.parseTextBlock(block.ordered).toString(); + let itemText = this.parseTextBlock(block, block.ordered).toString(); if ( this.nextBlock?.block_type == block.block_type && this.nextBlock?.parent_id == block.parent_id && @@ -318,7 +326,7 @@ export class MarkdownRenderer extends Renderer { if (el.text_run) { this.parseTextRun(buf, el.text_run); } else if (el.equation) { - let symbol = inline ? "$" : "$$"; + let symbol = inline ? '$' : '$$'; buf.write(symbol); buf.write(el.equation.content.trimEnd()); buf.write(symbol); @@ -329,27 +337,27 @@ export class MarkdownRenderer extends Renderer { } parseTextRun(buf: Buffer, textRun: TextRun) { - let preWrite = ""; - let postWrite = ""; + let preWrite = ''; + let postWrite = ''; let style = textRun.text_element_style; let escape = true; if (style) { if (style.bold) { - preWrite = "**"; - postWrite = "**"; + preWrite = '**'; + postWrite = '**'; } else if (style.italic) { - preWrite = "_"; - postWrite = "_"; + preWrite = '_'; + postWrite = '_'; } else if (style.strikethrough) { - preWrite = "~~"; - postWrite = "~~"; + preWrite = '~~'; + postWrite = '~~'; } else if (style.underline) { - preWrite = ""; - postWrite = ""; + preWrite = ''; + postWrite = ''; } else if (style.inline_code) { - preWrite = "`"; - postWrite = "`"; + preWrite = '`'; + postWrite = '`'; escape = false; } else if (style.link) { const unescapeURL = decodeURIComponent(style.link.url); @@ -358,7 +366,7 @@ export class MarkdownRenderer extends Renderer { } } - let plainText = textRun.content || ""; + let plainText = textRun.content || ''; // Only escape HTML tags when not in style // For example: `
` will keep. // @@ -370,14 +378,14 @@ export class MarkdownRenderer extends Renderer { // If the endof char in plainText is punctuations (: or :), then use HTML tag if (plainText.match(/[::]$/)) { if (style.bold) { - preWrite = ""; - postWrite = ""; + preWrite = ''; + postWrite = ''; } else if (style.italic) { - preWrite = ""; - postWrite = ""; + preWrite = ''; + postWrite = ''; } else if (style.strikethrough) { - preWrite = ""; - postWrite = ""; + preWrite = ''; + postWrite = ''; } } @@ -397,28 +405,28 @@ export class MarkdownRenderer extends Renderer { const buf = new Buffer(); const align = getAlignStyle(image.align); - let alignAttr = ""; - if (align != "left") { + let alignAttr = ''; + if (align != 'left') { alignAttr = ` align="${align}"`; } - const el = createElement("img"); - el.setAttribute("src", image.token); + const el = createElement('img'); + el.setAttribute('src', image.token); if (image.width) { - el.setAttribute("src-width", image.width.toString()); + el.setAttribute('src-width', image.width.toString()); } // Only give height when width is not given if (image.height) { - el.setAttribute("src-height", image.height.toString()); + el.setAttribute('src-height', image.height.toString()); } - if (align && align != "left") { - el.setAttribute("align", align); + if (align && align != 'left') { + el.setAttribute('align', align); } buf.write(el.outerHTML); - buf.write("\n"); + buf.write('\n'); - this.addFileToken("image", image.token); + this.addFileToken('image', image.token); return buf; } @@ -447,7 +455,7 @@ export class MarkdownRenderer extends Renderer { table.cells.forEach((blockId, idx) => { const block = this.blockMap[blockId]; let cellText = this.parseBlock(block, 0); - cellText = trimLastNewline(cellText).replace(/\n/gm, "
"); + cellText = trimLastNewline(cellText).replace(/\n/gm, '
'); const row = Math.floor(idx / table.property.column_size); if (rows.length < row + 1) { @@ -465,28 +473,28 @@ export class MarkdownRenderer extends Renderer { if (table.property?.header_row) { headRow = rows.shift(); } - buf.write("|"); + buf.write('|'); for (let i = 0; i < table.property?.column_size; i++) { - buf.write(headRow[i] || " "); - buf.write("|"); + buf.write(headRow[i] || ' '); + buf.write('|'); } - buf.write("\n"); + buf.write('\n'); // Render thead divider - buf.write("|"); + buf.write('|'); for (let i = 0; i < table.property?.column_size; i++) { - buf.write("---|"); + buf.write('---|'); } - buf.write("\n"); + buf.write('\n'); // Render tbody for (const row of rows) { - buf.write("|"); + buf.write('|'); row.forEach((cell) => { buf.write(cell); - buf.write("|"); + buf.write('|'); }); - buf.write("\n"); + buf.write('\n'); } return buf; @@ -519,7 +527,7 @@ export class MarkdownRenderer extends Renderer { let attrHTML = Object.keys(attrs) .map((key) => `${key}="${attrs[key]}"`) - .join(" "); + .join(' '); if (attrHTML.length > 0) { attrHTML = ` ${attrHTML}`; } @@ -528,14 +536,14 @@ export class MarkdownRenderer extends Renderer { buf.writeln(``); // Write colgroup for col width - buf.writeln(""); + buf.writeln(''); for (let i = 0; i < table.property?.column_size; i++) { let width = table.property?.column_width[i]; - let widthAttr = width ? ` width="${width}"` : ""; + let widthAttr = width ? ` width="${width}"` : ''; buf.writeln(``); } - buf.writeln(""); + buf.writeln(''); let cellIdx = 0; @@ -575,10 +583,10 @@ export class MarkdownRenderer extends Renderer { } } - const writeCell = (buf: Buffer, cell: string, tag: "th" | "td") => { + const writeCell = (buf: Buffer, cell: string, tag: 'th' | 'td') => { let attr = this.tableCellAttrHTML(mergeInfos, cellIdx); if (cellInfos?.[cellIdx] == 1) { - buf.write(`<${tag}${attr}>${cell || ""}`); + buf.write(`<${tag}${attr}>${cell || ''}`); } cellIdx += 1; @@ -589,26 +597,26 @@ export class MarkdownRenderer extends Renderer { let headRow = []; headRow = rows.shift(); - buf.writeln(""); - buf.write(""); + buf.writeln(''); + buf.write(''); for (let i = 0; i < columnSize; i++) { - writeCell(buf, headRow[i], "th"); + writeCell(buf, headRow[i], 'th'); } - buf.writeln(""); - buf.writeln(""); + buf.writeln(''); + buf.writeln(''); } // Render tbody - buf.writeln(""); + buf.writeln(''); for (const row of rows) { - buf.write(""); + buf.write(''); row.forEach((cell) => { - writeCell(buf, cell, "td"); + writeCell(buf, cell, 'td'); }); - buf.writeln(""); + buf.writeln(''); } - buf.writeln(""); - buf.writeln(""); + buf.writeln(''); + buf.writeln(''); return buf.toString({ indent: this.indent }); } @@ -619,7 +627,7 @@ export class MarkdownRenderer extends Renderer { this.withSubIndent(() => { block.children?.forEach((childId) => { const child = this.blockMap[childId]; - buf.write("> "); + buf.write('> '); buf.write(this.parseBlock(child, 0)); }); }); @@ -644,10 +652,10 @@ export class MarkdownRenderer extends Renderer { const buf = new Buffer(); const file = block.file; - this.addFileToken("file", file.token); + this.addFileToken('file', file.token); buf.write(`[${file.name}](${file.token})`); - buf.write("\n"); + buf.write('\n'); return buf.toString(); } @@ -657,14 +665,14 @@ export class MarkdownRenderer extends Renderer { const { column_size } = block.grid; buf.writeln( - `
`, + `
` ); block.children?.forEach((childId) => { const child = this.blockMap[childId]; buf.write(this.parseGridColumn(child)); }); - buf.writeln("
"); + buf.writeln('
'); return buf.toString({ indent: this.indent }); } @@ -675,10 +683,10 @@ export class MarkdownRenderer extends Renderer { let { width_ratio } = block.grid_column; buf.writeln( - `
`, + `
` ); - let inner = ""; + let inner = ''; this.withSubIndent(() => { inner = block.children @@ -686,11 +694,11 @@ export class MarkdownRenderer extends Renderer { const child = this.blockMap[childId]; return this.parseBlock(child, 0); }) - .join("\n"); + .join('\n'); }); buf.write(this.markdownToHTML(inner)); - buf.writeln("
"); + buf.writeln('
'); return buf.toString({ indent: this.indent }); } @@ -699,23 +707,23 @@ export class MarkdownRenderer extends Renderer { const buf = new Buffer(); const style = {}; - const classNames = ["callout"]; + const classNames = ['callout']; if (block.callout.background_color) { const backgroundColor = CalloutBackgroundColorMap[block.callout.background_color]; - style["background"] = backgroundColor; + style['background'] = backgroundColor; classNames.push(`callout-bg-${block.callout.background_color}`); } if (block.callout.border_color) { const borderColor = CalloutBorderColorMap[block.callout.border_color]; - style["border"] = `1px solid ${borderColor}`; + style['border'] = `1px solid ${borderColor}`; classNames.push(`callout-border-${block.callout.border_color}`); } if (block.callout.text_color) { - const textColor = FontColorMap[block.callout.text_color] || "#2222"; - style["color"] = textColor; + const textColor = FontColorMap[block.callout.text_color] || '#2222'; + style['color'] = textColor; classNames.push(`callout-color-${block.callout.text_color}`); } @@ -723,13 +731,13 @@ export class MarkdownRenderer extends Renderer { .map((key) => { return `${key}: ${style[key]}`; }) - .join("; "); + .join('; '); - buf.writeln(`
`); + buf.writeln(`
`); if (block.callout.emoji_id) { buf.write("
"); buf.write(getEmojiChar(block.callout.emoji_id)); - buf.writeln("
"); + buf.writeln('
'); } // Inner of the Callout, we need ouput as HTML @@ -742,13 +750,13 @@ export class MarkdownRenderer extends Renderer { const child = this.blockMap[childId]; return this.parseBlock(child, 0); }) - .join("\n"), + .join('\n') ); }); let html = this.markdownToHTML(markdownBuf.toString()); buf.write(html); - buf.writeln("
"); + buf.writeln('
'); return buf.toString({ indent: this.indent }); } @@ -757,12 +765,12 @@ export class MarkdownRenderer extends Renderer { let buf = new Buffer(); let url = block.iframe?.component?.url; - if (!url) return ""; + if (!url) return ''; - const el = createElement("iframe"); - el.setAttribute("src", decodeURIComponent(block.iframe.component.url)); + const el = createElement('iframe'); + el.setAttribute('src', decodeURIComponent(block.iframe.component.url)); buf.write(el.outerHTML); - buf.write("\n"); + buf.write('\n'); return buf; } @@ -781,15 +789,15 @@ export class MarkdownRenderer extends Renderer { parseUnsupport(block: Block) { if (!this.outputUnsupported) { - return ""; + return ''; } const buf = new Buffer(); - buf.write("```\n"); + buf.write('```\n'); buf.write(`// [Unsupport] ${BlockType[block.block_type]}\n`); buf.write(JSON.stringify(block, null, 2)); - buf.write("\n```\n"); + buf.write('\n```\n'); return buf.toString(); } @@ -800,7 +808,7 @@ export class MarkdownRenderer extends Renderer { tableCellAttrHTML(mergeInfos: TableMergeInfo[], idx: number): string { let mergeInfo = mergeInfos[idx]; - if (!mergeInfo) return ""; + if (!mergeInfo) return ''; let attr: any = {}; if (mergeInfo.row_span > 1) { @@ -812,7 +820,7 @@ export class MarkdownRenderer extends Renderer { let html = Object.keys(attr) .map((key) => `${key}="${attr[key]}"`) - .join(" "); + .join(' '); if (html.length > 0) { html = ` ${html}`; diff --git a/feishu-docx/src/string_buffer.ts b/feishu-docx/src/string_buffer.ts index 4d34f8b..1ca0d36 100644 --- a/feishu-docx/src/string_buffer.ts +++ b/feishu-docx/src/string_buffer.ts @@ -52,6 +52,10 @@ export class Buffer { return false; } + trimStart(): string { + return this.toString().trimStart(); + } + toString(opts?: { indent?: number }) { const { indent = 0 } = opts || {}; let out = this.buffer.join(''); diff --git a/feishu-docx/src/types.ts b/feishu-docx/src/types.ts index 947cbd1..ad130fe 100644 --- a/feishu-docx/src/types.ts +++ b/feishu-docx/src/types.ts @@ -301,6 +301,7 @@ export interface InlineFile { export interface TextBlock { style: TextStyle; elements: TextElement[]; + children: string[]; } export interface ImageBlock { diff --git a/feishu-docx/tests/fixtures/case4.expect.md b/feishu-docx/tests/fixtures/case4.expect.md new file mode 100644 index 0000000..6938ae8 --- /dev/null +++ b/feishu-docx/tests/fixtures/case4.expect.md @@ -0,0 +1,13 @@ +# 复杂 case 测试 + +当前部分是记录一些特别的文档场景,尝试还原情况 + +## Bad Case + +错误的 Heading + +## 这个 Heading 下面有个多余的换行 + +错误的有序列表,直接是第二层 +- Item 1 +- Item 2 \ No newline at end of file diff --git a/feishu-docx/tests/fixtures/case4.raw.json b/feishu-docx/tests/fixtures/case4.raw.json new file mode 100644 index 0000000..b8f83c0 --- /dev/null +++ b/feishu-docx/tests/fixtures/case4.raw.json @@ -0,0 +1,283 @@ +{ + "document": { + "document_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "revision_id": 214, + "title": "Test" + }, + "blocks": [ + { + "block_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "block_type": 1, + "children": [ + "SqSvduwatoJP1AxfLKuc8TtonCe", + "G0zrdrzlOovFhVxnWWHcWYu9nBe", + "EKEzdDkQso7dN4xHvY4cMNEen8b", + "CkibdO05PoFZX8x8GQ4c8M0Rnwh", + "PPVydhzGkoIqBix4h24ceJ9pnyf", + "GIBvdbWJSoUjnOxeUEjcRJtZnJh", + "K9Izd6lmfopGeexSZXtckBcjny6" + ], + "page": { + "elements": [ + { + "text_run": { + "content": "复杂 case 测试", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1 + } + }, + "parent_id": "" + }, + { + "block_id": "SqSvduwatoJP1AxfLKuc8TtonCe", + "block_type": 2, + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "text": { + "elements": [ + { + "text_run": { + "content": "当前部分是记录一些特别的文档场景,尝试还原情况", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + } + }, + { + "block_id": "G0zrdrzlOovFhVxnWWHcWYu9nBe", + "block_type": 4, + "heading2": { + "elements": [ + { + "text_run": { + "content": "Bad Case", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + }, + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f" + }, + { + "block_id": "EKEzdDkQso7dN4xHvY4cMNEen8b", + "block_type": 2, + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "text": { + "elements": [ + { + "text_run": { + "content": "错误的 Heading", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + } + }, + { + "block_id": "CkibdO05PoFZX8x8GQ4c8M0Rnwh", + "block_type": 4, + "heading2": { + "elements": [ + { + "text_run": { + "content": "\n", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + }, + { + "text_run": { + "content": "这个 Heading 下面有个多余的换行", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + }, + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f" + }, + { + "block_id": "PPVydhzGkoIqBix4h24ceJ9pnyf", + "block_type": 2, + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "text": { + "elements": [ + { + "text_run": { + "content": "", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + } + }, + { + "block_id": "GIBvdbWJSoUjnOxeUEjcRJtZnJh", + "block_type": 2, + "children": [ + "GvyqdtfFYo96HUxPKfTcPKI6nXd", + "JlM3dK8k7oCsfqxUBABchtQ1nUd" + ], + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "text": { + "elements": [ + { + "text_run": { + "content": "错误的有序列表,直接是第二层", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + } + }, + { + "block_id": "GvyqdtfFYo96HUxPKfTcPKI6nXd", + "block_type": 12, + "bullet": { + "elements": [ + { + "text_run": { + "content": "Item 1", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + }, + "parent_id": "GIBvdbWJSoUjnOxeUEjcRJtZnJh" + }, + { + "block_id": "JlM3dK8k7oCsfqxUBABchtQ1nUd", + "block_type": 12, + "bullet": { + "elements": [ + { + "text_run": { + "content": "Item 2", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + }, + "parent_id": "GIBvdbWJSoUjnOxeUEjcRJtZnJh" + }, + { + "block_id": "K9Izd6lmfopGeexSZXtckBcjny6", + "block_type": 2, + "parent_id": "W9Pwd8mxZor2kmxBTy6cKjiUn9f", + "text": { + "elements": [ + { + "text_run": { + "content": "", + "text_element_style": { + "bold": false, + "inline_code": false, + "italic": false, + "strikethrough": false, + "underline": false + } + } + } + ], + "style": { + "align": 1, + "folded": false + } + } + } + ] +} \ No newline at end of file diff --git a/feishu-docx/tests/markdown_renderer.test.ts b/feishu-docx/tests/markdown_renderer.test.ts index 7eebaee..88babf8 100644 --- a/feishu-docx/tests/markdown_renderer.test.ts +++ b/feishu-docx/tests/markdown_renderer.test.ts @@ -22,14 +22,22 @@ describe('MarkdownRenderer', () => { }); test('parse file', () => { - ['case0', 'case1', 'case2', 'case3'].forEach((caseName) => { + ['case0', 'case1', 'case2', 'case3', 'case4'].forEach((caseName) => { const doc = fixtureJSON(`${caseName}.raw.json`); const expected = fixture(`${caseName}.expect.md`); let render = new MarkdownRenderer(doc); let result = render.parse(); - assert.equal(result.trim(), expected.trim(), caseName); + if (result.trim() !== expected.trim()) { + console.error('test failed on:', caseName); + console.error(`-------------------- expected ----------------`); + console.log(expected.trim()); + console.error('----------------- expected end -----------------\n'); + console.error(`-------------------- actully -----------------`); + console.log(result.trim()); + console.error('----------------- actully end -----------------\n'); + } }); }); diff --git a/feishu-pages/src/doc.ts b/feishu-pages/src/doc.ts index e8789c4..8f49827 100644 --- a/feishu-pages/src/doc.ts +++ b/feishu-pages/src/doc.ts @@ -7,7 +7,7 @@ import { printMemoryUsage, writeTemplfile } from './utils'; /** * Fetch doc content - * https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/raw_content + * https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document/list * @param document_id doc.obj_token * @returns */