@sillsdev/docu-notion 0.12.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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +135 -0
  3. package/dist/FlatGuidLayoutStrategy.d.ts +6 -0
  4. package/dist/FlatGuidLayoutStrategy.js +25 -0
  5. package/dist/HierarchicalNamedLayoutStrategy.d.ts +7 -0
  6. package/dist/HierarchicalNamedLayoutStrategy.js +80 -0
  7. package/dist/LayoutStrategy.d.ts +12 -0
  8. package/dist/LayoutStrategy.js +83 -0
  9. package/dist/MakeImagePersistencePlan.d.ts +2 -0
  10. package/dist/MakeImagePersistencePlan.js +66 -0
  11. package/dist/NotionImage-CaptionReading.spec.d.ts +1 -0
  12. package/dist/NotionImage-CaptionReading.spec.js +233 -0
  13. package/dist/NotionPage.d.ts +44 -0
  14. package/dist/NotionPage.js +194 -0
  15. package/dist/config/configuration.d.ts +5 -0
  16. package/dist/config/configuration.js +86 -0
  17. package/dist/config/default.docunotion.config.d.ts +3 -0
  18. package/dist/config/default.docunotion.config.js +37 -0
  19. package/dist/images.d.ts +24 -0
  20. package/dist/images.js +230 -0
  21. package/dist/index.d.ts +7 -0
  22. package/dist/index.js +37 -0
  23. package/dist/log.d.ts +11 -0
  24. package/dist/log.js +61 -0
  25. package/dist/makeImagePersistencePlan.spec.d.ts +1 -0
  26. package/dist/makeImagePersistencePlan.spec.js +35 -0
  27. package/dist/notion-styles.css +58 -0
  28. package/dist/plugins/CalloutTransformer.d.ts +24 -0
  29. package/dist/plugins/CalloutTransformer.js +88 -0
  30. package/dist/plugins/CalloutTransformer.spec.d.ts +1 -0
  31. package/dist/plugins/CalloutTransformer.spec.js +199 -0
  32. package/dist/plugins/ColumnListTransformer.d.ts +2 -0
  33. package/dist/plugins/ColumnListTransformer.js +34 -0
  34. package/dist/plugins/ColumnTransformer.d.ts +2 -0
  35. package/dist/plugins/ColumnTransformer.js +67 -0
  36. package/dist/plugins/EscapeHtmlBlockModifier.d.ts +2 -0
  37. package/dist/plugins/EscapeHtmlBlockModifier.js +41 -0
  38. package/dist/plugins/EscapeHtmlBlockModifier.spec.d.ts +1 -0
  39. package/dist/plugins/EscapeHtmlBlockModifier.spec.js +130 -0
  40. package/dist/plugins/HeadingTranformer.spec.d.ts +1 -0
  41. package/dist/plugins/HeadingTranformer.spec.js +46 -0
  42. package/dist/plugins/HeadingTransformer.d.ts +2 -0
  43. package/dist/plugins/HeadingTransformer.js +63 -0
  44. package/dist/plugins/NumberedListTransformer.d.ts +2 -0
  45. package/dist/plugins/NumberedListTransformer.js +55 -0
  46. package/dist/plugins/NumberedListTransformer.spec.d.ts +1 -0
  47. package/dist/plugins/NumberedListTransformer.spec.js +86 -0
  48. package/dist/plugins/TableTransformer.d.ts +5 -0
  49. package/dist/plugins/TableTransformer.js +70 -0
  50. package/dist/plugins/embedTweaks.d.ts +5 -0
  51. package/dist/plugins/embedTweaks.js +46 -0
  52. package/dist/plugins/embedTweaks.spec.d.ts +1 -0
  53. package/dist/plugins/embedTweaks.spec.js +230 -0
  54. package/dist/plugins/externalLinks.d.ts +2 -0
  55. package/dist/plugins/externalLinks.js +26 -0
  56. package/dist/plugins/externalLinks.spec.d.ts +1 -0
  57. package/dist/plugins/externalLinks.spec.js +132 -0
  58. package/dist/plugins/internalLinks.d.ts +6 -0
  59. package/dist/plugins/internalLinks.js +78 -0
  60. package/dist/plugins/internalLinks.spec.d.ts +1 -0
  61. package/dist/plugins/internalLinks.spec.js +442 -0
  62. package/dist/plugins/pluginTestRun.d.ts +10 -0
  63. package/dist/plugins/pluginTestRun.js +248 -0
  64. package/dist/plugins/pluginTypes.d.ts +42 -0
  65. package/dist/plugins/pluginTypes.js +2 -0
  66. package/dist/pull.d.ts +12 -0
  67. package/dist/pull.js +253 -0
  68. package/dist/run.d.ts +1 -0
  69. package/dist/run.js +35 -0
  70. package/dist/transform.d.ts +6 -0
  71. package/dist/transform.js +195 -0
  72. package/dist/types.d.ts +8 -0
  73. package/dist/types.js +2 -0
  74. package/package.json +96 -0
@@ -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,2 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const standardColumnListTransformer: IPlugin;
@@ -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,2 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const standardColumnTransformer: IPlugin;
@@ -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,2 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const standardEscapeHtmlBlockModifier: IPlugin;
@@ -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 &lt; and &gt;
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 "&lt;" or "&gt;".
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("<", "&lt;")
37
+ .replaceAll(">", "&gt;");
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 &lt; 3 &gt; 1");
128
+ // that line is also bold and italic
129
+ expect(results).toContain("_**2 &lt; 3 &gt; 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
+ }));
@@ -0,0 +1,2 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const standardHeadingTransformer: IPlugin;
@@ -0,0 +1,63 @@
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.standardHeadingTransformer = void 0;
13
+ const log_1 = require("../log");
14
+ // Makes links to headings work in docusaurus
15
+ // https://github.com/sillsdev/docu-notion/issues/20
16
+ function headingTransformer(notionToMarkdown, block) {
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ // First, remove the prefix we added to the heading type
19
+ block.type = block.type.replace("DN_", "");
20
+ const markdown = yield notionToMarkdown.blockToMarkdown(block);
21
+ (0, log_1.logDebug)("headingTransformer, markdown of a heading before adding id", markdown);
22
+ // To make heading links work in docusaurus, we append an id. E.g.
23
+ // ### Hello World {#my-explicit-id}
24
+ // See https://docusaurus.io/docs/markdown-features/toc#heading-ids.
25
+ // For some reason, inline links come in without the dashes, so we have to strip
26
+ // dashes here to match them.
27
+ //console.log("block.id", block.id)
28
+ const blockIdWithoutDashes = block.id.replaceAll("-", "");
29
+ // Finally, append the block id so that it can be the target of a link.
30
+ return `${markdown} {#${blockIdWithoutDashes}}`;
31
+ });
32
+ }
33
+ exports.standardHeadingTransformer = {
34
+ name: "standardHeadingTransformer",
35
+ // AP wrote: We have to do this because if
36
+ // we simply set a custom transformer to heading_n, it will keep
37
+ // recursively calling this code, with blockToMarkdown using the custom transformer
38
+ // over and over. Instead, we want blockToMarkdown to give us the normal
39
+ // result, to which we will append the block ID to enable heading links.
40
+ notionBlockModifications: [
41
+ {
42
+ modify: (block) => {
43
+ // "as any" needed because we're putting a value in that is not allowed by the real type
44
+ block.type = block.type.replace("heading", "DN_heading");
45
+ },
46
+ },
47
+ ],
48
+ // then when it comes time to do markdown conversions, we'll get called for each of these
49
+ notionToMarkdownTransforms: [
50
+ {
51
+ type: "DN_heading_1",
52
+ getStringFromBlock: (context, block) => headingTransformer(context.notionToMarkdown, block),
53
+ },
54
+ {
55
+ type: "DN_heading_2",
56
+ getStringFromBlock: (context, block) => headingTransformer(context.notionToMarkdown, block),
57
+ },
58
+ {
59
+ type: "DN_heading_3",
60
+ getStringFromBlock: (context, block) => headingTransformer(context.notionToMarkdown, block),
61
+ },
62
+ ],
63
+ };
@@ -0,0 +1,2 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const standardNumberedListTransformer: IPlugin;