@bendyline/squisq-formats 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-532L4D5D.js +21 -0
- package/dist/chunk-532L4D5D.js.map +1 -0
- package/dist/chunk-67KIJHV2.js +21 -0
- package/dist/chunk-67KIJHV2.js.map +1 -0
- package/dist/chunk-KAK4V57E.js +387 -0
- package/dist/chunk-KAK4V57E.js.map +1 -0
- package/dist/chunk-MQHCXI56.js +1035 -0
- package/dist/chunk-MQHCXI56.js.map +1 -0
- package/dist/chunk-TBPD5PCU.js +1136 -0
- package/dist/chunk-TBPD5PCU.js.map +1 -0
- package/dist/chunk-ULLIPBEJ.js +271 -0
- package/dist/chunk-ULLIPBEJ.js.map +1 -0
- package/dist/docx/index.d.ts +108 -0
- package/dist/docx/index.js +14 -0
- package/dist/docx/index.js.map +1 -0
- package/dist/html/index.d.ts +166 -0
- package/dist/html/index.js +17 -0
- package/dist/html/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/ooxml/index.d.ts +351 -0
- package/dist/ooxml/index.js +99 -0
- package/dist/ooxml/index.js.map +1 -0
- package/dist/pdf/index.d.ts +130 -0
- package/dist/pdf/index.js +15 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/pptx/index.d.ts +58 -0
- package/dist/pptx/index.js +13 -0
- package/dist/pptx/index.js.map +1 -0
- package/dist/xlsx/index.d.ts +58 -0
- package/dist/xlsx/index.js +13 -0
- package/dist/xlsx/index.js.map +1 -0
- package/package.json +85 -0
- package/src/__tests__/docxExport.test.ts +457 -0
- package/src/__tests__/docxImport.test.ts +410 -0
- package/src/__tests__/html.test.ts +295 -0
- package/src/__tests__/ooxml.test.ts +197 -0
- package/src/__tests__/pdfExport.test.ts +322 -0
- package/src/__tests__/pdfImport.test.ts +290 -0
- package/src/__tests__/roundTrip.test.ts +201 -0
- package/src/docx/export.ts +978 -0
- package/src/docx/import.ts +909 -0
- package/src/docx/index.ts +26 -0
- package/src/docx/styles.ts +145 -0
- package/src/html/htmlTemplate.ts +307 -0
- package/src/html/imageUtils.ts +66 -0
- package/src/html/index.ts +168 -0
- package/src/index.ts +51 -0
- package/src/ooxml/index.ts +87 -0
- package/src/ooxml/namespaces.ts +160 -0
- package/src/ooxml/reader.ts +218 -0
- package/src/ooxml/types.ts +104 -0
- package/src/ooxml/writer.ts +321 -0
- package/src/ooxml/xmlUtils.ts +123 -0
- package/src/pdf/export.ts +1029 -0
- package/src/pdf/import.ts +835 -0
- package/src/pdf/index.ts +29 -0
- package/src/pdf/styles.ts +180 -0
- package/src/pptx/index.ts +78 -0
- package/src/xlsx/index.ts +78 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Round-trip tests: MarkdownDocument → DOCX → MarkdownDocument.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that core document structures survive the export → import cycle.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import type {
|
|
9
|
+
MarkdownDocument,
|
|
10
|
+
MarkdownHeading,
|
|
11
|
+
MarkdownParagraph,
|
|
12
|
+
MarkdownStrong,
|
|
13
|
+
MarkdownEmphasis,
|
|
14
|
+
MarkdownInlineNode,
|
|
15
|
+
MarkdownBlockNode,
|
|
16
|
+
} from '@bendyline/squisq/markdown';
|
|
17
|
+
|
|
18
|
+
import { markdownDocToDocx } from '../docx/export';
|
|
19
|
+
import { docxToMarkdownDoc } from '../docx/import';
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// Helpers
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
async function roundTrip(doc: MarkdownDocument): Promise<MarkdownDocument> {
|
|
26
|
+
const buffer = await markdownDocToDocx(doc);
|
|
27
|
+
return docxToMarkdownDoc(buffer);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extract plain text from an inline node tree.
|
|
32
|
+
*/
|
|
33
|
+
function extractText(node: MarkdownInlineNode): string {
|
|
34
|
+
if (node.type === 'text') return node.value;
|
|
35
|
+
if ('children' in node) return (node.children as MarkdownInlineNode[]).map(extractText).join('');
|
|
36
|
+
if ('value' in node) return node.value as string;
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function extractBlockText(block: MarkdownBlockNode): string {
|
|
41
|
+
if ('children' in block)
|
|
42
|
+
return (block.children as MarkdownInlineNode[]).map(extractText).join('');
|
|
43
|
+
if ('value' in block) return block.value as string;
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================
|
|
48
|
+
// Round-trip Tests
|
|
49
|
+
// ============================================
|
|
50
|
+
|
|
51
|
+
describe('DOCX round-trip', () => {
|
|
52
|
+
it('round-trips headings', async () => {
|
|
53
|
+
const original: MarkdownDocument = {
|
|
54
|
+
type: 'document',
|
|
55
|
+
children: [
|
|
56
|
+
{
|
|
57
|
+
type: 'heading',
|
|
58
|
+
depth: 1,
|
|
59
|
+
children: [{ type: 'text', value: 'Main Title' }],
|
|
60
|
+
} satisfies MarkdownHeading,
|
|
61
|
+
{
|
|
62
|
+
type: 'heading',
|
|
63
|
+
depth: 2,
|
|
64
|
+
children: [{ type: 'text', value: 'Section' }],
|
|
65
|
+
} satisfies MarkdownHeading,
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const result = await roundTrip(original);
|
|
70
|
+
|
|
71
|
+
expect(result.children.length).toBeGreaterThanOrEqual(2);
|
|
72
|
+
expect(result.children[0].type).toBe('heading');
|
|
73
|
+
expect((result.children[0] as MarkdownHeading).depth).toBe(1);
|
|
74
|
+
expect(extractBlockText(result.children[0])).toBe('Main Title');
|
|
75
|
+
|
|
76
|
+
expect(result.children[1].type).toBe('heading');
|
|
77
|
+
expect((result.children[1] as MarkdownHeading).depth).toBe(2);
|
|
78
|
+
expect(extractBlockText(result.children[1])).toBe('Section');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('round-trips paragraphs', async () => {
|
|
82
|
+
const original: MarkdownDocument = {
|
|
83
|
+
type: 'document',
|
|
84
|
+
children: [
|
|
85
|
+
{
|
|
86
|
+
type: 'paragraph',
|
|
87
|
+
children: [{ type: 'text', value: 'Hello world' }],
|
|
88
|
+
} satisfies MarkdownParagraph,
|
|
89
|
+
{
|
|
90
|
+
type: 'paragraph',
|
|
91
|
+
children: [{ type: 'text', value: 'Second paragraph' }],
|
|
92
|
+
} satisfies MarkdownParagraph,
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const result = await roundTrip(original);
|
|
97
|
+
|
|
98
|
+
expect(result.children.length).toBe(2);
|
|
99
|
+
expect(extractBlockText(result.children[0])).toBe('Hello world');
|
|
100
|
+
expect(extractBlockText(result.children[1])).toBe('Second paragraph');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('round-trips bold text', async () => {
|
|
104
|
+
const original: MarkdownDocument = {
|
|
105
|
+
type: 'document',
|
|
106
|
+
children: [
|
|
107
|
+
{
|
|
108
|
+
type: 'paragraph',
|
|
109
|
+
children: [
|
|
110
|
+
{ type: 'text', value: 'Normal ' },
|
|
111
|
+
{
|
|
112
|
+
type: 'strong',
|
|
113
|
+
children: [{ type: 'text', value: 'bold' }],
|
|
114
|
+
} satisfies MarkdownStrong,
|
|
115
|
+
{ type: 'text', value: ' text' },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const result = await roundTrip(original);
|
|
122
|
+
const para = result.children[0] as MarkdownParagraph;
|
|
123
|
+
expect(para.type).toBe('paragraph');
|
|
124
|
+
|
|
125
|
+
// Find the bold node
|
|
126
|
+
const boldNode = para.children.find((c: MarkdownInlineNode) => c.type === 'strong');
|
|
127
|
+
expect(boldNode).toBeDefined();
|
|
128
|
+
expect(extractText(boldNode!)).toBe('bold');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('round-trips italic text', async () => {
|
|
132
|
+
const original: MarkdownDocument = {
|
|
133
|
+
type: 'document',
|
|
134
|
+
children: [
|
|
135
|
+
{
|
|
136
|
+
type: 'paragraph',
|
|
137
|
+
children: [
|
|
138
|
+
{
|
|
139
|
+
type: 'emphasis',
|
|
140
|
+
children: [{ type: 'text', value: 'italic' }],
|
|
141
|
+
} satisfies MarkdownEmphasis,
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const result = await roundTrip(original);
|
|
148
|
+
const para = result.children[0] as MarkdownParagraph;
|
|
149
|
+
const italicNode = para.children.find((c: MarkdownInlineNode) => c.type === 'emphasis');
|
|
150
|
+
expect(italicNode).toBeDefined();
|
|
151
|
+
expect(extractText(italicNode!)).toBe('italic');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('round-trips mixed content document', async () => {
|
|
155
|
+
const original: MarkdownDocument = {
|
|
156
|
+
type: 'document',
|
|
157
|
+
children: [
|
|
158
|
+
{
|
|
159
|
+
type: 'heading',
|
|
160
|
+
depth: 1,
|
|
161
|
+
children: [{ type: 'text', value: 'Document Title' }],
|
|
162
|
+
} satisfies MarkdownHeading,
|
|
163
|
+
{
|
|
164
|
+
type: 'paragraph',
|
|
165
|
+
children: [
|
|
166
|
+
{ type: 'text', value: 'This is ' },
|
|
167
|
+
{
|
|
168
|
+
type: 'strong',
|
|
169
|
+
children: [{ type: 'text', value: 'important' }],
|
|
170
|
+
},
|
|
171
|
+
{ type: 'text', value: ' content.' },
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: 'heading',
|
|
176
|
+
depth: 2,
|
|
177
|
+
children: [{ type: 'text', value: 'Details' }],
|
|
178
|
+
} satisfies MarkdownHeading,
|
|
179
|
+
{
|
|
180
|
+
type: 'paragraph',
|
|
181
|
+
children: [{ type: 'text', value: 'More info here.' }],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const result = await roundTrip(original);
|
|
187
|
+
|
|
188
|
+
// Should have at least 4 blocks
|
|
189
|
+
expect(result.children.length).toBeGreaterThanOrEqual(4);
|
|
190
|
+
|
|
191
|
+
// First should be heading
|
|
192
|
+
expect(result.children[0].type).toBe('heading');
|
|
193
|
+
expect(extractBlockText(result.children[0])).toBe('Document Title');
|
|
194
|
+
|
|
195
|
+
// Then paragraph with mixed content
|
|
196
|
+
expect(result.children[1].type).toBe('paragraph');
|
|
197
|
+
const fullText = (result.children[1] as MarkdownParagraph).children.map(extractText).join('');
|
|
198
|
+
expect(fullText).toContain('important');
|
|
199
|
+
expect(fullText).toContain('content');
|
|
200
|
+
});
|
|
201
|
+
});
|