@atikk-co-jp/notion-mcp-server 0.1.0 → 0.2.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/README.ja.md +95 -4
- package/README.md +95 -4
- package/dist/bin/cli.js +10 -10
- package/dist/src/converters/__tests__/block-to-markdown.test.d.ts +2 -0
- package/dist/src/converters/__tests__/block-to-markdown.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/block-to-markdown.test.js +611 -0
- package/dist/src/converters/__tests__/page-to-markdown.test.d.ts +2 -0
- package/dist/src/converters/__tests__/page-to-markdown.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/page-to-markdown.test.js +567 -0
- package/dist/src/converters/__tests__/rich-text-to-markdown.test.d.ts +2 -0
- package/dist/src/converters/__tests__/rich-text-to-markdown.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/rich-text-to-markdown.test.js +353 -0
- package/dist/src/converters/block-to-markdown.d.ts +38 -0
- package/dist/src/converters/block-to-markdown.d.ts.map +1 -0
- package/dist/src/converters/block-to-markdown.js +484 -0
- package/dist/src/converters/index.d.ts +9 -0
- package/dist/src/converters/index.d.ts.map +1 -0
- package/dist/src/converters/index.js +11 -0
- package/dist/src/converters/page-to-markdown.d.ts +64 -0
- package/dist/src/converters/page-to-markdown.d.ts.map +1 -0
- package/dist/src/converters/page-to-markdown.js +189 -0
- package/dist/src/converters/rich-text-to-markdown.d.ts +61 -0
- package/dist/src/converters/rich-text-to-markdown.d.ts.map +1 -0
- package/dist/src/converters/rich-text-to-markdown.js +95 -0
- package/dist/src/index.d.ts +6 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -5
- package/dist/src/notion-client.d.ts +58 -15
- package/dist/src/notion-client.d.ts.map +1 -1
- package/dist/src/notion-client.js +23 -13
- package/dist/src/schemas/block.d.ts +2158 -622
- package/dist/src/schemas/block.d.ts.map +1 -1
- package/dist/src/schemas/block.js +283 -76
- package/dist/src/schemas/common.d.ts +518 -6
- package/dist/src/schemas/common.d.ts.map +1 -1
- package/dist/src/schemas/common.js +120 -42
- package/dist/src/schemas/database.d.ts +687 -0
- package/dist/src/schemas/database.d.ts.map +1 -0
- package/dist/src/schemas/database.js +264 -0
- package/dist/src/schemas/filter.d.ts +509 -3
- package/dist/src/schemas/filter.d.ts.map +1 -1
- package/dist/src/schemas/filter.js +110 -13
- package/dist/src/schemas/index.d.ts +5 -4
- package/dist/src/schemas/index.d.ts.map +1 -1
- package/dist/src/schemas/index.js +7 -5
- package/dist/src/schemas/page.d.ts +2152 -19
- package/dist/src/schemas/page.d.ts.map +1 -1
- package/dist/src/schemas/page.js +216 -22
- package/dist/src/schemas/schemas.test.d.ts +2 -0
- package/dist/src/schemas/schemas.test.d.ts.map +1 -0
- package/dist/src/schemas/schemas.test.js +418 -0
- package/dist/src/server.d.ts +2 -2
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +6 -6
- package/dist/src/tools/append-block-children.d.ts +2 -2
- package/dist/src/tools/append-block-children.d.ts.map +1 -1
- package/dist/src/tools/append-block-children.js +16 -10
- package/dist/src/tools/create-comment.d.ts +2 -2
- package/dist/src/tools/create-comment.d.ts.map +1 -1
- package/dist/src/tools/create-comment.js +15 -9
- package/dist/src/tools/create-database.d.ts +4 -0
- package/dist/src/tools/create-database.d.ts.map +1 -0
- package/dist/src/tools/create-database.js +57 -0
- package/dist/src/tools/create-page.d.ts +2 -2
- package/dist/src/tools/create-page.d.ts.map +1 -1
- package/dist/src/tools/create-page.js +22 -24
- package/dist/src/tools/get-block-children.d.ts +2 -2
- package/dist/src/tools/get-block-children.d.ts.map +1 -1
- package/dist/src/tools/get-block-children.js +39 -6
- package/dist/src/tools/index.d.ts +13 -11
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +13 -9
- package/dist/src/tools/query-database.d.ts +2 -2
- package/dist/src/tools/query-database.d.ts.map +1 -1
- package/dist/src/tools/query-database.js +27 -18
- package/dist/src/tools/retrieve-page.d.ts +2 -2
- package/dist/src/tools/retrieve-page.d.ts.map +1 -1
- package/dist/src/tools/retrieve-page.js +44 -4
- package/dist/src/tools/search.d.ts +2 -2
- package/dist/src/tools/search.d.ts.map +1 -1
- package/dist/src/tools/search.js +18 -12
- package/dist/src/tools/update-database.d.ts +4 -0
- package/dist/src/tools/update-database.d.ts.map +1 -0
- package/dist/src/tools/update-database.js +74 -0
- package/dist/src/tools/update-page.d.ts +2 -2
- package/dist/src/tools/update-page.d.ts.map +1 -1
- package/dist/src/tools/update-page.js +23 -18
- package/dist/src/utils/error-handler.d.ts +1 -1
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/error-handler.js +14 -14
- package/dist/src/utils/index.d.ts +2 -2
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -2
- package/dist/src/utils/response-formatter.d.ts +13 -1
- package/dist/src/utils/response-formatter.d.ts.map +1 -1
- package/dist/src/utils/response-formatter.js +46 -3
- package/package.json +11 -2
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { blocksToMarkdown, blocksToMarkdownSync } from '../block-to-markdown.js';
|
|
3
|
+
describe('blocksToMarkdownSync', () => {
|
|
4
|
+
describe('basic blocks', () => {
|
|
5
|
+
it('converts paragraph block', () => {
|
|
6
|
+
const blocks = [
|
|
7
|
+
{
|
|
8
|
+
type: 'paragraph',
|
|
9
|
+
paragraph: {
|
|
10
|
+
rich_text: [{ type: 'text', text: { content: 'Hello World' } }],
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
expect(blocksToMarkdownSync(blocks)).toBe('Hello World');
|
|
15
|
+
});
|
|
16
|
+
it('handles empty paragraph', () => {
|
|
17
|
+
const blocks = [
|
|
18
|
+
{
|
|
19
|
+
type: 'paragraph',
|
|
20
|
+
paragraph: { rich_text: [] },
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
expect(blocksToMarkdownSync(blocks)).toBe('');
|
|
24
|
+
});
|
|
25
|
+
it('handles empty array', () => {
|
|
26
|
+
expect(blocksToMarkdownSync([])).toBe('');
|
|
27
|
+
});
|
|
28
|
+
it('handles undefined input', () => {
|
|
29
|
+
expect(blocksToMarkdownSync(undefined)).toBe('');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('headings', () => {
|
|
33
|
+
it('converts heading_1', () => {
|
|
34
|
+
const blocks = [
|
|
35
|
+
{
|
|
36
|
+
type: 'heading_1',
|
|
37
|
+
heading_1: {
|
|
38
|
+
rich_text: [{ type: 'text', text: { content: 'Heading 1' } }],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
expect(blocksToMarkdownSync(blocks)).toBe('# Heading 1');
|
|
43
|
+
});
|
|
44
|
+
it('converts heading_2', () => {
|
|
45
|
+
const blocks = [
|
|
46
|
+
{
|
|
47
|
+
type: 'heading_2',
|
|
48
|
+
heading_2: {
|
|
49
|
+
rich_text: [{ type: 'text', text: { content: 'Heading 2' } }],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
expect(blocksToMarkdownSync(blocks)).toBe('## Heading 2');
|
|
54
|
+
});
|
|
55
|
+
it('converts heading_3', () => {
|
|
56
|
+
const blocks = [
|
|
57
|
+
{
|
|
58
|
+
type: 'heading_3',
|
|
59
|
+
heading_3: {
|
|
60
|
+
rich_text: [{ type: 'text', text: { content: 'Heading 3' } }],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
expect(blocksToMarkdownSync(blocks)).toBe('### Heading 3');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('list items', () => {
|
|
68
|
+
it('converts bulleted_list_item', () => {
|
|
69
|
+
const blocks = [
|
|
70
|
+
{
|
|
71
|
+
type: 'bulleted_list_item',
|
|
72
|
+
bulleted_list_item: {
|
|
73
|
+
rich_text: [{ type: 'text', text: { content: 'Item 1' } }],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: 'bulleted_list_item',
|
|
78
|
+
bulleted_list_item: {
|
|
79
|
+
rich_text: [{ type: 'text', text: { content: 'Item 2' } }],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
expect(blocksToMarkdownSync(blocks)).toBe('- Item 1\n- Item 2');
|
|
84
|
+
});
|
|
85
|
+
it('converts numbered_list_item with correct indices', () => {
|
|
86
|
+
const blocks = [
|
|
87
|
+
{
|
|
88
|
+
type: 'numbered_list_item',
|
|
89
|
+
numbered_list_item: {
|
|
90
|
+
rich_text: [{ type: 'text', text: { content: 'First' } }],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: 'numbered_list_item',
|
|
95
|
+
numbered_list_item: {
|
|
96
|
+
rich_text: [{ type: 'text', text: { content: 'Second' } }],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'numbered_list_item',
|
|
101
|
+
numbered_list_item: {
|
|
102
|
+
rich_text: [{ type: 'text', text: { content: 'Third' } }],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
expect(blocksToMarkdownSync(blocks)).toBe('1. First\n2. Second\n3. Third');
|
|
107
|
+
});
|
|
108
|
+
it('resets numbered list index after non-list block', () => {
|
|
109
|
+
const blocks = [
|
|
110
|
+
{
|
|
111
|
+
type: 'numbered_list_item',
|
|
112
|
+
numbered_list_item: {
|
|
113
|
+
rich_text: [{ type: 'text', text: { content: 'First' } }],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: 'paragraph',
|
|
118
|
+
paragraph: {
|
|
119
|
+
rich_text: [{ type: 'text', text: { content: 'Break' } }],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'numbered_list_item',
|
|
124
|
+
numbered_list_item: {
|
|
125
|
+
rich_text: [{ type: 'text', text: { content: 'New First' } }],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
expect(blocksToMarkdownSync(blocks)).toBe('1. First\nBreak\n1. New First');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('to_do', () => {
|
|
133
|
+
it('converts unchecked to_do', () => {
|
|
134
|
+
const blocks = [
|
|
135
|
+
{
|
|
136
|
+
type: 'to_do',
|
|
137
|
+
to_do: {
|
|
138
|
+
rich_text: [{ type: 'text', text: { content: 'Task' } }],
|
|
139
|
+
checked: false,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
expect(blocksToMarkdownSync(blocks)).toBe('- [ ] Task');
|
|
144
|
+
});
|
|
145
|
+
it('converts checked to_do', () => {
|
|
146
|
+
const blocks = [
|
|
147
|
+
{
|
|
148
|
+
type: 'to_do',
|
|
149
|
+
to_do: {
|
|
150
|
+
rich_text: [{ type: 'text', text: { content: 'Done Task' } }],
|
|
151
|
+
checked: true,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
expect(blocksToMarkdownSync(blocks)).toBe('- [x] Done Task');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('code block', () => {
|
|
159
|
+
it('converts code block with language', () => {
|
|
160
|
+
const blocks = [
|
|
161
|
+
{
|
|
162
|
+
type: 'code',
|
|
163
|
+
code: {
|
|
164
|
+
rich_text: [{ type: 'text', text: { content: "console.log('hello')" } }],
|
|
165
|
+
language: 'javascript',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
expect(blocksToMarkdownSync(blocks)).toBe("```javascript\nconsole.log('hello')\n```");
|
|
170
|
+
});
|
|
171
|
+
it('converts code block without language', () => {
|
|
172
|
+
const blocks = [
|
|
173
|
+
{
|
|
174
|
+
type: 'code',
|
|
175
|
+
code: {
|
|
176
|
+
rich_text: [{ type: 'text', text: { content: 'plain code' } }],
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
expect(blocksToMarkdownSync(blocks)).toBe('```\nplain code\n```');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('quote and callout', () => {
|
|
184
|
+
it('converts quote', () => {
|
|
185
|
+
const blocks = [
|
|
186
|
+
{
|
|
187
|
+
type: 'quote',
|
|
188
|
+
quote: {
|
|
189
|
+
rich_text: [{ type: 'text', text: { content: 'A wise quote' } }],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
expect(blocksToMarkdownSync(blocks)).toBe('> A wise quote');
|
|
194
|
+
});
|
|
195
|
+
it('converts multiline quote', () => {
|
|
196
|
+
const blocks = [
|
|
197
|
+
{
|
|
198
|
+
type: 'quote',
|
|
199
|
+
quote: {
|
|
200
|
+
rich_text: [{ type: 'text', text: { content: 'Line 1\nLine 2' } }],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
expect(blocksToMarkdownSync(blocks)).toBe('> Line 1\n> Line 2');
|
|
205
|
+
});
|
|
206
|
+
it('converts callout with emoji icon', () => {
|
|
207
|
+
const blocks = [
|
|
208
|
+
{
|
|
209
|
+
type: 'callout',
|
|
210
|
+
callout: {
|
|
211
|
+
rich_text: [{ type: 'text', text: { content: 'Important info' } }],
|
|
212
|
+
icon: { type: 'emoji', emoji: '💡' },
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
];
|
|
216
|
+
expect(blocksToMarkdownSync(blocks)).toBe('> 💡 **Note:** Important info');
|
|
217
|
+
});
|
|
218
|
+
it('converts callout without icon', () => {
|
|
219
|
+
const blocks = [
|
|
220
|
+
{
|
|
221
|
+
type: 'callout',
|
|
222
|
+
callout: {
|
|
223
|
+
rich_text: [{ type: 'text', text: { content: 'Info' } }],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
];
|
|
227
|
+
expect(blocksToMarkdownSync(blocks)).toBe('> **Note:** Info');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
describe('divider', () => {
|
|
231
|
+
it('converts divider', () => {
|
|
232
|
+
const blocks = [{ type: 'divider', divider: {} }];
|
|
233
|
+
expect(blocksToMarkdownSync(blocks)).toBe('---');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe('bookmark and links', () => {
|
|
237
|
+
it('converts bookmark', () => {
|
|
238
|
+
const blocks = [
|
|
239
|
+
{
|
|
240
|
+
type: 'bookmark',
|
|
241
|
+
bookmark: {
|
|
242
|
+
url: 'https://example.com',
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[https://example.com](https://example.com)');
|
|
247
|
+
});
|
|
248
|
+
it('converts bookmark with caption', () => {
|
|
249
|
+
const blocks = [
|
|
250
|
+
{
|
|
251
|
+
type: 'bookmark',
|
|
252
|
+
bookmark: {
|
|
253
|
+
url: 'https://example.com',
|
|
254
|
+
caption: [{ type: 'text', text: { content: 'Example Site' } }],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[Example Site](https://example.com)');
|
|
259
|
+
});
|
|
260
|
+
it('converts link_preview', () => {
|
|
261
|
+
const blocks = [
|
|
262
|
+
{
|
|
263
|
+
type: 'link_preview',
|
|
264
|
+
link_preview: {
|
|
265
|
+
url: 'https://example.com/preview',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[https://example.com/preview](https://example.com/preview)');
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
describe('media blocks', () => {
|
|
273
|
+
it('converts image with external url', () => {
|
|
274
|
+
const blocks = [
|
|
275
|
+
{
|
|
276
|
+
type: 'image',
|
|
277
|
+
image: {
|
|
278
|
+
type: 'external',
|
|
279
|
+
external: { url: 'https://example.com/image.png' },
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
];
|
|
283
|
+
expect(blocksToMarkdownSync(blocks)).toBe('');
|
|
284
|
+
});
|
|
285
|
+
it('converts image with caption', () => {
|
|
286
|
+
const blocks = [
|
|
287
|
+
{
|
|
288
|
+
type: 'image',
|
|
289
|
+
image: {
|
|
290
|
+
type: 'external',
|
|
291
|
+
external: { url: 'https://example.com/image.png' },
|
|
292
|
+
caption: [{ type: 'text', text: { content: 'My Image' } }],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
];
|
|
296
|
+
expect(blocksToMarkdownSync(blocks)).toBe('');
|
|
297
|
+
});
|
|
298
|
+
it('converts image with file url', () => {
|
|
299
|
+
const blocks = [
|
|
300
|
+
{
|
|
301
|
+
type: 'image',
|
|
302
|
+
image: {
|
|
303
|
+
type: 'file',
|
|
304
|
+
file: { url: 'https://s3.amazonaws.com/file.png' },
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
];
|
|
308
|
+
expect(blocksToMarkdownSync(blocks)).toBe('');
|
|
309
|
+
});
|
|
310
|
+
it('converts video', () => {
|
|
311
|
+
const blocks = [
|
|
312
|
+
{
|
|
313
|
+
type: 'video',
|
|
314
|
+
video: {
|
|
315
|
+
type: 'external',
|
|
316
|
+
external: { url: 'https://youtube.com/watch?v=123' },
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
];
|
|
320
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[Video](https://youtube.com/watch?v=123)');
|
|
321
|
+
});
|
|
322
|
+
it('converts audio', () => {
|
|
323
|
+
const blocks = [
|
|
324
|
+
{
|
|
325
|
+
type: 'audio',
|
|
326
|
+
audio: {
|
|
327
|
+
type: 'external',
|
|
328
|
+
external: { url: 'https://example.com/audio.mp3' },
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[Audio](https://example.com/audio.mp3)');
|
|
333
|
+
});
|
|
334
|
+
it('converts file', () => {
|
|
335
|
+
const blocks = [
|
|
336
|
+
{
|
|
337
|
+
type: 'file',
|
|
338
|
+
file: {
|
|
339
|
+
type: 'file',
|
|
340
|
+
file: { url: 'https://example.com/doc.pdf' },
|
|
341
|
+
name: 'Document',
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
];
|
|
345
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[Document](https://example.com/doc.pdf)');
|
|
346
|
+
});
|
|
347
|
+
it('converts pdf', () => {
|
|
348
|
+
const blocks = [
|
|
349
|
+
{
|
|
350
|
+
type: 'pdf',
|
|
351
|
+
pdf: {
|
|
352
|
+
type: 'external',
|
|
353
|
+
external: { url: 'https://example.com/doc.pdf' },
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
];
|
|
357
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[File](https://example.com/doc.pdf)');
|
|
358
|
+
});
|
|
359
|
+
it('converts embed', () => {
|
|
360
|
+
const blocks = [
|
|
361
|
+
{
|
|
362
|
+
type: 'embed',
|
|
363
|
+
embed: {
|
|
364
|
+
url: 'https://codepen.io/pen/123',
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
];
|
|
368
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[Embed](https://codepen.io/pen/123)');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
describe('toggle', () => {
|
|
372
|
+
it('converts toggle block', () => {
|
|
373
|
+
const blocks = [
|
|
374
|
+
{
|
|
375
|
+
type: 'toggle',
|
|
376
|
+
toggle: {
|
|
377
|
+
rich_text: [{ type: 'text', text: { content: 'Click to expand' } }],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
];
|
|
381
|
+
expect(blocksToMarkdownSync(blocks)).toBe('<details>\n<summary>Click to expand</summary>\n</details>');
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
describe('special blocks', () => {
|
|
385
|
+
it('converts table_of_contents', () => {
|
|
386
|
+
const blocks = [
|
|
387
|
+
{
|
|
388
|
+
type: 'table_of_contents',
|
|
389
|
+
table_of_contents: {},
|
|
390
|
+
},
|
|
391
|
+
];
|
|
392
|
+
expect(blocksToMarkdownSync(blocks)).toBe('[TOC]');
|
|
393
|
+
});
|
|
394
|
+
it('converts equation', () => {
|
|
395
|
+
const blocks = [
|
|
396
|
+
{
|
|
397
|
+
type: 'equation',
|
|
398
|
+
equation: {
|
|
399
|
+
expression: 'E = mc^2',
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
];
|
|
403
|
+
expect(blocksToMarkdownSync(blocks)).toBe('$$\nE = mc^2\n$$');
|
|
404
|
+
});
|
|
405
|
+
it('converts child_page', () => {
|
|
406
|
+
const blocks = [
|
|
407
|
+
{
|
|
408
|
+
type: 'child_page',
|
|
409
|
+
child_page: {
|
|
410
|
+
title: 'My Subpage',
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
];
|
|
414
|
+
expect(blocksToMarkdownSync(blocks)).toBe('📄 [My Subpage]');
|
|
415
|
+
});
|
|
416
|
+
it('converts child_database', () => {
|
|
417
|
+
const blocks = [
|
|
418
|
+
{
|
|
419
|
+
type: 'child_database',
|
|
420
|
+
child_database: {
|
|
421
|
+
title: 'My Database',
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
];
|
|
425
|
+
expect(blocksToMarkdownSync(blocks)).toBe('📊 [My Database]');
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
describe('unsupported blocks', () => {
|
|
429
|
+
it('handles unsupported block type', () => {
|
|
430
|
+
const blocks = [
|
|
431
|
+
{
|
|
432
|
+
type: 'unknown_type',
|
|
433
|
+
unknown_type: { some: 'data' },
|
|
434
|
+
},
|
|
435
|
+
];
|
|
436
|
+
expect(blocksToMarkdownSync(blocks)).toBe('<!-- Unsupported block type: unknown_type -->');
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
describe('real world data', () => {
|
|
440
|
+
it('converts actual Notion block data', () => {
|
|
441
|
+
// 実際のNotionから取得したデータ形式
|
|
442
|
+
const blocks = [
|
|
443
|
+
{
|
|
444
|
+
object: 'block',
|
|
445
|
+
id: 'f542c96d-5d08-4488-83a0-ec2bb4af3d10',
|
|
446
|
+
type: 'heading_2',
|
|
447
|
+
heading_2: {
|
|
448
|
+
rich_text: [
|
|
449
|
+
{
|
|
450
|
+
type: 'text',
|
|
451
|
+
text: { content: '問題', link: null },
|
|
452
|
+
annotations: {
|
|
453
|
+
bold: false,
|
|
454
|
+
italic: false,
|
|
455
|
+
strikethrough: false,
|
|
456
|
+
underline: false,
|
|
457
|
+
code: false,
|
|
458
|
+
color: 'default',
|
|
459
|
+
},
|
|
460
|
+
plain_text: '問題',
|
|
461
|
+
href: null,
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
is_toggleable: false,
|
|
465
|
+
color: 'default',
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
object: 'block',
|
|
470
|
+
id: '8c7f084b-e946-4456-a85b-1bd160ffed9a',
|
|
471
|
+
type: 'paragraph',
|
|
472
|
+
paragraph: {
|
|
473
|
+
rich_text: [
|
|
474
|
+
{
|
|
475
|
+
type: 'text',
|
|
476
|
+
text: { content: '以下のパッケージで ', link: null },
|
|
477
|
+
annotations: {
|
|
478
|
+
bold: false,
|
|
479
|
+
italic: false,
|
|
480
|
+
strikethrough: false,
|
|
481
|
+
underline: false,
|
|
482
|
+
code: false,
|
|
483
|
+
color: 'default',
|
|
484
|
+
},
|
|
485
|
+
plain_text: '以下のパッケージで ',
|
|
486
|
+
href: null,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
type: 'text',
|
|
490
|
+
text: { content: 'exports', link: null },
|
|
491
|
+
annotations: {
|
|
492
|
+
bold: false,
|
|
493
|
+
italic: false,
|
|
494
|
+
strikethrough: false,
|
|
495
|
+
underline: false,
|
|
496
|
+
code: true,
|
|
497
|
+
color: 'default',
|
|
498
|
+
},
|
|
499
|
+
plain_text: 'exports',
|
|
500
|
+
href: null,
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
type: 'text',
|
|
504
|
+
text: { content: ' が必要:', link: null },
|
|
505
|
+
annotations: {
|
|
506
|
+
bold: false,
|
|
507
|
+
italic: false,
|
|
508
|
+
strikethrough: false,
|
|
509
|
+
underline: false,
|
|
510
|
+
code: false,
|
|
511
|
+
color: 'default',
|
|
512
|
+
},
|
|
513
|
+
plain_text: ' が必要:',
|
|
514
|
+
href: null,
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
color: 'default',
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
object: 'block',
|
|
522
|
+
id: 'cde242b1-9b0c-47f4-af8e-29122bd1f048',
|
|
523
|
+
type: 'bulleted_list_item',
|
|
524
|
+
bulleted_list_item: {
|
|
525
|
+
rich_text: [
|
|
526
|
+
{
|
|
527
|
+
type: 'text',
|
|
528
|
+
text: { content: 'apps/supabase-client/package.json', link: null },
|
|
529
|
+
annotations: {
|
|
530
|
+
bold: false,
|
|
531
|
+
italic: false,
|
|
532
|
+
strikethrough: false,
|
|
533
|
+
underline: false,
|
|
534
|
+
code: true,
|
|
535
|
+
color: 'default',
|
|
536
|
+
},
|
|
537
|
+
plain_text: 'apps/supabase-client/package.json',
|
|
538
|
+
href: null,
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
color: 'default',
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
];
|
|
545
|
+
const result = blocksToMarkdownSync(blocks);
|
|
546
|
+
expect(result).toBe('## 問題\n以下のパッケージで `exports` が必要:\n- `apps/supabase-client/package.json`');
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
describe('blocksToMarkdown (async)', () => {
|
|
551
|
+
it('works without fetchChildren option', async () => {
|
|
552
|
+
const blocks = [
|
|
553
|
+
{
|
|
554
|
+
type: 'paragraph',
|
|
555
|
+
paragraph: {
|
|
556
|
+
rich_text: [{ type: 'text', text: { content: 'Hello' } }],
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
];
|
|
560
|
+
const result = await blocksToMarkdown(blocks);
|
|
561
|
+
expect(result).toBe('Hello');
|
|
562
|
+
});
|
|
563
|
+
it('fetches children for nested bulleted_list_item', async () => {
|
|
564
|
+
const mockFetchChildren = vi.fn().mockResolvedValue([
|
|
565
|
+
{
|
|
566
|
+
type: 'paragraph',
|
|
567
|
+
paragraph: {
|
|
568
|
+
rich_text: [{ type: 'text', text: { content: 'Nested content' } }],
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
]);
|
|
572
|
+
const blocks = [
|
|
573
|
+
{
|
|
574
|
+
id: 'parent-id',
|
|
575
|
+
type: 'bulleted_list_item',
|
|
576
|
+
has_children: true,
|
|
577
|
+
bulleted_list_item: {
|
|
578
|
+
rich_text: [{ type: 'text', text: { content: 'Parent item' } }],
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
];
|
|
582
|
+
const result = await blocksToMarkdown(blocks, { fetchChildren: mockFetchChildren });
|
|
583
|
+
expect(mockFetchChildren).toHaveBeenCalledWith('parent-id');
|
|
584
|
+
expect(result).toContain('- Parent item');
|
|
585
|
+
expect(result).toContain('Nested content');
|
|
586
|
+
});
|
|
587
|
+
it('handles toggle block with children', async () => {
|
|
588
|
+
const mockFetchChildren = vi.fn().mockResolvedValue([
|
|
589
|
+
{
|
|
590
|
+
type: 'paragraph',
|
|
591
|
+
paragraph: {
|
|
592
|
+
rich_text: [{ type: 'text', text: { content: 'Toggle content' } }],
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
]);
|
|
596
|
+
const blocks = [
|
|
597
|
+
{
|
|
598
|
+
id: 'toggle-id',
|
|
599
|
+
type: 'toggle',
|
|
600
|
+
has_children: true,
|
|
601
|
+
toggle: {
|
|
602
|
+
rich_text: [{ type: 'text', text: { content: 'Click me' } }],
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
];
|
|
606
|
+
const result = await blocksToMarkdown(blocks, { fetchChildren: mockFetchChildren });
|
|
607
|
+
expect(mockFetchChildren).toHaveBeenCalledWith('toggle-id');
|
|
608
|
+
expect(result).toContain('<summary>Click me</summary>');
|
|
609
|
+
expect(result).toContain('Toggle content');
|
|
610
|
+
});
|
|
611
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-to-markdown.test.d.ts","sourceRoot":"","sources":["../../../../src/converters/__tests__/page-to-markdown.test.ts"],"names":[],"mappings":""}
|