@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.
@@ -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
- function blocksToMarkdown(config, blocks, pages) {
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({ auth: "unused" });
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",
@@ -40,5 +40,6 @@ export type IDocuNotionContext = {
40
40
  convertNotionLinkToLocalDocusaurusLink: (url: string) => string | undefined;
41
41
  pages: NotionPage[];
42
42
  counts: ICounts;
43
+ imports: string[];
43
44
  };
44
45
  export {};
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, 3) + "...";
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.info)(`While doing "${label}", got error "${e.message}". Will retry after ${secondsToWait}s...`);
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
- return (_b = overallResult === null || overallResult === void 0 ? void 0 : overallResult.results) !== null && _b !== void 0 ? _b : [];
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
- const pkg = require("../package.json");
9
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
10
- console.log(`docu-notion version ${pkg.version}`);
11
- commander_1.program.name("docu-notion").description("");
12
- commander_1.program.usage("-n <token> -r <root> [options]");
13
- commander_1.program
14
- .requiredOption("-n, --notion-token <string>", "notion api token, which looks like secret_3bc1b50XFYb15123RHF243x43450XFY33250XFYa343")
15
- .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'")
16
- .option("-m, --markdown-output-path <string>", "Root of the hierarchy for md files. WARNING: node-pull-mdx will delete files from this directory. Note also that if it finds localized images, it will create an i18n/ directory as a sibling.", "./docs")
17
- .option("-t, --status-tag <string>", "Database pages without a Notion page property 'status' matching this will be ignored. Use '*' to ignore status altogether.", "Publish")
18
- .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, [])
19
- .addOption(new commander_1.Option("-l, --log-level <level>", "Log level").choices([
20
- "info",
21
- "verbose",
22
- "debug",
23
- ]))
24
- .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.")
25
- .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.");
26
- commander_1.program.showHelpAfterError();
27
- commander_1.program.parse();
28
- (0, log_1.setLogLevel)(commander_1.program.opts().logLevel);
29
- console.log(JSON.stringify(commander_1.program.opts));
30
- (0, pull_1.notionPull)(commander_1.program.opts()).then(() => console.log("docu-notion Finished."));
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 { imports, body } = yield doTransformsOnMarkdown(context, config, markdown);
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
- (_a = mod.imports) === null || _a === void 0 ? void 0 : _a.forEach(imp => imports.add(imp));
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
- const uniqueImports = [...new Set(imports)];
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
- const mdBlocks = yield docunotionContext.notionToMarkdown.blocksToMarkdown(blocks);
129
- const markdown = docunotionContext.notionToMarkdown.toMarkdownString(mdBlocks);
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": "2.5.5",
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.13.4"
94
+ "version": "0.14.0-alpha.10"
94
95
  }
@@ -1,2 +0,0 @@
1
- import { IPlugin } from "./pluginTypes";
2
- export declare const standardNumberedListTransformer: IPlugin;
@@ -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
- }));