@sillsdev/docu-notion 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/NotionPage.d.ts +1 -0
- package/dist/NotionPage.js +37 -2
- package/dist/NotionPage.spec.d.ts +1 -0
- package/dist/NotionPage.spec.js +143 -0
- package/dist/plugins/internalLinks.d.ts +2 -1
- package/dist/plugins/internalLinks.js +28 -4
- package/dist/plugins/internalLinks.spec.js +64 -1
- package/dist/plugins/mermaidLinkPlugin.spec.d.ts +1 -0
- package/dist/plugins/mermaidLinkPlugin.spec.js +77 -0
- package/dist/plugins/pluginTestRun.js +4 -0
- package/dist/plugins/pluginTypes.d.ts +3 -1
- package/dist/pull.js +3 -1
- package/dist/transform.js +12 -7
- package/package.json +20 -23
package/dist/NotionPage.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare class NotionPage {
|
|
|
30
30
|
get status(): string | undefined;
|
|
31
31
|
getPlainTextProperty(property: string, defaultIfEmpty: string): string;
|
|
32
32
|
getSelectProperty(property: string): string | undefined;
|
|
33
|
+
getDateProperty(property: string, defaultIfEmpty: string, start?: boolean): string;
|
|
33
34
|
getContentInfo(children: ListBlockChildrenResponseResults): Promise<{
|
|
34
35
|
childPageIdsAndOrder: {
|
|
35
36
|
id: string;
|
package/dist/NotionPage.js
CHANGED
|
@@ -83,7 +83,8 @@ class NotionPage {
|
|
|
83
83
|
const explicitSlug = this.getPlainTextProperty("Slug", "");
|
|
84
84
|
if (explicitSlug) {
|
|
85
85
|
if (explicitSlug === "/")
|
|
86
|
-
return explicitSlug;
|
|
86
|
+
return explicitSlug;
|
|
87
|
+
// the root page
|
|
87
88
|
else
|
|
88
89
|
return ("/" +
|
|
89
90
|
encodeURIComponent(explicitSlug
|
|
@@ -136,6 +137,10 @@ class NotionPage {
|
|
|
136
137
|
{
|
|
137
138
|
...
|
|
138
139
|
"plain_text": "Intro",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
...
|
|
143
|
+
"plain_text": " to Notion",
|
|
139
144
|
}
|
|
140
145
|
]
|
|
141
146
|
*/
|
|
@@ -148,7 +153,9 @@ class NotionPage {
|
|
|
148
153
|
const textArray = p[p.type];
|
|
149
154
|
//console.log("textarray:" + JSON.stringify(textArray, null, 2));
|
|
150
155
|
return textArray && textArray.length
|
|
151
|
-
? textArray
|
|
156
|
+
? textArray
|
|
157
|
+
.map((item) => item.plain_text)
|
|
158
|
+
.join("")
|
|
152
159
|
: defaultIfEmpty;
|
|
153
160
|
}
|
|
154
161
|
getSelectProperty(property) {
|
|
@@ -173,6 +180,34 @@ class NotionPage {
|
|
|
173
180
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
174
181
|
return ((_b = p.select) === null || _b === void 0 ? void 0 : _b.name) || undefined;
|
|
175
182
|
}
|
|
183
|
+
getDateProperty(property, defaultIfEmpty, start = true) {
|
|
184
|
+
/* Notion dates look like this
|
|
185
|
+
"properties": {
|
|
186
|
+
"published_date":
|
|
187
|
+
{
|
|
188
|
+
"id":"a%3Cql",
|
|
189
|
+
"type":"date",
|
|
190
|
+
"date":{
|
|
191
|
+
"start":"2021-10-24",
|
|
192
|
+
"end":null,
|
|
193
|
+
"time_zone":null
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
*/
|
|
198
|
+
var _a, _b, _c;
|
|
199
|
+
// console.log("metadata:\n" + JSON.stringify(this.metadata, null, 2));
|
|
200
|
+
const p = (_a = this.metadata.properties) === null || _a === void 0 ? void 0 : _a[property];
|
|
201
|
+
// console.log(`prop ${property} = ${JSON.stringify(p)}`);
|
|
202
|
+
if (!p)
|
|
203
|
+
return defaultIfEmpty;
|
|
204
|
+
if (start) {
|
|
205
|
+
return ((_b = p === null || p === void 0 ? void 0 : p.date) === null || _b === void 0 ? void 0 : _b.start) ? p.date.start : defaultIfEmpty;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return ((_c = p === null || p === void 0 ? void 0 : p.date) === null || _c === void 0 ? void 0 : _c.end) ? p.date.end : defaultIfEmpty;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
176
211
|
getContentInfo(children) {
|
|
177
212
|
return __awaiter(this, void 0, void 0, function* () {
|
|
178
213
|
for (let i = 0; i < children.length; i++) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const NotionPage_1 = require("./NotionPage");
|
|
4
|
+
describe("NotionPage", () => {
|
|
5
|
+
const mockMetadata = {
|
|
6
|
+
object: "page",
|
|
7
|
+
id: "6e6921b9-b1f5-4614-ab3c-bf1a73358a1f",
|
|
8
|
+
created_time: "2023-04-11T10:17:00.000Z",
|
|
9
|
+
last_edited_time: "2023-04-13T20:24:00.000Z",
|
|
10
|
+
created_by: {
|
|
11
|
+
object: "user",
|
|
12
|
+
id: "USERID",
|
|
13
|
+
},
|
|
14
|
+
last_edited_by: {
|
|
15
|
+
object: "user",
|
|
16
|
+
id: "USERID",
|
|
17
|
+
},
|
|
18
|
+
cover: null,
|
|
19
|
+
icon: {
|
|
20
|
+
type: "file",
|
|
21
|
+
file: {
|
|
22
|
+
url: "https:/dummy_URL",
|
|
23
|
+
expiry_time: "2023-04-15T11:50:20.461Z",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
parent: {
|
|
27
|
+
type: "workspace",
|
|
28
|
+
workspace: true,
|
|
29
|
+
},
|
|
30
|
+
archived: false,
|
|
31
|
+
properties: {
|
|
32
|
+
title: {
|
|
33
|
+
id: "title",
|
|
34
|
+
type: "title",
|
|
35
|
+
title: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: {
|
|
39
|
+
content: "Foo",
|
|
40
|
+
link: null,
|
|
41
|
+
},
|
|
42
|
+
annotations: {
|
|
43
|
+
bold: false,
|
|
44
|
+
italic: false,
|
|
45
|
+
strikethrough: false,
|
|
46
|
+
underline: false,
|
|
47
|
+
code: false,
|
|
48
|
+
color: "default",
|
|
49
|
+
},
|
|
50
|
+
plain_text: "Foo",
|
|
51
|
+
href: null,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: {
|
|
56
|
+
content: "Bar",
|
|
57
|
+
link: null,
|
|
58
|
+
},
|
|
59
|
+
annotations: {
|
|
60
|
+
bold: false,
|
|
61
|
+
italic: false,
|
|
62
|
+
strikethrough: false,
|
|
63
|
+
underline: false,
|
|
64
|
+
code: false,
|
|
65
|
+
color: "default",
|
|
66
|
+
},
|
|
67
|
+
plain_text: "Bar",
|
|
68
|
+
href: null,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
date_property: {
|
|
73
|
+
id: "a%3Cql",
|
|
74
|
+
type: "date",
|
|
75
|
+
date: {
|
|
76
|
+
start: "2021-10-24",
|
|
77
|
+
end: "2021-10-28",
|
|
78
|
+
time_zone: null,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
url: "https://www.notion.so/Site-docu-notion-PAGEID",
|
|
83
|
+
};
|
|
84
|
+
describe("getPlainTextProperty", () => {
|
|
85
|
+
it("should return the plain text value of a property", () => {
|
|
86
|
+
const page = new NotionPage_1.NotionPage({
|
|
87
|
+
layoutContext: "Test Context",
|
|
88
|
+
pageId: "123",
|
|
89
|
+
order: 1,
|
|
90
|
+
metadata: mockMetadata,
|
|
91
|
+
foundDirectlyInOutline: true,
|
|
92
|
+
});
|
|
93
|
+
const result = page.getPlainTextProperty("title", "");
|
|
94
|
+
expect(result).toBe("FooBar");
|
|
95
|
+
});
|
|
96
|
+
it("should return the default value if the property is not found", () => {
|
|
97
|
+
const page = new NotionPage_1.NotionPage({
|
|
98
|
+
layoutContext: "Test Context",
|
|
99
|
+
pageId: "123",
|
|
100
|
+
order: 1,
|
|
101
|
+
metadata: mockMetadata,
|
|
102
|
+
foundDirectlyInOutline: true,
|
|
103
|
+
});
|
|
104
|
+
const result = page.getPlainTextProperty("nonexistent", "Default Value");
|
|
105
|
+
expect(result).toBe("Default Value");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe("getDateProperty", () => {
|
|
109
|
+
it("should return the start date property by default", () => {
|
|
110
|
+
const page = new NotionPage_1.NotionPage({
|
|
111
|
+
layoutContext: "Test Context",
|
|
112
|
+
pageId: "123",
|
|
113
|
+
order: 1,
|
|
114
|
+
metadata: mockMetadata,
|
|
115
|
+
foundDirectlyInOutline: true,
|
|
116
|
+
});
|
|
117
|
+
const result = page.getDateProperty("date_property", "");
|
|
118
|
+
expect(result).toBe("2021-10-24");
|
|
119
|
+
});
|
|
120
|
+
it("should return the end date if start is false", () => {
|
|
121
|
+
const page = new NotionPage_1.NotionPage({
|
|
122
|
+
layoutContext: "Test Context",
|
|
123
|
+
pageId: "123",
|
|
124
|
+
order: 1,
|
|
125
|
+
metadata: mockMetadata,
|
|
126
|
+
foundDirectlyInOutline: true,
|
|
127
|
+
});
|
|
128
|
+
const result = page.getDateProperty("date_property", "", false);
|
|
129
|
+
expect(result).toBe("2021-10-28");
|
|
130
|
+
});
|
|
131
|
+
it("should return the default value if the property is not found", () => {
|
|
132
|
+
const page = new NotionPage_1.NotionPage({
|
|
133
|
+
layoutContext: "Test Context",
|
|
134
|
+
pageId: "123",
|
|
135
|
+
order: 1,
|
|
136
|
+
metadata: mockMetadata,
|
|
137
|
+
foundDirectlyInOutline: true,
|
|
138
|
+
});
|
|
139
|
+
const result = page.getPlainTextProperty("nonexistent", "Default Value");
|
|
140
|
+
expect(result).toBe("Default Value");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { IPlugin } from "./pluginTypes";
|
|
1
|
+
import { IDocuNotionContext, IPlugin } from "./pluginTypes";
|
|
2
|
+
export declare function convertInternalUrl(context: IDocuNotionContext, url: string): string | undefined;
|
|
2
3
|
export declare function parseLinkId(fullLinkId: string): {
|
|
3
4
|
baseLinkId: string;
|
|
4
5
|
fragmentId: string;
|
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.standardInternalLinkConversion = exports.parseLinkId = void 0;
|
|
3
|
+
exports.standardInternalLinkConversion = exports.parseLinkId = exports.convertInternalUrl = void 0;
|
|
4
4
|
const log_1 = require("../log");
|
|
5
|
+
// converts a url to a local link, if it is a link to a page in the Notion site
|
|
6
|
+
// only here for plugins, notion won't normally be giving us raw urls (at least not that I've noticed)
|
|
7
|
+
// If it finds a URL but can't find the page it points to, it will return undefined.
|
|
8
|
+
// If it doesn't find a match at all, it returns undefined.
|
|
9
|
+
function convertInternalUrl(context, url) {
|
|
10
|
+
const kGetIDFromNotionURL = /https:\/\/www\.notion\.so\S+-([a-z,0-9]+)+.*/;
|
|
11
|
+
const match = kGetIDFromNotionURL.exec(url);
|
|
12
|
+
if (match === null) {
|
|
13
|
+
(0, log_1.warning)(`[standardInternalLinkConversion] Could not parse link ${url} as a Notion URL`);
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const id = match[1];
|
|
17
|
+
const pages = context.pages;
|
|
18
|
+
// find the page where pageId matches hrefFromNotion
|
|
19
|
+
const targetPage = pages.find(p => {
|
|
20
|
+
return p.matchesLinkId(id);
|
|
21
|
+
});
|
|
22
|
+
if (!targetPage) {
|
|
23
|
+
// About this situation. See https://github.com/sillsdev/docu-notion/issues/9
|
|
24
|
+
(0, log_1.warning)(`[standardInternalLinkConversion] Could not find the target of this link. Note that links to outline sections are not supported. ${url}. https://github.com/sillsdev/docu-notion/issues/9`);
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return convertLinkHref(context, targetPage, url);
|
|
28
|
+
}
|
|
29
|
+
exports.convertInternalUrl = convertInternalUrl;
|
|
30
|
+
// handles the whole markdown link, including the label
|
|
5
31
|
function convertInternalLink(context, markdownLink) {
|
|
6
32
|
const linkRegExp = /\[([^\]]+)?\]\(\/?([^),^/]+)\)/g;
|
|
7
33
|
const match = linkRegExp.exec(markdownLink);
|
|
@@ -70,9 +96,7 @@ exports.standardInternalLinkConversion = {
|
|
|
70
96
|
// (has some other text that's been turned into a link) or "raw".
|
|
71
97
|
// Raw links come in without a leading slash, e.g. [link_to_page](4a6de8c0-b90b-444b-8a7b-d534d6ec71a4)
|
|
72
98
|
// Inline links come in with a leading slash, e.g. [pointer to the introduction](/4a6de8c0b90b444b8a7bd534d6ec71a4)
|
|
73
|
-
|
|
74
|
-
// review: currently we expect that internal links have an opening slash, but at one point it was optional.
|
|
75
|
-
match: /\[([^\]]+)?\]\(\/([^),^/]+)\)/,
|
|
99
|
+
match: /\[([^\]]+)?\]\((?!mailto:)(\/?[^),^/]+)\)/,
|
|
76
100
|
convert: convertInternalLink,
|
|
77
101
|
},
|
|
78
102
|
};
|
|
@@ -173,7 +173,8 @@ test("link to a heading block on a page", () => __awaiter(void 0, void 0, void 0
|
|
|
173
173
|
const slugResults = yield getMarkdown(blocks, slugTargetPage);
|
|
174
174
|
expect(slugResults.trim()).toBe(`(Inline [heading on some page](/hello-world#456) the end.)`);
|
|
175
175
|
}));
|
|
176
|
-
|
|
176
|
+
// Text that has been selected and turned into a link to one of our pages
|
|
177
|
+
test("inline link to an existing page on this site uses slug", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
177
178
|
const targetPageId = "123";
|
|
178
179
|
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
179
180
|
slug: "hello-world",
|
|
@@ -221,6 +222,33 @@ test("link to an existing page on this site uses slug", () => __awaiter(void 0,
|
|
|
221
222
|
}, targetPage);
|
|
222
223
|
expect(results.trim()).toBe("Inline [It’s good](/hello-world)");
|
|
223
224
|
}));
|
|
225
|
+
// this is the kind of link you get if you just insert a "link to page" to Notion
|
|
226
|
+
test("raw link to an existing page on this site that has a slug", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
227
|
+
const targetPageId = "123";
|
|
228
|
+
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
229
|
+
slug: "point-to-me",
|
|
230
|
+
name: "Point to Me",
|
|
231
|
+
id: targetPageId,
|
|
232
|
+
});
|
|
233
|
+
const results = yield getMarkdown({
|
|
234
|
+
object: "block",
|
|
235
|
+
id: "2051d790-e527-4b4e-b145-ec0beee2addf",
|
|
236
|
+
parent: {
|
|
237
|
+
type: "page_id",
|
|
238
|
+
page_id: "333",
|
|
239
|
+
},
|
|
240
|
+
created_time: "2023-06-14T20:09:00.000Z",
|
|
241
|
+
last_edited_time: "2023-06-14T20:09:00.000Z",
|
|
242
|
+
has_children: false,
|
|
243
|
+
archived: false,
|
|
244
|
+
type: "link_to_page",
|
|
245
|
+
link_to_page: {
|
|
246
|
+
type: "page_id",
|
|
247
|
+
page_id: targetPageId,
|
|
248
|
+
},
|
|
249
|
+
}, targetPage);
|
|
250
|
+
expect(results.trim()).toBe("[Point to Me](/point-to-me)");
|
|
251
|
+
}));
|
|
224
252
|
test("link in a bulleted list", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
225
253
|
const targetPageId = "123";
|
|
226
254
|
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
@@ -427,6 +455,41 @@ Callouts inline [great page](/hello-world).
|
|
|
427
455
|
|
|
428
456
|
:::`);
|
|
429
457
|
}));
|
|
458
|
+
test("internal link inside codeblock ignored", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
459
|
+
const targetPageId = "123";
|
|
460
|
+
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
461
|
+
slug: "hello-world",
|
|
462
|
+
name: "Hello World",
|
|
463
|
+
id: targetPageId,
|
|
464
|
+
});
|
|
465
|
+
const results = yield getMarkdown({
|
|
466
|
+
type: "code",
|
|
467
|
+
code: {
|
|
468
|
+
caption: [],
|
|
469
|
+
rich_text: [
|
|
470
|
+
{
|
|
471
|
+
type: "text",
|
|
472
|
+
text: {
|
|
473
|
+
content: "this should not change [link](https://www.notion.so/native/metapages/mypage)",
|
|
474
|
+
link: null,
|
|
475
|
+
},
|
|
476
|
+
annotations: {
|
|
477
|
+
bold: false,
|
|
478
|
+
italic: false,
|
|
479
|
+
strikethrough: false,
|
|
480
|
+
underline: false,
|
|
481
|
+
code: false,
|
|
482
|
+
color: "default",
|
|
483
|
+
},
|
|
484
|
+
plain_text: "this should not change [link](https://www.notion.so/native/metapages/mypage)",
|
|
485
|
+
href: null,
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
language: "javascript", // notion assumed javascript in my test in which I didn't specify a language
|
|
489
|
+
},
|
|
490
|
+
}, targetPage);
|
|
491
|
+
expect(results.trim()).toContain("this should not change [link](https://www.notion.so/native/metapages/mypage)");
|
|
492
|
+
}));
|
|
430
493
|
function getMarkdown(block, targetPage) {
|
|
431
494
|
return __awaiter(this, void 0, void 0, function* () {
|
|
432
495
|
const config = {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
test("raw url inside a mermaid codeblock gets converted to path using slug of that page", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
const targetPageId = "123";
|
|
16
|
+
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
17
|
+
slug: "slug-of-target",
|
|
18
|
+
name: "My Target Page",
|
|
19
|
+
id: targetPageId,
|
|
20
|
+
});
|
|
21
|
+
const input = {
|
|
22
|
+
type: "code",
|
|
23
|
+
code: {
|
|
24
|
+
caption: [],
|
|
25
|
+
rich_text: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: {
|
|
29
|
+
content: `click A "https://www.notion.so/native/metapages/A-Page-${targetPageId}"`,
|
|
30
|
+
link: null,
|
|
31
|
+
},
|
|
32
|
+
annotations: {
|
|
33
|
+
bold: false,
|
|
34
|
+
italic: false,
|
|
35
|
+
strikethrough: false,
|
|
36
|
+
underline: false,
|
|
37
|
+
code: false,
|
|
38
|
+
color: "default",
|
|
39
|
+
},
|
|
40
|
+
plain_text: `click A "https://www.notion.so/native/metapages/A-Page-${targetPageId}"`,
|
|
41
|
+
href: null,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
language: "mermaid", // notion assumed javascript in my test in which I didn't specify a language
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const mermaidLinks = {
|
|
48
|
+
name: "mermaidLinks",
|
|
49
|
+
regexMarkdownModifications: [
|
|
50
|
+
{
|
|
51
|
+
regex: /```mermaid\n.*"(https:\/\/www\.notion\.so\S+)"/,
|
|
52
|
+
includeCodeBlocks: true,
|
|
53
|
+
getReplacement: (context, match) => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
const url = match[1];
|
|
55
|
+
const docusaurusUrl = context.convertNotionLinkToLocalDocusaurusLink(url);
|
|
56
|
+
if (docusaurusUrl) {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
58
|
+
return yield match[0].replace(url, docusaurusUrl);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
(0, log_1.error)(`Could not convert link ${url} to a local docusaurus link`);
|
|
62
|
+
return match[0];
|
|
63
|
+
}
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
const config = {
|
|
69
|
+
plugins: [
|
|
70
|
+
// standardInternalLinkConversion,
|
|
71
|
+
// standardExternalLinkConversion,
|
|
72
|
+
mermaidLinks,
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
const results = yield (0, pluginTestRun_1.oneBlockToMarkdown)(config, input, targetPage);
|
|
76
|
+
expect(results.trim()).toContain(`click A "/slug-of-target"`);
|
|
77
|
+
}));
|
|
@@ -15,6 +15,7 @@ const notion_to_md_1 = require("notion-to-md");
|
|
|
15
15
|
const HierarchicalNamedLayoutStrategy_1 = require("../HierarchicalNamedLayoutStrategy");
|
|
16
16
|
const NotionPage_1 = require("../NotionPage");
|
|
17
17
|
const transform_1 = require("../transform");
|
|
18
|
+
const internalLinks_1 = require("./internalLinks");
|
|
18
19
|
function blocksToMarkdown(config, blocks, pages) {
|
|
19
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
21
|
const notionClient = new client_1.Client({ auth: "unused" });
|
|
@@ -35,6 +36,9 @@ function blocksToMarkdown(config, blocks, pages) {
|
|
|
35
36
|
resolve([]);
|
|
36
37
|
});
|
|
37
38
|
},
|
|
39
|
+
convertNotionLinkToLocalDocusaurusLink: (url) => {
|
|
40
|
+
return (0, internalLinks_1.convertInternalUrl)(docunotionContext, url);
|
|
41
|
+
},
|
|
38
42
|
//TODO might be needed for some tests, e.g. the image transformer...
|
|
39
43
|
directoryContainingMarkdown: "not yet",
|
|
40
44
|
relativeFilePathToFolderContainingPage: "not yet",
|
|
@@ -24,7 +24,8 @@ export type IPlugin = {
|
|
|
24
24
|
export type IRegexMarkdownModification = {
|
|
25
25
|
regex: RegExp;
|
|
26
26
|
replacementPattern?: string;
|
|
27
|
-
getReplacement?(
|
|
27
|
+
getReplacement?(context: IDocuNotionContext, match: RegExpExecArray): Promise<string>;
|
|
28
|
+
includeCodeBlocks?: boolean;
|
|
28
29
|
imports?: string[];
|
|
29
30
|
};
|
|
30
31
|
export type ICustomNotionToMarkdownConversion = (block: ListBlockChildrenResponseResult, context: IDocuNotionContext) => () => Promise<string>;
|
|
@@ -36,6 +37,7 @@ export type IDocuNotionContext = {
|
|
|
36
37
|
notionToMarkdown: NotionToMarkdown;
|
|
37
38
|
directoryContainingMarkdown: string;
|
|
38
39
|
relativeFilePathToFolderContainingPage: string;
|
|
40
|
+
convertNotionLinkToLocalDocusaurusLink: (url: string) => string | undefined;
|
|
39
41
|
pages: NotionPage[];
|
|
40
42
|
counts: ICounts;
|
|
41
43
|
};
|
package/dist/pull.js
CHANGED
|
@@ -45,6 +45,7 @@ const limiter_1 = require("limiter");
|
|
|
45
45
|
const client_1 = require("@notionhq/client");
|
|
46
46
|
const process_1 = require("process");
|
|
47
47
|
const configuration_1 = require("./config/configuration");
|
|
48
|
+
const internalLinks_1 = require("./plugins/internalLinks");
|
|
48
49
|
let layoutStrategy;
|
|
49
50
|
let notionToMarkdown;
|
|
50
51
|
const pages = new Array();
|
|
@@ -97,7 +98,8 @@ function outputPages(options, config, pages) {
|
|
|
97
98
|
notionToMarkdown: notionToMarkdown,
|
|
98
99
|
options: options,
|
|
99
100
|
pages: pages,
|
|
100
|
-
counts: counts,
|
|
101
|
+
counts: counts,
|
|
102
|
+
convertNotionLinkToLocalDocusaurusLink: (url) => (0, internalLinks_1.convertInternalUrl)(context, url),
|
|
101
103
|
};
|
|
102
104
|
for (const page of pages) {
|
|
103
105
|
layoutStrategy.pageWasSeen(page);
|
package/dist/transform.js
CHANGED
|
@@ -44,7 +44,7 @@ function getMarkdownFromNotionBlocks(context, config, blocks) {
|
|
|
44
44
|
markdown = doLinkFixes(context, markdown, config);
|
|
45
45
|
//console.log("markdown after link fixes", markdown);
|
|
46
46
|
// simple regex-based tweaks. These are usually related to docusaurus
|
|
47
|
-
const { imports, body } = yield doTransformsOnMarkdown(config, markdown);
|
|
47
|
+
const { imports, body } = yield doTransformsOnMarkdown(context, config, markdown);
|
|
48
48
|
// console.log("markdown after regex fixes", markdown);
|
|
49
49
|
// console.log("body after regex", body);
|
|
50
50
|
return `${imports}\n${body}`;
|
|
@@ -64,7 +64,7 @@ function doNotionBlockTransforms(blocks, config) {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
function doTransformsOnMarkdown(config, input) {
|
|
67
|
+
function doTransformsOnMarkdown(context, config, input) {
|
|
68
68
|
var _a;
|
|
69
69
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
70
|
const regexMods = config.plugins
|
|
@@ -88,15 +88,19 @@ function doTransformsOnMarkdown(config, input) {
|
|
|
88
88
|
// regex.exec is stateful, so we don't want to mess up the plugin's use of its own regex, so we clone it.
|
|
89
89
|
// we also add the "g" flag to make sure we get all matches
|
|
90
90
|
const regex = new RegExp(`${codeBlocks.source}|(${mod.regex.source})`, "g");
|
|
91
|
-
let count = 0;
|
|
92
91
|
while ((match = regex.exec(input)) !== null) {
|
|
93
92
|
if (match[0]) {
|
|
94
93
|
const original = match[0];
|
|
95
|
-
if (original.startsWith("```") &&
|
|
96
|
-
|
|
94
|
+
if (original.startsWith("```") &&
|
|
95
|
+
original.endsWith("```") &&
|
|
96
|
+
!mod.includeCodeBlocks) {
|
|
97
|
+
continue; // code block, and they didn't say to include them
|
|
97
98
|
}
|
|
98
99
|
if (mod.getReplacement) {
|
|
99
|
-
|
|
100
|
+
// our match here has an extra group, which is an implementation detail
|
|
101
|
+
// that shouldn't be made visible to the plugin
|
|
102
|
+
const matchAsThePluginWouldExpectIt = mod.regex.exec(match[0]);
|
|
103
|
+
replacement = yield mod.getReplacement(context, matchAsThePluginWouldExpectIt);
|
|
100
104
|
}
|
|
101
105
|
else if (mod.replacementPattern) {
|
|
102
106
|
console.log(`mod.replacementPattern.replace("$1", ${match[2]}`);
|
|
@@ -123,7 +127,7 @@ function doTransformsOnMarkdown(config, input) {
|
|
|
123
127
|
function doNotionToMarkdown(docunotionContext, blocks) {
|
|
124
128
|
return __awaiter(this, void 0, void 0, function* () {
|
|
125
129
|
const mdBlocks = yield docunotionContext.notionToMarkdown.blocksToMarkdown(blocks);
|
|
126
|
-
|
|
130
|
+
const markdown = docunotionContext.notionToMarkdown.toMarkdownString(mdBlocks);
|
|
127
131
|
return markdown;
|
|
128
132
|
});
|
|
129
133
|
}
|
|
@@ -151,6 +155,7 @@ function doLinkFixes(context, markdown, config) {
|
|
|
151
155
|
if (!plugin.linkModifier)
|
|
152
156
|
return false;
|
|
153
157
|
if (plugin.linkModifier.match.exec(originalLinkMarkdown) === null) {
|
|
158
|
+
(0, log_1.verbose)(`plugin "${plugin.name}" did not match this url`);
|
|
154
159
|
return false;
|
|
155
160
|
}
|
|
156
161
|
const newMarkdown = plugin.linkModifier.convert(context, originalLinkMarkdown);
|
package/package.json
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"scripts": {
|
|
3
|
-
"test": "
|
|
4
|
-
"build": "
|
|
3
|
+
"test": "vitest",
|
|
4
|
+
"build": "npm run test -- --run && tsc && cp ./src/css/*.css dist/",
|
|
5
5
|
"build-only": "tsc && cp ./src/css/*.css dist/",
|
|
6
6
|
"clean": "rimraf ./dist/",
|
|
7
7
|
"semantic-release": "semantic-release",
|
|
8
|
-
"typecheck": "tsc --noEmit",
|
|
9
|
-
"notion-download": "node dist/index.js",
|
|
10
8
|
"cmdhelp": "ts-node src/index.ts",
|
|
11
9
|
"// note that we're not using ts-node at the moment because of ": "https://github.com/Codex-/cosmiconfig-typescript-loader/issues/70",
|
|
12
|
-
"ts": "
|
|
10
|
+
"ts": "tsc && rimraf ./docs/ && cross-var node dist/index.js",
|
|
11
|
+
"// typescript check": "",
|
|
12
|
+
"tsc": "tsc",
|
|
13
13
|
"// test out with a private sample notion db": "",
|
|
14
|
-
"pull-test-tagged": "
|
|
15
|
-
"pull-sample-site": "
|
|
14
|
+
"pull-test-tagged": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_TEST_ROOT_PAGE_ID --log-level debug --status-tag test",
|
|
15
|
+
"pull-sample-site": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_SAMPLE_ROOT_PAGE --log-level debug",
|
|
16
16
|
"// test with a semi-stable/public site:": "",
|
|
17
|
-
"pull-sample": "
|
|
18
|
-
"pull-sample-with-paths": "
|
|
17
|
+
"pull-sample": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_SAMPLE_ROOT_PAGE -m ./sample --locales en,es,fr,de --log-level verbose",
|
|
18
|
+
"pull-sample-with-paths": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_SAMPLE_ROOT_PAGE -m ./sample --img-output-path ./sample_img"
|
|
19
19
|
},
|
|
20
20
|
"//file-type": "have to use this version before they switched to ESM, which gives a compile error related to require()",
|
|
21
21
|
"//node-fetch@2.6.6file-type": "have to use this version before they switched to ESM, which gives a compile error related to require()",
|
|
22
22
|
"//chalk@4": "also ESM related problem",
|
|
23
23
|
"//notion-client@4": "also ESM related problem",
|
|
24
|
+
"//note: ts-node": "really is a runtime dependency",
|
|
24
25
|
"dependencies": {
|
|
25
26
|
"@notionhq/client": "2.2.3",
|
|
26
27
|
"chalk": "^4.1.2",
|
|
@@ -35,29 +36,29 @@
|
|
|
35
36
|
"notion-client": "^4",
|
|
36
37
|
"notion-to-md": "^2.5.5",
|
|
37
38
|
"path": "^0.12.7",
|
|
39
|
+
"ts-node": "^10.2.1",
|
|
38
40
|
"sanitize-filename": "^1.6.3"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/fs-extra": "^9.0.13",
|
|
42
|
-
"@types/jest": "^28.1.6",
|
|
43
44
|
"@types/markdown-table": "^2.0.0",
|
|
44
|
-
"@types/node": "^
|
|
45
|
+
"@types/node": "^18.15.11",
|
|
45
46
|
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
|
46
47
|
"@typescript-eslint/parser": "^4.22.0",
|
|
48
|
+
"@vitest/ui": "^0.30.1",
|
|
47
49
|
"cross-var": "^1.1.0",
|
|
48
50
|
"cz-conventional-changelog": "^3.3.0",
|
|
49
51
|
"eslint": "^7.25.0",
|
|
50
52
|
"eslint-config-prettier": "^8.3.0",
|
|
51
53
|
"eslint-plugin-node": "^11.1.0",
|
|
52
54
|
"eslint-plugin-prettier": "^3.4.0",
|
|
53
|
-
"jest": "^28.1.3",
|
|
54
55
|
"lint-staged": "^10.5.4",
|
|
55
56
|
"prettier": "^2.2.1",
|
|
56
57
|
"rimraf": "^4.1.2",
|
|
57
58
|
"semantic-release": "^19.0.2",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
59
|
+
"typescript": "^4.6.4",
|
|
60
|
+
"vite": "^4.2.1",
|
|
61
|
+
"vitest": "^0.30.1"
|
|
61
62
|
},
|
|
62
63
|
"name": "@sillsdev/docu-notion",
|
|
63
64
|
"description": "Download Notion pages as markdown and image files, preserving hierarchy and enabling workflow properties. Works with Docusaurus.",
|
|
@@ -77,20 +78,16 @@
|
|
|
77
78
|
"url": "https://github.com/sillsdev/docu-notion/issues"
|
|
78
79
|
},
|
|
79
80
|
"homepage": "https://github.com/sillsdev/docu-notion#readme",
|
|
80
|
-
"packageManager": "yarn@3.4.1",
|
|
81
81
|
"main": "./dist/index.js",
|
|
82
82
|
"bin": "dist/index.js",
|
|
83
83
|
"files": [
|
|
84
84
|
"dist/**/*"
|
|
85
85
|
],
|
|
86
|
-
"release": {
|
|
87
|
-
"branches": [
|
|
88
|
-
"main",
|
|
89
|
-
"release"
|
|
90
|
-
]
|
|
91
|
-
},
|
|
92
86
|
"publishConfig": {
|
|
93
87
|
"access": "public"
|
|
94
88
|
},
|
|
95
|
-
"
|
|
89
|
+
"volta": {
|
|
90
|
+
"node": "18.16.0"
|
|
91
|
+
},
|
|
92
|
+
"version": "0.13.0"
|
|
96
93
|
}
|