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 || ""}${tag}>`);
+ buf.write(`<${tag}${attr}>${cell || ''}${tag}>`);
}
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
*/