@sillsdev/docu-notion 0.11.1
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/LICENSE +21 -0
- package/README.md +135 -0
- package/dist/FlatGuidLayoutStrategy.d.ts +6 -0
- package/dist/FlatGuidLayoutStrategy.js +25 -0
- package/dist/HierarchicalNamedLayoutStrategy.d.ts +7 -0
- package/dist/HierarchicalNamedLayoutStrategy.js +80 -0
- package/dist/LayoutStrategy.d.ts +12 -0
- package/dist/LayoutStrategy.js +83 -0
- package/dist/MakeImagePersistencePlan.d.ts +2 -0
- package/dist/MakeImagePersistencePlan.js +66 -0
- package/dist/NotionImage-CaptionReading.spec.d.ts +1 -0
- package/dist/NotionImage-CaptionReading.spec.js +233 -0
- package/dist/NotionPage.d.ts +45 -0
- package/dist/NotionPage.js +229 -0
- package/dist/NotionPage.spec.d.ts +1 -0
- package/dist/NotionPage.spec.js +143 -0
- package/dist/config/configuration.d.ts +5 -0
- package/dist/config/configuration.js +86 -0
- package/dist/config/default.docunotion.config.d.ts +3 -0
- package/dist/config/default.docunotion.config.js +37 -0
- package/dist/images.d.ts +24 -0
- package/dist/images.js +230 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +37 -0
- package/dist/log.d.ts +11 -0
- package/dist/log.js +61 -0
- package/dist/makeImagePersistencePlan.spec.d.ts +1 -0
- package/dist/makeImagePersistencePlan.spec.js +35 -0
- package/dist/notion-styles.css +58 -0
- package/dist/plugins/CalloutTransformer.d.ts +24 -0
- package/dist/plugins/CalloutTransformer.js +88 -0
- package/dist/plugins/CalloutTransformer.spec.d.ts +1 -0
- package/dist/plugins/CalloutTransformer.spec.js +199 -0
- package/dist/plugins/ColumnListTransformer.d.ts +2 -0
- package/dist/plugins/ColumnListTransformer.js +34 -0
- package/dist/plugins/ColumnTransformer.d.ts +2 -0
- package/dist/plugins/ColumnTransformer.js +67 -0
- package/dist/plugins/EscapeHtmlBlockModifier.d.ts +2 -0
- package/dist/plugins/EscapeHtmlBlockModifier.js +41 -0
- package/dist/plugins/EscapeHtmlBlockModifier.spec.d.ts +1 -0
- package/dist/plugins/EscapeHtmlBlockModifier.spec.js +130 -0
- package/dist/plugins/HeadingTranformer.spec.d.ts +1 -0
- package/dist/plugins/HeadingTranformer.spec.js +46 -0
- package/dist/plugins/HeadingTransformer.d.ts +2 -0
- package/dist/plugins/HeadingTransformer.js +63 -0
- package/dist/plugins/NumberedListTransformer.d.ts +2 -0
- package/dist/plugins/NumberedListTransformer.js +55 -0
- package/dist/plugins/NumberedListTransformer.spec.d.ts +1 -0
- package/dist/plugins/NumberedListTransformer.spec.js +86 -0
- package/dist/plugins/TableTransformer.d.ts +5 -0
- package/dist/plugins/TableTransformer.js +70 -0
- package/dist/plugins/embedTweaks.d.ts +5 -0
- package/dist/plugins/embedTweaks.js +46 -0
- package/dist/plugins/embedTweaks.spec.d.ts +1 -0
- package/dist/plugins/embedTweaks.spec.js +230 -0
- package/dist/plugins/externalLinks.d.ts +2 -0
- package/dist/plugins/externalLinks.js +26 -0
- package/dist/plugins/externalLinks.spec.d.ts +1 -0
- package/dist/plugins/externalLinks.spec.js +132 -0
- package/dist/plugins/internalLinks.d.ts +7 -0
- package/dist/plugins/internalLinks.js +102 -0
- package/dist/plugins/internalLinks.spec.d.ts +1 -0
- package/dist/plugins/internalLinks.spec.js +505 -0
- package/dist/plugins/mermaidLinkPlugin.spec.d.ts +1 -0
- package/dist/plugins/mermaidLinkPlugin.spec.js +77 -0
- package/dist/plugins/pluginTestRun.d.ts +10 -0
- package/dist/plugins/pluginTestRun.js +252 -0
- package/dist/plugins/pluginTypes.d.ts +44 -0
- package/dist/plugins/pluginTypes.js +2 -0
- package/dist/pull.d.ts +12 -0
- package/dist/pull.js +255 -0
- package/dist/run.d.ts +1 -0
- package/dist/run.js +35 -0
- package/dist/transform.d.ts +6 -0
- package/dist/transform.js +200 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.js +2 -0
- package/package.json +103 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.standardCalloutTransformer = void 0;
|
|
13
|
+
// In Notion, you can make a callout and change its emoji. We map 5 of these
|
|
14
|
+
// to the 5 Docusaurus admonition styles.
|
|
15
|
+
// This is mostly a copy of the callout code from notion-to-md. The change is to output docusaurus
|
|
16
|
+
// admonitions instead of emulating a callout with markdown > syntax.
|
|
17
|
+
// Note: I haven't yet tested this with any emoji except "💡"/"tip", nor the case where the
|
|
18
|
+
// callout has-children. Not even sure what that would mean, since the document I was testing
|
|
19
|
+
// with has quite complex markup inside the callout, but still takes the no-children branch.
|
|
20
|
+
function notionCalloutToAdmonition(notionToMarkdown, getBlockChildren, block) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
// In this case typescript is not able to index the types properly, hence ignoring the error
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const blockContent = block.callout.text || block.callout.rich_text || [];
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
const icon = block.callout.icon;
|
|
27
|
+
let parsedData = "";
|
|
28
|
+
blockContent.map((content) => {
|
|
29
|
+
const annotations = content.annotations;
|
|
30
|
+
let plain_text = content.plain_text;
|
|
31
|
+
plain_text = notionToMarkdown.annotatePlainText(plain_text, annotations);
|
|
32
|
+
if (content["href"])
|
|
33
|
+
plain_text = `[${plain_text}](${content["href"]})`;
|
|
34
|
+
parsedData += plain_text;
|
|
35
|
+
});
|
|
36
|
+
let callout_string = "";
|
|
37
|
+
const { id, has_children } = block;
|
|
38
|
+
if (!has_children) {
|
|
39
|
+
const result1 = callout(parsedData, icon);
|
|
40
|
+
return result1;
|
|
41
|
+
}
|
|
42
|
+
const callout_children_object = yield getBlockChildren(id);
|
|
43
|
+
// // parse children blocks to md object
|
|
44
|
+
const callout_children = yield notionToMarkdown.blocksToMarkdown(callout_children_object);
|
|
45
|
+
callout_string += `${parsedData}\n`;
|
|
46
|
+
callout_children.map(child => {
|
|
47
|
+
callout_string += `${child.parent}\n\n`;
|
|
48
|
+
});
|
|
49
|
+
const result = callout(callout_string.trim(), icon);
|
|
50
|
+
return result;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const calloutsToAdmonitions = {
|
|
54
|
+
/* prettier-ignore */ "ℹ️": "note",
|
|
55
|
+
"📝": "note",
|
|
56
|
+
"💡": "tip",
|
|
57
|
+
"❗": "info",
|
|
58
|
+
"⚠️": "caution",
|
|
59
|
+
"🔥": "danger",
|
|
60
|
+
};
|
|
61
|
+
// This is the main change from the notion-to-md code.
|
|
62
|
+
function callout(text, icon) {
|
|
63
|
+
var _a;
|
|
64
|
+
let emoji;
|
|
65
|
+
if ((icon === null || icon === void 0 ? void 0 : icon.type) === "emoji") {
|
|
66
|
+
emoji = icon.emoji;
|
|
67
|
+
}
|
|
68
|
+
let docusaurusAdmonition = "note";
|
|
69
|
+
if (emoji) {
|
|
70
|
+
// the keyof typeof magic persuades typescript that it really is OK to use emoji as a key into calloutsToAdmonitions
|
|
71
|
+
docusaurusAdmonition =
|
|
72
|
+
(_a = calloutsToAdmonitions[emoji]) !== null && _a !== void 0 ? _a :
|
|
73
|
+
// For Notion callouts with other emojis, pass them through using hte emoji as the name.
|
|
74
|
+
// For this to work on a Docusaurus site, it will need to define that time on the remark-admonitions options in the docusaurus.config.js.
|
|
75
|
+
// See https://github.com/elviswolcott/remark-admonitions and https://docusaurus.io/docs/using-plugins#using-presets.
|
|
76
|
+
emoji;
|
|
77
|
+
}
|
|
78
|
+
return `:::${docusaurusAdmonition}\n\n${text}\n\n:::\n\n`;
|
|
79
|
+
}
|
|
80
|
+
exports.standardCalloutTransformer = {
|
|
81
|
+
name: "standardCalloutTransformer",
|
|
82
|
+
notionToMarkdownTransforms: [
|
|
83
|
+
{
|
|
84
|
+
type: "callout",
|
|
85
|
+
getStringFromBlock: (context, block) => notionCalloutToAdmonition(context.notionToMarkdown, context.getBlockChildren, block),
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const pluginTestRun_1 = require("./pluginTestRun");
|
|
13
|
+
const CalloutTransformer_1 = require("./CalloutTransformer");
|
|
14
|
+
const externalLinks_1 = require("./externalLinks");
|
|
15
|
+
const internalLinks_1 = require("./internalLinks");
|
|
16
|
+
let block;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
block = {
|
|
19
|
+
has_children: false,
|
|
20
|
+
archived: false,
|
|
21
|
+
type: "callout",
|
|
22
|
+
callout: {
|
|
23
|
+
rich_text: [
|
|
24
|
+
{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: { content: "This is information callout", link: null },
|
|
27
|
+
annotations: {
|
|
28
|
+
bold: false,
|
|
29
|
+
italic: false,
|
|
30
|
+
strikethrough: false,
|
|
31
|
+
underline: false,
|
|
32
|
+
code: false,
|
|
33
|
+
color: "default",
|
|
34
|
+
},
|
|
35
|
+
plain_text: "This is the callout",
|
|
36
|
+
href: null,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
icon: { type: "emoji", emoji: "ℹ️" },
|
|
40
|
+
color: "gray_background",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
test("smoketest callout", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
+
const config = { plugins: [CalloutTransformer_1.standardCalloutTransformer] };
|
|
46
|
+
block.callout.icon.emoji = "ℹ️";
|
|
47
|
+
let results = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
48
|
+
block,
|
|
49
|
+
]);
|
|
50
|
+
expect(results).toContain("\n:::note\n\nThis is the callout\n\n:::\n");
|
|
51
|
+
block.callout.icon.emoji = "❗";
|
|
52
|
+
results = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [block]);
|
|
53
|
+
expect(results).toContain(":::info");
|
|
54
|
+
}));
|
|
55
|
+
test("external link inside callout, bold preserved", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
56
|
+
const config = {
|
|
57
|
+
plugins: [
|
|
58
|
+
CalloutTransformer_1.standardCalloutTransformer,
|
|
59
|
+
internalLinks_1.standardInternalLinkConversion,
|
|
60
|
+
externalLinks_1.standardExternalLinkConversion,
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
const results = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
64
|
+
{
|
|
65
|
+
type: "callout",
|
|
66
|
+
callout: {
|
|
67
|
+
rich_text: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: { content: "Callouts inline ", link: null },
|
|
71
|
+
annotations: {
|
|
72
|
+
bold: false,
|
|
73
|
+
italic: false,
|
|
74
|
+
strikethrough: false,
|
|
75
|
+
underline: false,
|
|
76
|
+
code: false,
|
|
77
|
+
color: "default",
|
|
78
|
+
},
|
|
79
|
+
plain_text: "Callouts inline ",
|
|
80
|
+
href: null,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: {
|
|
85
|
+
content: "great page",
|
|
86
|
+
link: { url: `https://github.com` },
|
|
87
|
+
},
|
|
88
|
+
annotations: {
|
|
89
|
+
bold: true,
|
|
90
|
+
italic: false,
|
|
91
|
+
strikethrough: false,
|
|
92
|
+
underline: false,
|
|
93
|
+
code: false,
|
|
94
|
+
color: "default",
|
|
95
|
+
},
|
|
96
|
+
plain_text: "great page",
|
|
97
|
+
href: `https://github.com`,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: { content: ".", link: null },
|
|
102
|
+
annotations: {
|
|
103
|
+
bold: false,
|
|
104
|
+
italic: false,
|
|
105
|
+
strikethrough: false,
|
|
106
|
+
underline: false,
|
|
107
|
+
code: false,
|
|
108
|
+
color: "default",
|
|
109
|
+
},
|
|
110
|
+
plain_text: ".",
|
|
111
|
+
href: null,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
icon: { type: "emoji", emoji: "⚠️" },
|
|
115
|
+
color: "gray_background",
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
]);
|
|
119
|
+
expect(results.trim()).toBe(`:::caution
|
|
120
|
+
|
|
121
|
+
Callouts inline [**great page**](https://github.com).
|
|
122
|
+
|
|
123
|
+
:::`);
|
|
124
|
+
}));
|
|
125
|
+
test("internal link inside callout, bold preserved", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
126
|
+
const config = {
|
|
127
|
+
plugins: [
|
|
128
|
+
CalloutTransformer_1.standardCalloutTransformer,
|
|
129
|
+
internalLinks_1.standardInternalLinkConversion,
|
|
130
|
+
externalLinks_1.standardExternalLinkConversion,
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
const slugTargetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
134
|
+
slug: "hello-world",
|
|
135
|
+
name: "Hello World",
|
|
136
|
+
id: "123",
|
|
137
|
+
});
|
|
138
|
+
const results = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
139
|
+
{
|
|
140
|
+
type: "callout",
|
|
141
|
+
callout: {
|
|
142
|
+
rich_text: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: { content: "Callouts inline ", link: null },
|
|
146
|
+
annotations: {
|
|
147
|
+
bold: false,
|
|
148
|
+
italic: false,
|
|
149
|
+
strikethrough: false,
|
|
150
|
+
underline: false,
|
|
151
|
+
code: false,
|
|
152
|
+
color: "default",
|
|
153
|
+
},
|
|
154
|
+
plain_text: "Callouts inline ",
|
|
155
|
+
href: null,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: {
|
|
160
|
+
content: "great page",
|
|
161
|
+
link: { url: `/123#456` },
|
|
162
|
+
},
|
|
163
|
+
annotations: {
|
|
164
|
+
bold: true,
|
|
165
|
+
italic: false,
|
|
166
|
+
strikethrough: false,
|
|
167
|
+
underline: false,
|
|
168
|
+
code: false,
|
|
169
|
+
color: "default",
|
|
170
|
+
},
|
|
171
|
+
plain_text: "great page",
|
|
172
|
+
href: `/123#456`,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: { content: ".", link: null },
|
|
177
|
+
annotations: {
|
|
178
|
+
bold: false,
|
|
179
|
+
italic: false,
|
|
180
|
+
strikethrough: false,
|
|
181
|
+
underline: false,
|
|
182
|
+
code: false,
|
|
183
|
+
color: "default",
|
|
184
|
+
},
|
|
185
|
+
plain_text: " the end.",
|
|
186
|
+
href: null,
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
icon: { type: "emoji", emoji: "⚠️" },
|
|
190
|
+
color: "gray_background",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
], [slugTargetPage]);
|
|
194
|
+
expect(results.trim()).toBe(`:::caution
|
|
195
|
+
|
|
196
|
+
Callouts inline [**great page**](/hello-world#456) the end.
|
|
197
|
+
|
|
198
|
+
:::`);
|
|
199
|
+
}));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.standardColumnListTransformer = void 0;
|
|
13
|
+
function notionColumnListToMarkdown(notionToMarkdown, getBlockChildren, block) {
|
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
// Enhance: The @notionhq/client, which uses the official API, cannot yet get at column formatting information (column_ratio)
|
|
16
|
+
// However https://github1s.com/NotionX/react-notion-x/blob/master/packages/react-notion-x/src/block.tsx#L528 can get it.
|
|
17
|
+
const { id, has_children } = block; // "any" because the notion api type system is complex with a union that don't know how to help TS to cope with
|
|
18
|
+
if (!has_children)
|
|
19
|
+
return "";
|
|
20
|
+
const column_list_children = yield getBlockChildren(id);
|
|
21
|
+
const column_list_promise = column_list_children.map((column) => __awaiter(this, void 0, void 0, function* () { return yield notionToMarkdown.blockToMarkdown(column); }));
|
|
22
|
+
const columns = yield Promise.all(column_list_promise);
|
|
23
|
+
return `<div class='notion-row'>\n${columns.join("\n\n")}\n</div>`;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
exports.standardColumnListTransformer = {
|
|
27
|
+
name: "standardColumnListTransformer",
|
|
28
|
+
notionToMarkdownTransforms: [
|
|
29
|
+
{
|
|
30
|
+
type: "column_list",
|
|
31
|
+
getStringFromBlock: (context, block) => notionColumnListToMarkdown(context.notionToMarkdown, context.getBlockChildren, block),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.standardColumnTransformer = void 0;
|
|
13
|
+
const notion_client_1 = require("notion-client");
|
|
14
|
+
exports.standardColumnTransformer = {
|
|
15
|
+
name: "standardColumnTransformer",
|
|
16
|
+
notionToMarkdownTransforms: [
|
|
17
|
+
{
|
|
18
|
+
type: "column",
|
|
19
|
+
getStringFromBlock: (context, block) => notionColumnToMarkdown(context.notionToMarkdown, context.getBlockChildren, block),
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
// This runs when notion-to-md encounters a column block
|
|
24
|
+
function notionColumnToMarkdown(notionToMarkdown, getBlockChildren, block) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
//console.log(JSON.stringify(block));
|
|
27
|
+
const { id, has_children } = block; // "any" because the notion api type system is complex with a union that don't know how to help TS to cope with
|
|
28
|
+
if (!has_children)
|
|
29
|
+
return "";
|
|
30
|
+
const children = yield getBlockChildren(id);
|
|
31
|
+
const childrenPromises = children.map((column) => __awaiter(this, void 0, void 0, function* () { return yield notionToMarkdown.blockToMarkdown(column); }));
|
|
32
|
+
const childrenStrings = yield Promise.all(childrenPromises);
|
|
33
|
+
const columnWidth = yield getColumnWidth(block);
|
|
34
|
+
// note: it would look better in the markup with \n, but that
|
|
35
|
+
// causes notion-to-md to give us ":::A" instead of \n for some reason.
|
|
36
|
+
return (`<div class='notion-column' style={{width: '${columnWidth}'}}>\n\n${childrenStrings.join("\n\n")}\n\n</div>` +
|
|
37
|
+
// Spacer between columns. CSS takes care of hiding this for the last column
|
|
38
|
+
// and when the screen is too narrow for multiple columns.
|
|
39
|
+
`<div className='notion-spacer' />`);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// The official API doesn't give us access to the format information, including column_ratio.
|
|
43
|
+
// So we use 'notion-client' which uses the unofficial API.
|
|
44
|
+
// Once the official API gives us access to the format information, we can remove this
|
|
45
|
+
// and the 'notion-client' dependency.
|
|
46
|
+
// This logic was mostly taken from react-notion-x (sister project of notion-client).
|
|
47
|
+
function getColumnWidth(block) {
|
|
48
|
+
var _a, _b, _c, _d;
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const unofficialNotionClient = new notion_client_1.NotionAPI();
|
|
51
|
+
const blockId = block.id;
|
|
52
|
+
// Yes, it is odd to call 'getPage' for a block, but that's how we access the format info.
|
|
53
|
+
const recordMap = yield unofficialNotionClient.getPage(blockId);
|
|
54
|
+
const blockResult = recordMap.block[blockId];
|
|
55
|
+
// ENHANCE: could we use https://github.com/NotionX/react-notion-x/tree/master/packages/notion-types
|
|
56
|
+
// to get away from "any", which might be particularly helpful in the future
|
|
57
|
+
// since this is using the unofficial (reverse engineered?) API.
|
|
58
|
+
const columnFormat = (_a = blockResult === null || blockResult === void 0 ? void 0 : blockResult.value) === null || _a === void 0 ? void 0 : _a.format;
|
|
59
|
+
const columnRatio = (columnFormat === null || columnFormat === void 0 ? void 0 : columnFormat.column_ratio) || 0.5;
|
|
60
|
+
const parentBlock = (_c = recordMap.block[(_b = blockResult === null || blockResult === void 0 ? void 0 : blockResult.value) === null || _b === void 0 ? void 0 : _b.parent_id]) === null || _c === void 0 ? void 0 : _c.value;
|
|
61
|
+
// I'm not sure why we wouldn't get a parent, but the react-notion-x has
|
|
62
|
+
// this fallback to a guess based on the columnRatio.
|
|
63
|
+
const columnCount = ((_d = parentBlock === null || parentBlock === void 0 ? void 0 : parentBlock.content) === null || _d === void 0 ? void 0 : _d.length) || Math.max(2, Math.ceil(1.0 / columnRatio));
|
|
64
|
+
const spacerWidth = `min(32px, 4vw)`; // This matches the value in css for 'notion-spacer'.
|
|
65
|
+
return `calc((100% - (${spacerWidth} * ${columnCount - 1})) * ${columnRatio})`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.standardEscapeHtmlBlockModifier = void 0;
|
|
4
|
+
exports.standardEscapeHtmlBlockModifier = {
|
|
5
|
+
name: "standardEscapeHtmlBlockModifier",
|
|
6
|
+
notionBlockModifications: [
|
|
7
|
+
{
|
|
8
|
+
modify: (block) => {
|
|
9
|
+
escapeHtml(block);
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
};
|
|
14
|
+
function escapeHtml(block) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
//console.log("escapeHtml called with\n", JSON.stringify(block, null, 2));
|
|
17
|
+
const blockContent = block[block.type]; // e.g. block["paragraph"] gives an array of the strings that make up the paragraph
|
|
18
|
+
if ((_a = blockContent.rich_text) === null || _a === void 0 ? void 0 : _a.length) {
|
|
19
|
+
for (let i = 0; i < blockContent.rich_text.length; i++) {
|
|
20
|
+
const rt = blockContent.rich_text[i];
|
|
21
|
+
// See https://github.com/sillsdev/docu-notion/issues/21.
|
|
22
|
+
// For now, we just do a simple replace of < an > with < and >
|
|
23
|
+
// but only if the text will not be displayed as code.
|
|
24
|
+
// If it will be displayed as code,
|
|
25
|
+
// a) nothing will be trying to parse it, so it is safe.
|
|
26
|
+
// b) at no point does anything interpret the escaped character **back** to html;
|
|
27
|
+
// so it will be displayed as "<" or ">".
|
|
28
|
+
// We may have to add more complex logic here in the future if we
|
|
29
|
+
// want to start letting html through which we **do** want to parse.
|
|
30
|
+
// For example, we could assume that text in a valid html structure should be parsed.
|
|
31
|
+
if ((rt === null || rt === void 0 ? void 0 : rt.plain_text) &&
|
|
32
|
+
block.type !== "code" &&
|
|
33
|
+
rt.type !== "code" &&
|
|
34
|
+
!((_b = rt.annotations) === null || _b === void 0 ? void 0 : _b.code)) {
|
|
35
|
+
rt.plain_text = rt.plain_text
|
|
36
|
+
.replaceAll("<", "<")
|
|
37
|
+
.replaceAll(">", ">");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const pluginTestRun_1 = require("./pluginTestRun");
|
|
13
|
+
const EscapeHtmlBlockModifier_1 = require("./EscapeHtmlBlockModifier");
|
|
14
|
+
let blocks;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
blocks = [
|
|
17
|
+
{
|
|
18
|
+
object: "block",
|
|
19
|
+
id: "503533c3-c1cc-4f5f-89bc-95472486d16c",
|
|
20
|
+
parent: {
|
|
21
|
+
type: "page_id",
|
|
22
|
+
page_id: "a623852e-3552-40cf-89a9-7e3adbc07e9c",
|
|
23
|
+
},
|
|
24
|
+
created_time: "2023-01-12T20:25:00.000Z",
|
|
25
|
+
last_edited_time: "2023-01-12T20:36:00.000Z",
|
|
26
|
+
created_by: {
|
|
27
|
+
object: "user",
|
|
28
|
+
id: "11fb7f16-0560-4aee-ab88-ed75a850cfc4",
|
|
29
|
+
},
|
|
30
|
+
last_edited_by: {
|
|
31
|
+
object: "user",
|
|
32
|
+
id: "11fb7f16-0560-4aee-ab88-ed75a850cfc4",
|
|
33
|
+
},
|
|
34
|
+
has_children: false,
|
|
35
|
+
archived: false,
|
|
36
|
+
type: "paragraph",
|
|
37
|
+
paragraph: {
|
|
38
|
+
rich_text: [
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: { content: "2 < 3 > 1", link: null },
|
|
42
|
+
annotations: {
|
|
43
|
+
bold: true,
|
|
44
|
+
italic: true,
|
|
45
|
+
strikethrough: false,
|
|
46
|
+
underline: false,
|
|
47
|
+
code: false,
|
|
48
|
+
color: "yellow",
|
|
49
|
+
},
|
|
50
|
+
plain_text: "2 < 3 > 1",
|
|
51
|
+
href: null,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
color: "default",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
object: "block",
|
|
59
|
+
id: "5bd7b925-9d87-435a-bbfb-df97e07e7d39",
|
|
60
|
+
parent: {
|
|
61
|
+
type: "page_id",
|
|
62
|
+
page_id: "a623852e-3552-40cf-89a9-7e3adbc07e9c",
|
|
63
|
+
},
|
|
64
|
+
created_time: "2023-01-12T20:26:00.000Z",
|
|
65
|
+
last_edited_time: "2023-01-12T20:36:00.000Z",
|
|
66
|
+
created_by: {
|
|
67
|
+
object: "user",
|
|
68
|
+
id: "11fb7f16-0560-4aee-ab88-ed75a850cfc4",
|
|
69
|
+
},
|
|
70
|
+
last_edited_by: {
|
|
71
|
+
object: "user",
|
|
72
|
+
id: "11fb7f16-0560-4aee-ab88-ed75a850cfc4",
|
|
73
|
+
},
|
|
74
|
+
has_children: false,
|
|
75
|
+
archived: false,
|
|
76
|
+
type: "paragraph",
|
|
77
|
+
paragraph: {
|
|
78
|
+
rich_text: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: { content: "This is code: if(1 < 3)", link: null },
|
|
82
|
+
annotations: {
|
|
83
|
+
bold: false,
|
|
84
|
+
italic: false,
|
|
85
|
+
strikethrough: false,
|
|
86
|
+
underline: false,
|
|
87
|
+
code: true,
|
|
88
|
+
color: "default",
|
|
89
|
+
},
|
|
90
|
+
plain_text: "This is code: if(1 < 3)",
|
|
91
|
+
href: null,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
color: "default",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
object: "block",
|
|
99
|
+
id: "bdd2569a-8b0d-450e-ba03-4315e5f726b8",
|
|
100
|
+
parent: {
|
|
101
|
+
type: "page_id",
|
|
102
|
+
page_id: "a623852e-3552-40cf-89a9-7e3adbc07e9c",
|
|
103
|
+
},
|
|
104
|
+
created_time: "2023-01-12T20:27:00.000Z",
|
|
105
|
+
last_edited_time: "2023-01-12T20:27:00.000Z",
|
|
106
|
+
created_by: {
|
|
107
|
+
object: "user",
|
|
108
|
+
id: "11fb7f16-0560-4aee-ab88-ed75a850cfc4",
|
|
109
|
+
},
|
|
110
|
+
last_edited_by: {
|
|
111
|
+
object: "user",
|
|
112
|
+
id: "11fb7f16-0560-4aee-ab88-ed75a850cfc4",
|
|
113
|
+
},
|
|
114
|
+
has_children: false,
|
|
115
|
+
archived: false,
|
|
116
|
+
type: "paragraph",
|
|
117
|
+
paragraph: { rich_text: [], color: "default" },
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
});
|
|
121
|
+
test("smoketest ", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
122
|
+
const config = { plugins: [EscapeHtmlBlockModifier_1.standardEscapeHtmlBlockModifier] };
|
|
123
|
+
let results = yield (0, pluginTestRun_1.blocksToMarkdown)(config, blocks);
|
|
124
|
+
// shouldn't escape inside a code block
|
|
125
|
+
expect(results).toContain("This is code: if(1 < 3)");
|
|
126
|
+
// should escape outside a code block
|
|
127
|
+
expect(results).toContain("2 < 3 > 1");
|
|
128
|
+
// that line is also bold and italic
|
|
129
|
+
expect(results).toContain("_**2 < 3 > 1**_");
|
|
130
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const pluginTestRun_1 = require("./pluginTestRun");
|
|
13
|
+
const HeadingTransformer_1 = require("./HeadingTransformer");
|
|
14
|
+
test("Adds anchor to headings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
//setLogLevel("verbose");
|
|
16
|
+
const headingBlockId = "86f746f4-1c79-4ba1-a2f6-a1d59c2f9d23";
|
|
17
|
+
const config = { plugins: [HeadingTransformer_1.standardHeadingTransformer] };
|
|
18
|
+
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
19
|
+
{
|
|
20
|
+
object: "block",
|
|
21
|
+
id: headingBlockId,
|
|
22
|
+
type: "heading_1",
|
|
23
|
+
heading_1: {
|
|
24
|
+
rich_text: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: { content: "Heading One", link: null },
|
|
28
|
+
annotations: {
|
|
29
|
+
bold: false,
|
|
30
|
+
italic: false,
|
|
31
|
+
strikethrough: false,
|
|
32
|
+
underline: false,
|
|
33
|
+
code: false,
|
|
34
|
+
color: "default",
|
|
35
|
+
},
|
|
36
|
+
plain_text: "Heading One",
|
|
37
|
+
href: null,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
is_toggleable: false,
|
|
41
|
+
color: "default",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
expect(result.trim()).toBe(`# Heading One {#${headingBlockId.replaceAll("-", "")}}`);
|
|
46
|
+
}));
|