@sillsdev/docu-notion 0.13.4 → 0.14.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/dist/config/default.docunotion.config.js +2 -4
- package/dist/{notion-styles.css → docu-notion-styles.css} +60 -58
- package/dist/latex.spec.js +97 -0
- package/dist/plugins/ColumnTransformer.js +5 -7
- package/dist/plugins/ColumnTransformer.spec.d.ts +1 -0
- package/dist/plugins/ColumnTransformer.spec.js +118 -0
- package/dist/plugins/VideoTransformer.d.ts +2 -0
- package/dist/plugins/VideoTransformer.js +33 -0
- package/dist/plugins/VideoTransformer.spec.d.ts +1 -0
- package/dist/plugins/VideoTransformer.spec.js +112 -0
- package/dist/plugins/embedTweaks.d.ts +0 -2
- package/dist/plugins/embedTweaks.js +3 -25
- package/dist/plugins/embedTweaks.spec.js +0 -47
- package/dist/plugins/externalLinks.spec.js +13 -0
- package/dist/plugins/internalLinks.spec.js +0 -2
- package/dist/plugins/pluginTestRun.d.ts +1 -1
- package/dist/plugins/pluginTestRun.js +15 -6
- package/dist/plugins/pluginTypes.d.ts +1 -0
- package/dist/pull.d.ts +2 -0
- package/dist/pull.js +37 -5
- package/dist/run.d.ts +1 -1
- package/dist/run.js +76 -23
- package/dist/transform.js +21 -8
- package/package.json +4 -3
- package/dist/plugins/NumberedListTransformer.d.ts +0 -2
- package/dist/plugins/NumberedListTransformer.js +0 -55
- package/dist/plugins/NumberedListTransformer.spec.js +0 -86
- /package/dist/{plugins/NumberedListTransformer.spec.d.ts → latex.spec.d.ts} +0 -0
|
@@ -12,53 +12,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const log_1 = require("../log");
|
|
13
13
|
const pluginTestRun_1 = require("./pluginTestRun");
|
|
14
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
15
|
test("imgur", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
16
|
(0, log_1.setLogLevel)("verbose");
|
|
64
17
|
const config = { plugins: [embedTweaks_1.imgurGifEmbed] };
|
|
@@ -23,6 +23,19 @@ test("links turned into bookmarks", () => __awaiter(void 0, void 0, void 0, func
|
|
|
23
23
|
});
|
|
24
24
|
expect(results.trim()).toBe("[https://github.com](https://github.com)");
|
|
25
25
|
}));
|
|
26
|
+
test("video links turned into bookmarks", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
+
(0, log_1.setLogLevel)("debug");
|
|
28
|
+
const results = yield getMarkdown({
|
|
29
|
+
object: "block",
|
|
30
|
+
type: "bookmark",
|
|
31
|
+
bookmark: {
|
|
32
|
+
caption: [],
|
|
33
|
+
url: "https://vimeo.com/4613611xx",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
expect(results).toContain("[https://vimeo.com/4613611xx](https://vimeo.com/4613611xx)");
|
|
37
|
+
expect(results).not.toContain(`import`);
|
|
38
|
+
}));
|
|
26
39
|
test("external link inside callout", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
40
|
const results = yield getMarkdown({
|
|
28
41
|
type: "callout",
|
|
@@ -14,7 +14,6 @@ const pluginTestRun_1 = require("./pluginTestRun");
|
|
|
14
14
|
const CalloutTransformer_1 = require("./CalloutTransformer");
|
|
15
15
|
const externalLinks_1 = require("./externalLinks");
|
|
16
16
|
const internalLinks_1 = require("./internalLinks");
|
|
17
|
-
const NumberedListTransformer_1 = require("./NumberedListTransformer");
|
|
18
17
|
test("urls that show up as raw text get left that way", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
18
|
const results = yield getMarkdown({
|
|
20
19
|
type: "paragraph",
|
|
@@ -523,7 +522,6 @@ function getMarkdown(block, targetPage) {
|
|
|
523
522
|
const config = {
|
|
524
523
|
plugins: [
|
|
525
524
|
CalloutTransformer_1.standardCalloutTransformer,
|
|
526
|
-
NumberedListTransformer_1.standardNumberedListTransformer,
|
|
527
525
|
internalLinks_1.standardInternalLinkConversion,
|
|
528
526
|
externalLinks_1.standardExternalLinkConversion,
|
|
529
527
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NotionPage } from "../NotionPage";
|
|
2
2
|
import { IDocuNotionConfig } from "../config/configuration";
|
|
3
3
|
import { NotionBlock } from "../types";
|
|
4
|
-
export declare function blocksToMarkdown(config: IDocuNotionConfig, blocks: NotionBlock[], pages?: NotionPage[]): Promise<string>;
|
|
4
|
+
export declare function blocksToMarkdown(config: IDocuNotionConfig, blocks: NotionBlock[], pages?: NotionPage[], children?: NotionBlock[], validApiKey?: string): Promise<string>;
|
|
5
5
|
export declare function makeSamplePageObject(options: {
|
|
6
6
|
slug?: string;
|
|
7
7
|
name?: string;
|
|
@@ -16,9 +16,17 @@ const HierarchicalNamedLayoutStrategy_1 = require("../HierarchicalNamedLayoutStr
|
|
|
16
16
|
const NotionPage_1 = require("../NotionPage");
|
|
17
17
|
const transform_1 = require("../transform");
|
|
18
18
|
const internalLinks_1 = require("./internalLinks");
|
|
19
|
-
|
|
19
|
+
const pull_1 = require("../pull");
|
|
20
|
+
function blocksToMarkdown(config, blocks, pages,
|
|
21
|
+
// Notes on children:
|
|
22
|
+
// - These children will apply to each block in blocks. (could enhance but not needed yet)
|
|
23
|
+
// - If you are passing in children, it is probably because your parent block has has_children=true.
|
|
24
|
+
// In that case, notion-to-md will make an API call... you'll need to set any validApiKey.
|
|
25
|
+
children, validApiKey) {
|
|
20
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
-
const notionClient = new client_1.Client({
|
|
27
|
+
const notionClient = new client_1.Client({
|
|
28
|
+
auth: validApiKey || "unused",
|
|
29
|
+
});
|
|
22
30
|
const notionToMD = new notion_to_md_1.NotionToMarkdown({
|
|
23
31
|
notionClient,
|
|
24
32
|
});
|
|
@@ -28,17 +36,18 @@ function blocksToMarkdown(config, blocks, pages) {
|
|
|
28
36
|
// }
|
|
29
37
|
const docunotionContext = {
|
|
30
38
|
notionToMarkdown: notionToMD,
|
|
31
|
-
// TODO when does this actually need to do get some children?
|
|
32
|
-
// We can add a children argument to this method, but for the tests
|
|
33
|
-
// I have so far, it's not needed.
|
|
34
39
|
getBlockChildren: (id) => {
|
|
40
|
+
// We call numberChildrenIfNumberedList here because the real getBlockChildren does
|
|
41
|
+
if (children)
|
|
42
|
+
(0, pull_1.numberChildrenIfNumberedList)(children);
|
|
35
43
|
return new Promise((resolve, reject) => {
|
|
36
|
-
resolve([]);
|
|
44
|
+
resolve(children !== null && children !== void 0 ? children : []);
|
|
37
45
|
});
|
|
38
46
|
},
|
|
39
47
|
convertNotionLinkToLocalDocusaurusLink: (url) => {
|
|
40
48
|
return (0, internalLinks_1.convertInternalUrl)(docunotionContext, url);
|
|
41
49
|
},
|
|
50
|
+
imports: [],
|
|
42
51
|
//TODO might be needed for some tests, e.g. the image transformer...
|
|
43
52
|
directoryContainingMarkdown: "not yet",
|
|
44
53
|
relativeFilePathToFolderContainingPage: "not yet",
|
package/dist/pull.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Client } from "@notionhq/client";
|
|
2
|
+
import { ListBlockChildrenResponseResults } from "notion-to-md/build/types";
|
|
2
3
|
export type DocuNotionOptions = {
|
|
3
4
|
notionToken: string;
|
|
4
5
|
rootPage: string;
|
|
@@ -11,3 +12,4 @@ export type DocuNotionOptions = {
|
|
|
11
12
|
export declare function notionPull(options: DocuNotionOptions): Promise<void>;
|
|
12
13
|
export declare function executeWithRateLimitAndRetries<T>(label: string, asyncFunction: () => Promise<T>): Promise<T>;
|
|
13
14
|
export declare function initNotionClient(notionToken: string): Client;
|
|
15
|
+
export declare function numberChildrenIfNumberedList(blocks: ListBlockChildrenResponseResults): void;
|
package/dist/pull.js
CHANGED
|
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.initNotionClient = exports.executeWithRateLimitAndRetries = exports.notionPull = void 0;
|
|
35
|
+
exports.numberChildrenIfNumberedList = exports.initNotionClient = exports.executeWithRateLimitAndRetries = exports.notionPull = void 0;
|
|
36
36
|
const fs = __importStar(require("fs-extra"));
|
|
37
37
|
const notion_to_md_1 = require("notion-to-md");
|
|
38
38
|
const HierarchicalNamedLayoutStrategy_1 = require("./HierarchicalNamedLayoutStrategy");
|
|
@@ -62,7 +62,7 @@ function notionPull(options) {
|
|
|
62
62
|
const optionsForLogging = Object.assign({}, options);
|
|
63
63
|
// Just show the first few letters of the notion token, which start with "secret" anyhow.
|
|
64
64
|
optionsForLogging.notionToken =
|
|
65
|
-
optionsForLogging.notionToken.substring(0,
|
|
65
|
+
optionsForLogging.notionToken.substring(0, 10) + "...";
|
|
66
66
|
const config = yield (0, configuration_1.loadConfigAsync)();
|
|
67
67
|
(0, log_1.verbose)(`Options:${JSON.stringify(optionsForLogging, null, 2)}`);
|
|
68
68
|
yield (0, images_1.initImageHandling)(options.imgPrefixInMarkdown || options.imgOutputPath || "", options.imgOutputPath || "", options.locales);
|
|
@@ -73,6 +73,16 @@ function notionPull(options) {
|
|
|
73
73
|
layoutStrategy.setRootDirectoryForMarkdown(options.markdownOutputPath.replace(/\/+$/, "") // trim any trailing slash
|
|
74
74
|
);
|
|
75
75
|
(0, log_1.info)("Connecting to Notion...");
|
|
76
|
+
// Do a quick test to see if we can connect to the root so that we can give a better error than just a generic "could not find page" one.
|
|
77
|
+
try {
|
|
78
|
+
yield executeWithRateLimitAndRetries("retrieving root page", () => __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
yield notionClient.pages.retrieve({ page_id: options.rootPage });
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
(0, log_1.error)(`docu-notion could not retrieve the root page from Notion. \r\na) Check that the root page id really is "${options.rootPage}".\r\nb) Check that your Notion API token (the "Integration Secret") is correct. It starts with "${optionsForLogging.notionToken}".\r\nc) Check that your root page includes your "integration" in its "connections".\r\nThis internal error message may help:\r\n ${e.message}`);
|
|
84
|
+
(0, process_1.exit)(1);
|
|
85
|
+
}
|
|
76
86
|
(0, log_1.group)("Stage 1: walk children of the page named 'Outline', looking for pages...");
|
|
77
87
|
yield getPagesRecursively(options, "", options.rootPage, 0, true);
|
|
78
88
|
(0, log_1.logDebug)("getPagesRecursively", JSON.stringify(pages, null, 2));
|
|
@@ -99,6 +109,7 @@ function outputPages(options, config, pages) {
|
|
|
99
109
|
options: options,
|
|
100
110
|
pages: pages,
|
|
101
111
|
counts: counts,
|
|
112
|
+
imports: [],
|
|
102
113
|
convertNotionLinkToLocalDocusaurusLink: (url) => (0, internalLinks_1.convertInternalUrl)(context, url),
|
|
103
114
|
};
|
|
104
115
|
for (const page of pages) {
|
|
@@ -212,9 +223,11 @@ function executeWithRateLimitAndRetries(label, asyncFunction) {
|
|
|
212
223
|
e.message.includes("timeout") ||
|
|
213
224
|
e.message.includes("Timeout") ||
|
|
214
225
|
e.message.includes("limit") ||
|
|
215
|
-
e.message.includes("Limit")
|
|
226
|
+
e.message.includes("Limit") ||
|
|
227
|
+
(e === null || e === void 0 ? void 0 : e.code) === "notionhq_client_response_error" ||
|
|
228
|
+
(e === null || e === void 0 ? void 0 : e.code) === "service_unavailable") {
|
|
216
229
|
const secondsToWait = i + 1;
|
|
217
|
-
(0, log_1.
|
|
230
|
+
(0, log_1.warning)(`While doing "${label}", got error "${e.message}". Will retry after ${secondsToWait}s...`);
|
|
218
231
|
yield new Promise(resolve => setTimeout(resolve, 1000 * secondsToWait));
|
|
219
232
|
}
|
|
220
233
|
else {
|
|
@@ -264,7 +277,9 @@ function getBlockChildren(id) {
|
|
|
264
277
|
(0, log_1.error)(`The Notion API returned some blocks that were not full blocks. docu-notion does not handle this yet. Please report it.`);
|
|
265
278
|
(0, process_1.exit)(1);
|
|
266
279
|
}
|
|
267
|
-
|
|
280
|
+
const result = (_b = overallResult === null || overallResult === void 0 ? void 0 : overallResult.results) !== null && _b !== void 0 ? _b : [];
|
|
281
|
+
numberChildrenIfNumberedList(result);
|
|
282
|
+
return result;
|
|
268
283
|
});
|
|
269
284
|
}
|
|
270
285
|
function initNotionClient(notionToken) {
|
|
@@ -287,3 +302,20 @@ function fromPageId(context, pageId, order, foundDirectlyInOutline) {
|
|
|
287
302
|
});
|
|
288
303
|
});
|
|
289
304
|
}
|
|
305
|
+
// This function is copied (and renamed from modifyNumberedListObject) from notion-to-md.
|
|
306
|
+
// They always run it on the results of their getBlockChildren.
|
|
307
|
+
// When we use our own getBlockChildren, we need to run it too.
|
|
308
|
+
function numberChildrenIfNumberedList(blocks) {
|
|
309
|
+
let numberedListIndex = 0;
|
|
310
|
+
for (const block of blocks) {
|
|
311
|
+
if ("type" in block && block.type === "numbered_list_item") {
|
|
312
|
+
// add numbers
|
|
313
|
+
// @ts-ignore
|
|
314
|
+
block.numbered_list_item.number = ++numberedListIndex;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
numberedListIndex = 0;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
exports.numberChildrenIfNumberedList = numberChildrenIfNumberedList;
|
package/dist/run.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function run(): void
|
|
1
|
+
export declare function run(): Promise<void>;
|
package/dist/run.js
CHANGED
|
@@ -1,33 +1,86 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
2
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
38
|
exports.run = void 0;
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
4
40
|
const commander_1 = require("commander");
|
|
5
41
|
const log_1 = require("./log");
|
|
6
42
|
const pull_1 = require("./pull");
|
|
43
|
+
const path_1 = __importDefault(require("path"));
|
|
7
44
|
function run() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const pkg = require("../package.json");
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
48
|
+
console.log(`docu-notion version ${pkg.version}`);
|
|
49
|
+
commander_1.program.name("docu-notion").description("");
|
|
50
|
+
commander_1.program.usage("-n <token> -r <root> [options]");
|
|
51
|
+
commander_1.program
|
|
52
|
+
.requiredOption("-n, --notion-token <string>", "notion api token, which looks like secret_3bc1b50XFYb15123RHF243x43450XFY33250XFYa343")
|
|
53
|
+
.requiredOption("-r, --root-page <string>", "The 31 character ID of the page which is the root of your docs page in notion. The code will look like 9120ec9960244ead80fa2ef4bc1bba25. This page must have a child page named 'Outline'")
|
|
54
|
+
.option("-m, --markdown-output-path <string>", "Root of the hierarchy for md files. WARNING: docu-notion will delete files from this directory. Note also that if it finds localized images, it will create an i18n/ directory as a sibling.", "./docs")
|
|
55
|
+
.option("--css-output-directory <string>", "docu-notion has a docu-notion-styles.css file that you will need to use to get things like notion columns to look right. This option specifies where that file should be copied to.", "./css")
|
|
56
|
+
.option("-t, --status-tag <string>", "Database pages without a Notion page property 'status' matching this will be ignored. Use '*' to ignore status altogether.", "Publish")
|
|
57
|
+
.option("--locales <codes>", "Comma-separated list of iso 639-2 codes, the same list as in docusaurus.config.js, minus the primary (i.e. 'en'). This is needed for image localization.", parseLocales, [])
|
|
58
|
+
.addOption(new commander_1.Option("-l, --log-level <level>", "Log level").choices([
|
|
59
|
+
"info",
|
|
60
|
+
"verbose",
|
|
61
|
+
"debug",
|
|
62
|
+
]))
|
|
63
|
+
.option("-i, --img-output-path <string>", "Path to directory where images will be stored. If this is not included, images will be placed in the same directory as the document that uses them, which then allows for localization of screenshots.")
|
|
64
|
+
.option("-p, --img-prefix-in-markdown <string>", "When referencing an image from markdown, prefix with this path instead of the full img-output-path. Should be used only in conjunction with --img-output-path.");
|
|
65
|
+
commander_1.program.showHelpAfterError();
|
|
66
|
+
commander_1.program.parse();
|
|
67
|
+
(0, log_1.setLogLevel)(commander_1.program.opts().logLevel);
|
|
68
|
+
console.log(JSON.stringify(commander_1.program.opts()));
|
|
69
|
+
// copy in the this version of the css needed to make columns (and maybe other things?) work
|
|
70
|
+
let pathToCss = "";
|
|
71
|
+
try {
|
|
72
|
+
pathToCss = require.resolve("@sillsdev/docu-notion/dist/docu-notion-styles.css");
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
// when testing from the docu-notion project itself:
|
|
76
|
+
pathToCss = "./src/css/docu-notion-styles.css";
|
|
77
|
+
}
|
|
78
|
+
// make any missing parts of the path exist
|
|
79
|
+
fs.ensureDirSync(commander_1.program.opts().cssOutputDirectory);
|
|
80
|
+
fs.copyFileSync(pathToCss, path_1.default.join(commander_1.program.opts().cssOutputDirectory, "docu-notion-styles.css"));
|
|
81
|
+
// pull and convert
|
|
82
|
+
yield (0, pull_1.notionPull)(commander_1.program.opts()).then(() => console.log("docu-notion Finished."));
|
|
83
|
+
});
|
|
31
84
|
}
|
|
32
85
|
exports.run = run;
|
|
33
86
|
function parseLocales(value) {
|
package/dist/transform.js
CHANGED
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.getMarkdownFromNotionBlocks = exports.getMarkdownForPage = void 0;
|
|
16
16
|
const chalk_1 = __importDefault(require("chalk"));
|
|
17
17
|
const log_1 = require("./log");
|
|
18
|
+
const pull_1 = require("./pull");
|
|
18
19
|
function getMarkdownForPage(config, context, page) {
|
|
19
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
21
|
(0, log_1.info)(`Reading & converting page ${page.layoutContext}/${page.nameOrTitle} (${chalk_1.default.blue(page.hasExplicitSlug
|
|
@@ -44,9 +45,12 @@ function getMarkdownFromNotionBlocks(context, config, blocks) {
|
|
|
44
45
|
markdown = doLinkFixes(context, markdown, config);
|
|
45
46
|
//console.log("markdown after link fixes", markdown);
|
|
46
47
|
// simple regex-based tweaks. These are usually related to docusaurus
|
|
47
|
-
const
|
|
48
|
+
const body = yield doTransformsOnMarkdown(context, config, markdown);
|
|
48
49
|
// console.log("markdown after regex fixes", markdown);
|
|
49
50
|
// console.log("body after regex", body);
|
|
51
|
+
const uniqueImports = [...new Set(context.imports)];
|
|
52
|
+
const imports = uniqueImports.join("\n");
|
|
53
|
+
context.imports = []; // reset for next page
|
|
50
54
|
return `${imports}\n${body}`;
|
|
51
55
|
});
|
|
52
56
|
}
|
|
@@ -65,7 +69,6 @@ function doNotionBlockTransforms(blocks, config) {
|
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
function doTransformsOnMarkdown(context, config, input) {
|
|
68
|
-
var _a;
|
|
69
72
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
73
|
const regexMods = config.plugins
|
|
71
74
|
.filter(plugin => !!plugin.regexMarkdownModifications)
|
|
@@ -81,7 +84,6 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
81
84
|
let body = input;
|
|
82
85
|
//console.log("body before regex: " + body);
|
|
83
86
|
let match;
|
|
84
|
-
const imports = new Set();
|
|
85
87
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
86
88
|
for (const mod of regexMods) {
|
|
87
89
|
let replacement = undefined;
|
|
@@ -113,20 +115,31 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
113
115
|
precedingPart +
|
|
114
116
|
partStartingFromThisMatch.replace(original, replacement);
|
|
115
117
|
// add any library imports
|
|
116
|
-
(
|
|
118
|
+
if (!context.imports)
|
|
119
|
+
context.imports = [];
|
|
120
|
+
context.imports.push(...(mod.imports || []));
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
(0, log_1.logDebug)("doTransformsOnMarkdown", "body after regex: " + body);
|
|
122
|
-
|
|
123
|
-
return { body, imports: [...uniqueImports].join("\n") };
|
|
126
|
+
return body;
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
129
|
function doNotionToMarkdown(docunotionContext, blocks) {
|
|
127
130
|
return __awaiter(this, void 0, void 0, function* () {
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
let mdBlocks;
|
|
132
|
+
yield (0, pull_1.executeWithRateLimitAndRetries)("notionToMarkdown.blocksToMarkdown", () => __awaiter(this, void 0, void 0, function* () {
|
|
133
|
+
mdBlocks = yield docunotionContext.notionToMarkdown.blocksToMarkdown(
|
|
134
|
+
// We need to provide a copy of blocks.
|
|
135
|
+
// Calling blocksToMarkdown can modify the values in the blocks. If it does, and then
|
|
136
|
+
// we have to retry, we end up retrying with the modified values, which
|
|
137
|
+
// causes various issues (like using the transformed image url instead of the original one).
|
|
138
|
+
// Note, currently, we don't do anything else with blocks after this.
|
|
139
|
+
// If that changes, we'll need to figure out a more sophisticated approach.
|
|
140
|
+
JSON.parse(JSON.stringify(blocks)));
|
|
141
|
+
}));
|
|
142
|
+
const markdown = docunotionContext.notionToMarkdown.toMarkdownString(mdBlocks).parent || "";
|
|
130
143
|
return markdown;
|
|
131
144
|
});
|
|
132
145
|
}
|
package/package.json
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
"// typescript check": "",
|
|
12
12
|
"tsc": "tsc",
|
|
13
13
|
"// test out with a private sample notion db": "",
|
|
14
|
-
"large-site-test": "npm run ts -- -n $SIL_BLOOM_DOCS_NOTION_TOKEN -r $SIL_BLOOM_DOCS_NOTION_ROOT_PAGE --locales en,fr",
|
|
14
|
+
"large-site-test": "npm run ts -- -n $SIL_BLOOM_DOCS_NOTION_TOKEN -r $SIL_BLOOM_DOCS_NOTION_ROOT_PAGE --locales en,fr --log-level debug",
|
|
15
15
|
"pull-test-tagged": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_TEST_ROOT_PAGE_ID --log-level debug --status-tag test",
|
|
16
|
+
"pull-test-css": "npm run ts -- --css-output-directory ./test/css -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_TEST_ROOT_PAGE_ID --log-level debug --status-tag test",
|
|
16
17
|
"pull-sample-site": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_SAMPLE_ROOT_PAGE --log-level debug",
|
|
17
18
|
"// test with a semi-stable/public site:": "",
|
|
18
19
|
"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",
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"markdown-table": "^2.0.0",
|
|
36
37
|
"node-fetch": "2.6.6",
|
|
37
38
|
"notion-client": "^4",
|
|
38
|
-
"notion-to-md": "
|
|
39
|
+
"notion-to-md": "3.1.1",
|
|
39
40
|
"path": "^0.12.7",
|
|
40
41
|
"ts-node": "^10.2.1",
|
|
41
42
|
"sanitize-filename": "^1.6.3"
|
|
@@ -90,5 +91,5 @@
|
|
|
90
91
|
"volta": {
|
|
91
92
|
"node": "18.16.0"
|
|
92
93
|
},
|
|
93
|
-
"version": "0.
|
|
94
|
+
"version": "0.14.0-alpha.10"
|
|
94
95
|
}
|
|
@@ -1,55 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,86 +0,0 @@
|
|
|
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
|
-
}));
|
|
File without changes
|