@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,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.standardNumberedListTransformer = void 0;
4
+ // This is mostly what notion-to-markdown would normally do with a block of type
5
+ // numbered_list_item. A patch is documented at the end.
6
+ function numberedListTransformer(notionToMarkdown, block) {
7
+ var _a, _b, _c;
8
+ //console.log("got numbered list block " + JSON.stringify(block));
9
+ // In this case typescript is not able to index the types properly, hence ignoring the error
10
+ // @ts-ignore
11
+ const blockContent =
12
+ // @ts-ignore
13
+ ((_a = block.numbered_list_item) === null || _a === void 0 ? void 0 : _a.text) || ((_b = block.numbered_list_item) === null || _b === void 0 ? void 0 : _b.rich_text) || [];
14
+ let parsedData = "";
15
+ blockContent.map((content) => {
16
+ const annotations = content.annotations;
17
+ let plain_text = content.plain_text;
18
+ plain_text = notionToMarkdown.annotatePlainText(plain_text, annotations);
19
+ if (content["href"]) {
20
+ plain_text = `[${plain_text}](${content["href"]})`;
21
+ }
22
+ parsedData += plain_text;
23
+ });
24
+ // There is code in notion-to-md which attempts to set an incrementing number
25
+ // on each of these. Somehow it fails; in my testing, block.numbered_list_item never
26
+ // has a field 'number'. But we don't actually need incrementing numbers;
27
+ // markdown will do the numbering if we just make something that looks like
28
+ // a member of a numbered list by starting with number followed by period and space.
29
+ // I'm keeping the original code in case notion-to-md gets fixed and there is actually
30
+ // some reason to use incrementing numbers (it would at least make the markdown more
31
+ // human-readable); but this at least works.
32
+ // A problem is that in notion, a numbered list may continue after some intermediate
33
+ // content. To achieve this in markdown, we'd need to indent the intermediate content
34
+ // by a tab. Not only is it difficult to do this, but there appears to be no way to
35
+ // know whether we should. The data we get from notion doesn't include the item number,
36
+ // and its parent is the page rather than a particular list. So there is no way I can
37
+ // see to distinguish a list continuation from a new list. The code here will leave
38
+ // it up to markdown to decide whether to start a new list; I believe it will do so
39
+ // if it sees any intervening lines that are not list items.
40
+ let num = (_c = block.numbered_list_item) === null || _c === void 0 ? void 0 : _c.number;
41
+ //console.log("got number " + num?.toString());
42
+ if (!num) {
43
+ num = 1;
44
+ }
45
+ return Promise.resolve(`${num}. ${parsedData.trim()}`);
46
+ }
47
+ exports.standardNumberedListTransformer = {
48
+ name: "standardNumberedListTransformer",
49
+ notionToMarkdownTransforms: [
50
+ {
51
+ type: "numbered_list_item",
52
+ getStringFromBlock: (context, block) => numberedListTransformer(context.notionToMarkdown, block),
53
+ },
54
+ ],
55
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,86 @@
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 NumberedListTransformer_1 = require("./NumberedListTransformer");
14
+ let block;
15
+ beforeEach(() => {
16
+ block = {
17
+ has_children: false,
18
+ archived: false,
19
+ type: "callout",
20
+ callout: {
21
+ rich_text: [
22
+ {
23
+ type: "text",
24
+ text: { content: "This is information callout", link: null },
25
+ annotations: {
26
+ bold: false,
27
+ italic: false,
28
+ strikethrough: false,
29
+ underline: false,
30
+ code: false,
31
+ color: "default",
32
+ },
33
+ plain_text: "This is the callout",
34
+ href: null,
35
+ },
36
+ ],
37
+ icon: { type: "emoji", emoji: "ℹ️" },
38
+ color: "gray_background",
39
+ },
40
+ };
41
+ });
42
+ test("external link inside numbered list, italic preserved", () => __awaiter(void 0, void 0, void 0, function* () {
43
+ const config = { plugins: [NumberedListTransformer_1.standardNumberedListTransformer] };
44
+ const results = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
45
+ {
46
+ type: "numbered_list_item",
47
+ numbered_list_item: {
48
+ rich_text: [
49
+ {
50
+ type: "text",
51
+ text: { content: "link ", link: null },
52
+ annotations: {
53
+ bold: false,
54
+ italic: false,
55
+ strikethrough: false,
56
+ underline: false,
57
+ code: false,
58
+ color: "default",
59
+ },
60
+ plain_text: "link ",
61
+ href: null,
62
+ },
63
+ {
64
+ type: "text",
65
+ text: {
66
+ content: "github",
67
+ link: { url: "https://github.com" },
68
+ },
69
+ annotations: {
70
+ bold: false,
71
+ italic: true,
72
+ strikethrough: false,
73
+ underline: false,
74
+ code: false,
75
+ color: "default",
76
+ },
77
+ plain_text: "github",
78
+ href: "https://github.com",
79
+ },
80
+ ],
81
+ color: "default",
82
+ },
83
+ },
84
+ ]);
85
+ expect(results.trim()).toBe(`1. link [_github_](https://github.com)`);
86
+ }));
@@ -0,0 +1,5 @@
1
+ import { NotionToMarkdown } from "notion-to-md";
2
+ import { IGetBlockChildrenFn, IPlugin } from "./pluginTypes";
3
+ import { NotionBlock } from "../types";
4
+ export declare function tableTransformer(notionToMarkdown: NotionToMarkdown, getBlockChildren: IGetBlockChildrenFn, block: NotionBlock): Promise<string>;
5
+ export declare const standardTableTransformer: IPlugin;
@@ -0,0 +1,70 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.standardTableTransformer = exports.tableTransformer = void 0;
16
+ const markdown_table_1 = __importDefault(require("markdown-table"));
17
+ // This is mostly a copy of the table handler from notion-to-md. The change is to handle newlines in the
18
+ // notion cell content.
19
+ function tableTransformer(notionToMarkdown, getBlockChildren, block) {
20
+ return __awaiter(this, void 0, void 0, function* () {
21
+ const { id, has_children } = block;
22
+ const tableArr = [];
23
+ if (has_children) {
24
+ const tableRows = yield getBlockChildren(id);
25
+ // console.log(">>", tableRows);
26
+ const rowsPromise = tableRows === null || tableRows === void 0 ? void 0 : tableRows.map((row) => __awaiter(this, void 0, void 0, function* () {
27
+ const { type } = row;
28
+ const cells = row[type]["cells"];
29
+ /**
30
+ * this is more like a hack since matching the type text was
31
+ * difficult. So converting each cell to paragraph type to
32
+ * reuse the blockToMarkdown function
33
+ */
34
+ const cellStringPromise = cells.map((cell) => __awaiter(this, void 0, void 0, function* () {
35
+ return yield notionToMarkdown.blockToMarkdown({
36
+ type: "paragraph",
37
+ paragraph: { rich_text: cell },
38
+ });
39
+ }));
40
+ const cellStringArrRaw = yield Promise.all(cellStringPromise);
41
+ // This is our patch to the original notion-to-md code.
42
+ const cellStringArr = cellStringArrRaw.map(c => c
43
+ // Trailing newlines are almost certainly not wanted, and converting to br's gives weird results
44
+ .replace(/[\r\n]+$/, "")
45
+ // Preserving line breaks within cells can't be done in stock markdown. Since we're producing
46
+ // mdx, which supports embedded HTML, we can handle it with <br/>.
47
+ // I'm not sure exactly what line breaks might occur in the input, depending on platform,
48
+ // so handle all the common cases.
49
+ .replaceAll("\r\n", "<br/>")
50
+ .replaceAll("\n", "<br/>")
51
+ .replaceAll("\r", "<br/>"));
52
+ // console.log("~~", cellStringArr);
53
+ tableArr.push(cellStringArr);
54
+ // console.log(tableArr);
55
+ }));
56
+ yield Promise.all(rowsPromise || []);
57
+ }
58
+ return (0, markdown_table_1.default)(tableArr);
59
+ });
60
+ }
61
+ exports.tableTransformer = tableTransformer;
62
+ exports.standardTableTransformer = {
63
+ name: "standardTableTransformer",
64
+ notionToMarkdownTransforms: [
65
+ {
66
+ type: "table",
67
+ getStringFromBlock: (context, block) => tableTransformer(context.notionToMarkdown, context.getBlockChildren, block),
68
+ },
69
+ ],
70
+ };
@@ -0,0 +1,5 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const gifEmbed: IPlugin;
3
+ export declare const imgurGifEmbed: IPlugin;
4
+ export declare const youtubeEmbed: IPlugin;
5
+ export declare const vimeoEmbed: IPlugin;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.vimeoEmbed = exports.youtubeEmbed = exports.imgurGifEmbed = exports.gifEmbed = void 0;
4
+ exports.gifEmbed = {
5
+ name: "gif",
6
+ regexMarkdownModifications: [
7
+ {
8
+ // I once saw a gif coming from Notion that wasn't a full
9
+ // url, which wouldn't work, hence the "http" requirement
10
+ regex: /\[.*\]\((http.*(\.(gif|GIF)))\)/,
11
+ replacementPattern: `![]($1)`,
12
+ },
13
+ ],
14
+ };
15
+ exports.imgurGifEmbed = {
16
+ name: "imgur",
17
+ regexMarkdownModifications: [
18
+ {
19
+ regex: /\[.*\]\((.*imgur\.com\/.*)\)/,
20
+ // imgur links to gifs need a .gif at the end, but the url they give you doesn't have one.
21
+ replacementPattern: `![]($1.gif)`,
22
+ },
23
+ ],
24
+ };
25
+ exports.youtubeEmbed = {
26
+ name: "youtube",
27
+ regexMarkdownModifications: [
28
+ {
29
+ regex: /\[.*\]\((.*youtube\.com\/watch.*)\)/,
30
+ imports: [`import ReactPlayer from "react-player";`],
31
+ replacementPattern: `<ReactPlayer controls url="$1" />`,
32
+ },
33
+ ],
34
+ };
35
+ exports.vimeoEmbed = {
36
+ name: "vimeo",
37
+ regexMarkdownModifications: [
38
+ {
39
+ regex: /\[.*\]\((https:\/\/.*vimeo.*)\)/,
40
+ // we use to have the following, but the above should handle both the player an not-player urls.
41
+ //regex: /\[.*\]\((.*player\.vimeo.*)\)/gm, // player.vimeo
42
+ imports: [`import ReactPlayer from "react-player";`],
43
+ replacementPattern: `<ReactPlayer controls url="$1" />`,
44
+ },
45
+ ],
46
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,230 @@
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 log_1 = require("../log");
13
+ const pluginTestRun_1 = require("./pluginTestRun");
14
+ const embedTweaks_1 = require("./embedTweaks");
15
+ test("youtube", () => __awaiter(void 0, void 0, void 0, function* () {
16
+ const config = { plugins: [embedTweaks_1.youtubeEmbed] };
17
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
18
+ {
19
+ object: "block",
20
+ id: "e6ddd1d4-36d4-4925-94c1-5dff4662c1f3",
21
+ has_children: false,
22
+ archived: false,
23
+ type: "video",
24
+ video: {
25
+ caption: [
26
+ {
27
+ type: "text",
28
+ text: {
29
+ content: "A video about editing in Notion",
30
+ link: null,
31
+ },
32
+ plain_text: "A video about editing in Notion",
33
+ href: null,
34
+ },
35
+ ],
36
+ type: "external",
37
+ external: { url: "https://www.youtube.com/watch?v=FXIrojSK3Jo" },
38
+ },
39
+ },
40
+ ]);
41
+ expect(result).toContain(`import ReactPlayer from "react-player";`);
42
+ expect(result).toContain(`<ReactPlayer controls url="https://www.youtube.com/watch?v=FXIrojSK3Jo" />`);
43
+ }));
44
+ test("vimeo", () => __awaiter(void 0, void 0, void 0, function* () {
45
+ (0, log_1.setLogLevel)("verbose");
46
+ const config = { plugins: [embedTweaks_1.vimeoEmbed] };
47
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
48
+ {
49
+ object: "block",
50
+ id: "39ff83a3-2fb5-4411-a715-960656a177ff",
51
+ type: "video",
52
+ video: {
53
+ caption: [],
54
+ type: "external",
55
+ external: { url: "https://vimeo.com/4613611xx" },
56
+ },
57
+ },
58
+ ]);
59
+ expect(result).toContain(`import ReactPlayer from "react-player";`);
60
+ expect(result).toContain(`<ReactPlayer controls url="https://vimeo.com/4613611xx" />`);
61
+ }));
62
+ test("imgur", () => __awaiter(void 0, void 0, void 0, function* () {
63
+ (0, log_1.setLogLevel)("verbose");
64
+ const config = { plugins: [embedTweaks_1.imgurGifEmbed] };
65
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
66
+ {
67
+ object: "block",
68
+ id: "e36710d8-98ad-40dc-b41b-b376ebdd6894",
69
+ type: "bookmark",
70
+ bookmark: { caption: [], url: "https://imgur.com/gallery/U8TTNuI" },
71
+ },
72
+ ]);
73
+ expect(result.trim()).toBe(`![](https://imgur.com/gallery/U8TTNuI.gif)`);
74
+ }));
75
+ test("gif", () => __awaiter(void 0, void 0, void 0, function* () {
76
+ (0, log_1.setLogLevel)("verbose");
77
+ const config = { plugins: [embedTweaks_1.gifEmbed] };
78
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
79
+ {
80
+ object: "block",
81
+ id: "e36710d8-98ad-40dc-b41b-b376ebdd6894",
82
+ type: "bookmark",
83
+ bookmark: {
84
+ caption: [],
85
+ url: "https://en.wikipedia.org/wiki/GIF#/media/File:Rotating_earth_(large).gif",
86
+ },
87
+ },
88
+ ]);
89
+ expect(result.trim()).toBe(`![](https://en.wikipedia.org/wiki/GIF#/media/File:Rotating_earth_(large).gif)`);
90
+ }));
91
+ test("tweaks are not applied inside code blocks", () => __awaiter(void 0, void 0, void 0, function* () {
92
+ (0, log_1.setLogLevel)("verbose");
93
+ const p = {
94
+ name: "test",
95
+ regexMarkdownModifications: [
96
+ {
97
+ regex: /find/,
98
+ replacementPattern: `found`,
99
+ },
100
+ ],
101
+ };
102
+ const config = { plugins: [p] };
103
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
104
+ {
105
+ type: "code",
106
+ code: {
107
+ caption: [],
108
+ rich_text: [
109
+ {
110
+ type: "text",
111
+ text: {
112
+ content: "don't find me",
113
+ link: null,
114
+ },
115
+ annotations: {
116
+ bold: false,
117
+ italic: false,
118
+ strikethrough: false,
119
+ underline: false,
120
+ code: false,
121
+ color: "default",
122
+ },
123
+ plain_text: "don't find me",
124
+ href: null,
125
+ },
126
+ ],
127
+ language: "",
128
+ },
129
+ },
130
+ {
131
+ type: "paragraph",
132
+ paragraph: {
133
+ rich_text: [
134
+ {
135
+ type: "text",
136
+ text: { content: "find this", link: null },
137
+ annotations: {
138
+ bold: false,
139
+ italic: false,
140
+ strikethrough: false,
141
+ underline: false,
142
+ code: true,
143
+ color: "default",
144
+ },
145
+ plain_text: "find this",
146
+ href: null,
147
+ },
148
+ ],
149
+ },
150
+ },
151
+ ]);
152
+ // we should not change the code one
153
+ expect(result.trim()).toContain("don't find me");
154
+ // but we should change the non-code block one
155
+ expect(result.trim()).toContain("found this");
156
+ }));
157
+ test("simplest possible", () => __awaiter(void 0, void 0, void 0, function* () {
158
+ (0, log_1.setLogLevel)("verbose");
159
+ const p = {
160
+ name: "test",
161
+ regexMarkdownModifications: [
162
+ {
163
+ regex: /find/,
164
+ replacementPattern: `found`,
165
+ },
166
+ ],
167
+ };
168
+ const config = { plugins: [p] };
169
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
170
+ {
171
+ type: "paragraph",
172
+ paragraph: {
173
+ rich_text: [
174
+ {
175
+ type: "text",
176
+ text: { content: "find this", link: null },
177
+ annotations: {
178
+ bold: false,
179
+ italic: false,
180
+ strikethrough: false,
181
+ underline: false,
182
+ code: true,
183
+ color: "default",
184
+ },
185
+ plain_text: "find this",
186
+ href: null,
187
+ },
188
+ ],
189
+ },
190
+ },
191
+ ]);
192
+ expect(result.trim()).toContain("found this");
193
+ }));
194
+ test("use match in output", () => __awaiter(void 0, void 0, void 0, function* () {
195
+ (0, log_1.setLogLevel)("verbose");
196
+ const p = {
197
+ name: "test",
198
+ regexMarkdownModifications: [
199
+ {
200
+ regex: /(find)/,
201
+ replacementPattern: `found $1`,
202
+ },
203
+ ],
204
+ };
205
+ const config = { plugins: [p] };
206
+ const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
207
+ {
208
+ type: "paragraph",
209
+ paragraph: {
210
+ rich_text: [
211
+ {
212
+ type: "text",
213
+ text: { content: "find this", link: null },
214
+ annotations: {
215
+ bold: false,
216
+ italic: false,
217
+ strikethrough: false,
218
+ underline: false,
219
+ code: true,
220
+ color: "default",
221
+ },
222
+ plain_text: "find this",
223
+ href: null,
224
+ },
225
+ ],
226
+ },
227
+ },
228
+ ]);
229
+ expect(result.trim()).toContain("found find");
230
+ }));
@@ -0,0 +1,2 @@
1
+ import { IPlugin } from "./pluginTypes";
2
+ export declare const standardExternalLinkConversion: IPlugin;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.standardExternalLinkConversion = void 0;
4
+ const log_1 = require("../log");
5
+ exports.standardExternalLinkConversion = {
6
+ name: "standard external link conversion",
7
+ linkModifier: {
8
+ match: /\[.*\]\(http.*\)/,
9
+ convert: (context, markdownLink) => {
10
+ const linkRegExp = /\[([^\]]+)?\]\((http.*)\)/;
11
+ const match = linkRegExp.exec(markdownLink);
12
+ if (match === null) {
13
+ (0, log_1.error)(`[standardExternalLinkConversion] Could not parse link ${markdownLink}`);
14
+ return markdownLink;
15
+ }
16
+ const label = match[1];
17
+ const url = match[2];
18
+ if (label === "bookmark") {
19
+ const replacement = `[${url}](${url})`;
20
+ (0, log_1.warning)(`[standardExternalLinkConversion] Found Notion "Bookmark" link. In Notion this would show as an embed. The best docu-notion can do at the moment is replace "Bookmark" with the actual URL: ${replacement}`);
21
+ return replacement;
22
+ }
23
+ return `[${label}](${url})`;
24
+ },
25
+ },
26
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,132 @@
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 log_1 = require("../log");
13
+ const pluginTestRun_1 = require("./pluginTestRun");
14
+ const externalLinks_1 = require("./externalLinks");
15
+ // If you paste a link in notion and then choose "Create bookmark", the markdown
16
+ // would normally be [bookmark](https://example.com)]. Instead of seeing "bookmark",
17
+ // we change to the url.
18
+ test("links turned into bookmarks", () => __awaiter(void 0, void 0, void 0, function* () {
19
+ (0, log_1.setLogLevel)("debug");
20
+ const results = yield getMarkdown({
21
+ type: "bookmark",
22
+ bookmark: { caption: [], url: "https://github.com" },
23
+ });
24
+ expect(results.trim()).toBe("[https://github.com](https://github.com)");
25
+ }));
26
+ test("external link inside callout", () => __awaiter(void 0, void 0, void 0, function* () {
27
+ const results = yield getMarkdown({
28
+ type: "callout",
29
+ callout: {
30
+ rich_text: [
31
+ {
32
+ type: "text",
33
+ text: { content: "Callouts inline ", link: null },
34
+ annotations: {
35
+ bold: false,
36
+ italic: false,
37
+ strikethrough: false,
38
+ underline: false,
39
+ code: false,
40
+ color: "default",
41
+ },
42
+ plain_text: "Callouts inline ",
43
+ href: null,
44
+ },
45
+ {
46
+ type: "text",
47
+ text: {
48
+ content: "great page",
49
+ link: { url: `https://github.com` },
50
+ },
51
+ annotations: {
52
+ bold: false,
53
+ italic: false,
54
+ strikethrough: false,
55
+ underline: false,
56
+ code: false,
57
+ color: "default",
58
+ },
59
+ plain_text: "great page",
60
+ href: `https://github.com`,
61
+ },
62
+ {
63
+ type: "text",
64
+ text: { content: ".", link: null },
65
+ annotations: {
66
+ bold: false,
67
+ italic: false,
68
+ strikethrough: false,
69
+ underline: false,
70
+ code: false,
71
+ color: "default",
72
+ },
73
+ plain_text: ".",
74
+ href: null,
75
+ },
76
+ ],
77
+ icon: { type: "emoji", emoji: "⚠️" },
78
+ color: "gray_background",
79
+ },
80
+ });
81
+ expect(results.trim()).toBe("> ⚠️ Callouts inline [great page](https://github.com).");
82
+ }));
83
+ test("inline links to external site", () => __awaiter(void 0, void 0, void 0, function* () {
84
+ const results = yield getMarkdown({
85
+ type: "paragraph",
86
+ paragraph: {
87
+ rich_text: [
88
+ {
89
+ type: "text",
90
+ text: { content: "Inline ", link: null },
91
+ annotations: {
92
+ bold: false,
93
+ italic: false,
94
+ strikethrough: false,
95
+ underline: false,
96
+ code: false,
97
+ color: "default",
98
+ },
99
+ plain_text: "Inline ",
100
+ href: null,
101
+ },
102
+ {
103
+ type: "text",
104
+ text: {
105
+ content: "github",
106
+ link: { url: "https://github.com" },
107
+ },
108
+ annotations: {
109
+ bold: false,
110
+ italic: false,
111
+ strikethrough: false,
112
+ underline: false,
113
+ code: false,
114
+ color: "default",
115
+ },
116
+ plain_text: "github",
117
+ href: "https://github.com",
118
+ },
119
+ ],
120
+ color: "default",
121
+ },
122
+ });
123
+ expect(results.trim()).toBe("Inline [github](https://github.com)");
124
+ }));
125
+ function getMarkdown(block) {
126
+ return __awaiter(this, void 0, void 0, function* () {
127
+ const config = {
128
+ plugins: [externalLinks_1.standardExternalLinkConversion],
129
+ };
130
+ return yield (0, pluginTestRun_1.oneBlockToMarkdown)(config, block);
131
+ });
132
+ }