@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
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ Example Site: https://sillsdev.github.io/docu-notion-sample-site/
|
|
|
10
10
|
|
|
11
11
|
First, prepare your markdown-based static file system like [Docusaurus](https://docusaurus.io/). For a shortcut with github actions, search, and deployment to github pages, you can just copy [this template](https://github.com/sillsdev/docu-notion-sample-site).
|
|
12
12
|
|
|
13
|
+
If you do not use the above sample, you will need to manually add [notion-styles.css](src/css/notion-styles.css) to your site. This enables multi-column layouts and another other special styling that we need to make the output of docu-notion look right.
|
|
14
|
+
|
|
13
15
|
## 2. In Notion, duplicate the docu-notion template
|
|
14
16
|
|
|
15
17
|
Go to [this template page](https://hattonjohn.notion.site/Documentation-Template-Docusaurus-0e998b32da3c47edad0f62a25b49818c). Duplicate it into your own workspace.
|
|
@@ -17,13 +19,13 @@ You can name it anything you like, e.g. "Documentation Root".
|
|
|
17
19
|
|
|
18
20
|
## 3. Create a Notion Integration
|
|
19
21
|
|
|
20
|
-
In order for docu-notion to read your site via Notion's API, you need to create what Notion calls an "integration". Follow [these instructions](https://developers.notion.com/docs/getting-started) to make an integration and get your token.
|
|
22
|
+
In order for docu-notion to read your site via Notion's API, you need to create what Notion calls an "integration". Follow [these instructions](https://developers.notion.com/docs/getting-started) to make an integration and get your token. Remember to limit your integration to "READ" access.
|
|
21
23
|
|
|
22
|
-
## 4.
|
|
24
|
+
## 4. Connect your Integration
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
Go to the page that will be the root of your site. This page should have, as direct children, your "Outline" (required) and "Database" (optional) pages. Follow [these instructions](https://developers.notion.com/docs/create-a-notion-integration#give-your-integration-page-permissions).
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
<img width="318" alt="image" src="https://github.com/sillsdev/docu-notion/assets/8448/810c6dca-f9ab-4370-93b4-dc1479332af7">
|
|
27
29
|
|
|
28
30
|
## 5. Add your pages under your Outline page.
|
|
29
31
|
|
|
@@ -35,7 +37,7 @@ First, determine the id of your root page by clicking "Share" and looking at the
|
|
|
35
37
|
https://www.notion.so/hattonjohn/My-Docs-0456aa5842946bdbea3a4f37c97a0e5
|
|
36
38
|
means that the id is "0456aa5842946PRETEND4f37c97a0e5".
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
Try it out:
|
|
39
41
|
|
|
40
42
|
```
|
|
41
43
|
npx @sillsdev/docu-notion -n secret_PRETEND123456789PRETEND123456789PRETEND6789 -r 0456aa5842946PRETEND4f37c97a0e5"
|
|
@@ -8,8 +8,8 @@ const ColumnListTransformer_1 = require("../plugins/ColumnListTransformer");
|
|
|
8
8
|
const ColumnTransformer_1 = require("../plugins/ColumnTransformer");
|
|
9
9
|
const EscapeHtmlBlockModifier_1 = require("../plugins/EscapeHtmlBlockModifier");
|
|
10
10
|
const HeadingTransformer_1 = require("../plugins/HeadingTransformer");
|
|
11
|
-
const NumberedListTransformer_1 = require("../plugins/NumberedListTransformer");
|
|
12
11
|
const TableTransformer_1 = require("../plugins/TableTransformer");
|
|
12
|
+
const VideoTransformer_1 = require("../plugins/VideoTransformer");
|
|
13
13
|
const externalLinks_1 = require("../plugins/externalLinks");
|
|
14
14
|
const defaultConfig = {
|
|
15
15
|
plugins: [
|
|
@@ -23,15 +23,13 @@ const defaultConfig = {
|
|
|
23
23
|
images_1.standardImageTransformer,
|
|
24
24
|
CalloutTransformer_1.standardCalloutTransformer,
|
|
25
25
|
TableTransformer_1.standardTableTransformer,
|
|
26
|
-
|
|
26
|
+
VideoTransformer_1.standardVideoTransformer,
|
|
27
27
|
// Link modifiers, which are special because they can read metadata from all the pages in order to figure out the correct url
|
|
28
28
|
internalLinks_1.standardInternalLinkConversion,
|
|
29
29
|
externalLinks_1.standardExternalLinkConversion,
|
|
30
30
|
// Regexps plus javascript `import`s that operate on the Markdown output
|
|
31
31
|
embedTweaks_1.imgurGifEmbed,
|
|
32
32
|
embedTweaks_1.gifEmbed,
|
|
33
|
-
embedTweaks_1.youtubeEmbed,
|
|
34
|
-
embedTweaks_1.vimeoEmbed,
|
|
35
33
|
],
|
|
36
34
|
};
|
|
37
35
|
exports.default = defaultConfig;
|
|
@@ -1,58 +1,60 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
https://github1s.com/NotionX/react-notion-x/blob/master/packages/react-notion-x/src/styles.css#
|
|
9
|
-
|
|
10
|
-
.notion-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
margin-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
margin-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
1
|
+
/* This should be added to the docusaurus.config.js in order to show some notion things correctly.
|
|
2
|
+
See the option: --css-output-directory
|
|
3
|
+
See the docusaurus docs: https://docusaurus.io/docs/styling-layout
|
|
4
|
+
See the use in the docu-notion-sample-site: https://github.com/sillsdev/docu-notion-sample-site/blob/main/docusaurus.config.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* Copied from
|
|
8
|
+
https://github1s.com/NotionX/react-notion-x/blob/master/packages/react-notion-x/src/styles.css#L934
|
|
9
|
+
and
|
|
10
|
+
https://github1s.com/NotionX/react-notion-x/blob/master/packages/react-notion-x/src/styles.css#L1063
|
|
11
|
+
*/
|
|
12
|
+
.notion-column {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
padding-top: 12px;
|
|
16
|
+
padding-bottom: 12px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.notion-column > *:first-child {
|
|
20
|
+
margin-top: 0;
|
|
21
|
+
margin-left: 0;
|
|
22
|
+
margin-right: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.notion-column > *:last-child {
|
|
26
|
+
margin-left: 0;
|
|
27
|
+
margin-right: 0;
|
|
28
|
+
margin-bottom: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.notion-row {
|
|
32
|
+
display: flex;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
width: 100%;
|
|
35
|
+
max-width: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@media (max-width: 640px) {
|
|
39
|
+
.notion-row {
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.notion-row .notion-column {
|
|
44
|
+
width: 100% !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.notion-row .notion-spacer {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.notion-spacer {
|
|
53
|
+
/* This matches the value in ColumnTransformer.ts */
|
|
54
|
+
width: calc(min(32px, 4vw));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.notion-spacer:last-child {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
/* End copied from NotionX */
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const notion_to_md_1 = require("notion-to-md");
|
|
16
|
+
const HierarchicalNamedLayoutStrategy_1 = require("./HierarchicalNamedLayoutStrategy");
|
|
17
|
+
const transform_1 = require("./transform");
|
|
18
|
+
const internalLinks_1 = require("./plugins/internalLinks");
|
|
19
|
+
const pull_1 = require("./pull");
|
|
20
|
+
const default_docunotion_config_1 = __importDefault(require("./config/default.docunotion.config"));
|
|
21
|
+
test("Latex Rendering", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
const pages = new Array();
|
|
23
|
+
const counts = {
|
|
24
|
+
output_normally: 0,
|
|
25
|
+
skipped_because_empty: 0,
|
|
26
|
+
skipped_because_status: 0,
|
|
27
|
+
skipped_because_level_cannot_have_content: 0,
|
|
28
|
+
};
|
|
29
|
+
const notionClient = (0, pull_1.initNotionClient)("");
|
|
30
|
+
const layoutStrategy = new HierarchicalNamedLayoutStrategy_1.HierarchicalNamedLayoutStrategy();
|
|
31
|
+
const config = default_docunotion_config_1.default;
|
|
32
|
+
const context = {
|
|
33
|
+
getBlockChildren: (id) => {
|
|
34
|
+
return new Promise(resolve => resolve(new Array()));
|
|
35
|
+
},
|
|
36
|
+
directoryContainingMarkdown: "",
|
|
37
|
+
relativeFilePathToFolderContainingPage: "",
|
|
38
|
+
layoutStrategy: layoutStrategy,
|
|
39
|
+
notionToMarkdown: new notion_to_md_1.NotionToMarkdown({ notionClient }),
|
|
40
|
+
options: {
|
|
41
|
+
notionToken: "",
|
|
42
|
+
rootPage: "",
|
|
43
|
+
locales: [""],
|
|
44
|
+
markdownOutputPath: "",
|
|
45
|
+
imgOutputPath: "",
|
|
46
|
+
imgPrefixInMarkdown: "",
|
|
47
|
+
statusTag: "",
|
|
48
|
+
},
|
|
49
|
+
pages: pages,
|
|
50
|
+
counts: counts,
|
|
51
|
+
imports: [],
|
|
52
|
+
convertNotionLinkToLocalDocusaurusLink: (url) => (0, internalLinks_1.convertInternalUrl)(context, url),
|
|
53
|
+
};
|
|
54
|
+
const blocks = [
|
|
55
|
+
{
|
|
56
|
+
object: "block",
|
|
57
|
+
id: "169e1c47-6706-4518-adca-73086b2738ac",
|
|
58
|
+
parent: {
|
|
59
|
+
type: "page_id",
|
|
60
|
+
page_id: "2acc11a4-82a9-4759-b429-fa011c164888",
|
|
61
|
+
},
|
|
62
|
+
created_time: "2023-08-18T15:51:00.000Z",
|
|
63
|
+
last_edited_time: "2023-08-18T15:51:00.000Z",
|
|
64
|
+
created_by: {
|
|
65
|
+
object: "user",
|
|
66
|
+
id: "af5c163e-82b1-49d1-9f1c-539907bb9fb9",
|
|
67
|
+
},
|
|
68
|
+
last_edited_by: {
|
|
69
|
+
object: "user",
|
|
70
|
+
id: "af5c163e-82b1-49d1-9f1c-539907bb9fb9",
|
|
71
|
+
},
|
|
72
|
+
has_children: false,
|
|
73
|
+
archived: false,
|
|
74
|
+
type: "paragraph",
|
|
75
|
+
paragraph: {
|
|
76
|
+
rich_text: [
|
|
77
|
+
{
|
|
78
|
+
type: "equation",
|
|
79
|
+
equation: { expression: "x" },
|
|
80
|
+
annotations: {
|
|
81
|
+
bold: false,
|
|
82
|
+
italic: false,
|
|
83
|
+
strikethrough: false,
|
|
84
|
+
underline: false,
|
|
85
|
+
code: false,
|
|
86
|
+
color: "default",
|
|
87
|
+
},
|
|
88
|
+
plain_text: "x",
|
|
89
|
+
href: null,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
color: "default",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
expect(yield (0, transform_1.getMarkdownFromNotionBlocks)(context, config, blocks)).toContain("$x$");
|
|
97
|
+
}));
|
|
@@ -28,16 +28,14 @@ function notionColumnToMarkdown(notionToMarkdown, getBlockChildren, block) {
|
|
|
28
28
|
const { id, has_children } = block; // "any" because the notion api type system is complex with a union that don't know how to help TS to cope with
|
|
29
29
|
if (!has_children)
|
|
30
30
|
return "";
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
31
|
+
const columnChildren = yield getBlockChildren(id);
|
|
32
|
+
const childrenMdBlocksArray = yield Promise.all(columnChildren.map((child) => __awaiter(this, void 0, void 0, function* () { return yield notionToMarkdown.blocksToMarkdown([child]); })));
|
|
33
|
+
const childrenMarkdown = childrenMdBlocksArray.map(mdBlockArray => notionToMarkdown.toMarkdownString(mdBlockArray).parent);
|
|
34
34
|
const columnWidth = yield getColumnWidth(block);
|
|
35
|
-
|
|
36
|
-
// causes notion-to-md to give us ":::A" instead of \n for some reason.
|
|
37
|
-
return (`<div class='notion-column' style={{width: '${columnWidth}'}}>\n\n${childrenStrings.join("\n\n")}\n\n</div>` +
|
|
35
|
+
return (`<div class='notion-column' style={{width: '${columnWidth}'}}>\n\n${childrenMarkdown.join("\n")}\n</div>` +
|
|
38
36
|
// Spacer between columns. CSS takes care of hiding this for the last column
|
|
39
37
|
// and when the screen is too narrow for multiple columns.
|
|
40
|
-
`<div className='notion-spacer'
|
|
38
|
+
`<div className='notion-spacer'></div>`);
|
|
41
39
|
});
|
|
42
40
|
}
|
|
43
41
|
// The official API doesn't give us access to the format information, including column_ratio.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
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 ColumnTransformer_1 = require("./ColumnTransformer");
|
|
14
|
+
// Even though we can set up most tests with our own children
|
|
15
|
+
// so that we aren't relying on real data from Notion,
|
|
16
|
+
// we can't prevent the notion-to-md library from making an API call
|
|
17
|
+
// every time it processes a block with has_children:true.
|
|
18
|
+
// So for these tests with children, we need any valid API key.
|
|
19
|
+
const runTestsWhichRequireAnyValidApiKey = !!process.env.DOCU_NOTION_INTEGRATION_TOKEN;
|
|
20
|
+
// To test grandchildren, we can't get around notion-to-md making an API call
|
|
21
|
+
// to get real children. So we need a specific notion record.
|
|
22
|
+
// For that reason, we don't try to run these tests unless the user changes this flag.
|
|
23
|
+
// But it is an important test; grandchildren in columns were broken.
|
|
24
|
+
// See https://github.com/sillsdev/docu-notion/issues/70.
|
|
25
|
+
const runManualTestsWhichRequireSpecificNotionRecords = false;
|
|
26
|
+
const columnBlock = {
|
|
27
|
+
object: "block",
|
|
28
|
+
id: "e6d2d7b7-b1ed-464a-86d2-bb5f6be78a03",
|
|
29
|
+
has_children: true,
|
|
30
|
+
type: "column",
|
|
31
|
+
column: {},
|
|
32
|
+
};
|
|
33
|
+
function getResults(children) {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
return yield (0, pluginTestRun_1.blocksToMarkdown)({ plugins: [ColumnTransformer_1.standardColumnTransformer] }, [columnBlock], undefined, children, process.env.DOCU_NOTION_INTEGRATION_TOKEN);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const columnWrapperStart = "<div class='notion-column' style=\\{\\{width: '.*?'\\}\\}>\\n\\n";
|
|
39
|
+
const columnWrapperEnd = "\\n\\n<\\/div><div className='notion-spacer'><\\/div>";
|
|
40
|
+
if (runTestsWhichRequireAnyValidApiKey) {
|
|
41
|
+
columnBlock.has_children = true;
|
|
42
|
+
test("requires API key - column with paragraph", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
const results = yield getResults([getTestParagraphBlock()]);
|
|
44
|
+
expect(results).toMatch(new RegExp(`${columnWrapperStart}\\s*?my paragraph\\s*?${columnWrapperEnd}`));
|
|
45
|
+
}), 20000);
|
|
46
|
+
test("requires API key - column with two paragraphs", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
const results = yield getResults([
|
|
48
|
+
getTestParagraphBlock(1),
|
|
49
|
+
getTestParagraphBlock(2),
|
|
50
|
+
]);
|
|
51
|
+
expect(results).toMatch(new RegExp(`${columnWrapperStart}\\s*?my paragraph 1\\s+?my paragraph 2\\s*?${columnWrapperEnd}`));
|
|
52
|
+
}), 20000);
|
|
53
|
+
test("requires API key - column with numbered list", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
const results = yield getResults([
|
|
55
|
+
getNumberedListItemBlock(1),
|
|
56
|
+
getNumberedListItemBlock(2),
|
|
57
|
+
]);
|
|
58
|
+
expect(results).toMatch(new RegExp(`${columnWrapperStart}\\s*?1\\. Numbered list item 1\\s+?2\\. Numbered list item 2\\s*?${columnWrapperEnd}`, "s"));
|
|
59
|
+
}), 20000);
|
|
60
|
+
if (runManualTestsWhichRequireSpecificNotionRecords) {
|
|
61
|
+
test("manual test - requires specific notion record and API key - column with numbered list with sublist", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
+
const realNumberedListBlock = getNumberedListItemBlock(1);
|
|
63
|
+
realNumberedListBlock.id = "ca08d14b-9b70-4f6f-9d17-9fd74b57afeb";
|
|
64
|
+
realNumberedListBlock.has_children = true;
|
|
65
|
+
const results = yield getResults([realNumberedListBlock]);
|
|
66
|
+
expect(results).toMatch(new RegExp(`${columnWrapperStart}\\s*?1\\. Numbered list item 1\\s+?- unordered sub-bullet\\s*?${columnWrapperEnd}`, "s"));
|
|
67
|
+
}), 20000);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// This test prevents an error when runTestsWhichRequireAnyValidApiKey is false
|
|
72
|
+
// due to having a test suite with no tests.
|
|
73
|
+
test("no column transformer tests were run because there is no API key provided", () => {
|
|
74
|
+
expect(true).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function getNumberedListItemBlock(identifier) {
|
|
78
|
+
const content = identifier
|
|
79
|
+
? `Numbered list item ${identifier}`
|
|
80
|
+
: `Numbered list item`;
|
|
81
|
+
return {
|
|
82
|
+
object: "block",
|
|
83
|
+
type: "numbered_list_item",
|
|
84
|
+
numbered_list_item: {
|
|
85
|
+
rich_text: [
|
|
86
|
+
{
|
|
87
|
+
type: "text",
|
|
88
|
+
text: { content: content },
|
|
89
|
+
annotations: {
|
|
90
|
+
code: false,
|
|
91
|
+
},
|
|
92
|
+
plain_text: content,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function getTestParagraphBlock(identifier) {
|
|
99
|
+
const content = identifier ? `my paragraph ${identifier}` : `my paragraph`;
|
|
100
|
+
return {
|
|
101
|
+
object: "block",
|
|
102
|
+
type: "paragraph",
|
|
103
|
+
paragraph: {
|
|
104
|
+
rich_text: [
|
|
105
|
+
{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: {
|
|
108
|
+
content: content,
|
|
109
|
+
},
|
|
110
|
+
annotations: {
|
|
111
|
+
code: false,
|
|
112
|
+
},
|
|
113
|
+
plain_text: content,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.standardVideoTransformer = void 0;
|
|
4
|
+
const log_1 = require("../log");
|
|
5
|
+
exports.standardVideoTransformer = {
|
|
6
|
+
name: "video",
|
|
7
|
+
notionToMarkdownTransforms: [
|
|
8
|
+
{
|
|
9
|
+
type: "video",
|
|
10
|
+
getStringFromBlock: (context, block) => {
|
|
11
|
+
const video = block.video;
|
|
12
|
+
let url = "";
|
|
13
|
+
switch (video.type) {
|
|
14
|
+
case "external":
|
|
15
|
+
url = video.external.url;
|
|
16
|
+
break;
|
|
17
|
+
case "file":
|
|
18
|
+
url = video.file.url;
|
|
19
|
+
break;
|
|
20
|
+
default:
|
|
21
|
+
// video.type can only be "external" or "file" as of the writing of this code, so typescript
|
|
22
|
+
// isn't happy trying to turn video.type into a string. But this default in our switch is
|
|
23
|
+
// just attempting some future-proofing. Thus the strange typing/stringifying below.
|
|
24
|
+
(0, log_1.warning)(`[standardVideoTransformer] Found Notion "video" block with type ${JSON.stringify(video.type)}. The best docu-notion can do for now is ignore it.`);
|
|
25
|
+
return "";
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
context.imports.push(`import ReactPlayer from "react-player";`);
|
|
29
|
+
return `<ReactPlayer controls url="${url}" />`;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
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 VideoTransformer_1 = require("./VideoTransformer");
|
|
14
|
+
const pluginTestRun_1 = require("./pluginTestRun");
|
|
15
|
+
test("youtube embedded", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const config = { plugins: [VideoTransformer_1.standardVideoTransformer] };
|
|
17
|
+
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
18
|
+
{
|
|
19
|
+
object: "block",
|
|
20
|
+
type: "video",
|
|
21
|
+
video: {
|
|
22
|
+
caption: [
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
text: {
|
|
26
|
+
content: "A video about editing in Notion",
|
|
27
|
+
link: null,
|
|
28
|
+
},
|
|
29
|
+
plain_text: "A video about editing in Notion",
|
|
30
|
+
href: null,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
type: "external",
|
|
34
|
+
external: { url: "https://www.youtube.com/watch?v=FXIrojSK3Jo" },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
expect(result).toContain(`import ReactPlayer from "react-player";`);
|
|
39
|
+
expect(result).toContain(`<ReactPlayer controls url="https://www.youtube.com/watch?v=FXIrojSK3Jo" />`);
|
|
40
|
+
}));
|
|
41
|
+
test("vimeo embedded", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
(0, log_1.setLogLevel)("verbose");
|
|
43
|
+
const config = { plugins: [VideoTransformer_1.standardVideoTransformer] };
|
|
44
|
+
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
45
|
+
{
|
|
46
|
+
object: "block",
|
|
47
|
+
type: "video",
|
|
48
|
+
video: {
|
|
49
|
+
caption: [],
|
|
50
|
+
type: "external",
|
|
51
|
+
external: { url: "https://vimeo.com/4613611xx" },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
expect(result).toContain(`import ReactPlayer from "react-player";`);
|
|
56
|
+
expect(result).toContain(`<ReactPlayer controls url="https://vimeo.com/4613611xx" />`);
|
|
57
|
+
}));
|
|
58
|
+
test("video link, not embedded", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
(0, log_1.setLogLevel)("verbose");
|
|
60
|
+
const config = { plugins: [VideoTransformer_1.standardVideoTransformer] };
|
|
61
|
+
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
62
|
+
{
|
|
63
|
+
object: "block",
|
|
64
|
+
type: "paragraph",
|
|
65
|
+
paragraph: {
|
|
66
|
+
rich_text: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: {
|
|
70
|
+
content: "https://vimeo.com/4613611xx",
|
|
71
|
+
link: {
|
|
72
|
+
url: "https://vimeo.com/4613611xx",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
annotations: {
|
|
76
|
+
code: false,
|
|
77
|
+
},
|
|
78
|
+
plain_text: "https://vimeo.com/4613611xx",
|
|
79
|
+
href: "https://vimeo.com/4613611xx",
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
color: "default",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
expect(result).toContain("[https://vimeo.com/4613611xx](https://vimeo.com/4613611xx)");
|
|
87
|
+
expect(result).not.toContain(`import`);
|
|
88
|
+
}));
|
|
89
|
+
test("direct upload to to Notion (embedded)", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
(0, log_1.setLogLevel)("verbose");
|
|
91
|
+
const config = { plugins: [VideoTransformer_1.standardVideoTransformer] };
|
|
92
|
+
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
93
|
+
{
|
|
94
|
+
object: "block",
|
|
95
|
+
id: "12f7db3b-4412-4be9-a3f7-6ac423fee94a",
|
|
96
|
+
parent: {
|
|
97
|
+
type: "page_id",
|
|
98
|
+
page_id: "edaffeb2-ece8-4d44-976f-351e6b5757bb",
|
|
99
|
+
},
|
|
100
|
+
type: "video",
|
|
101
|
+
video: {
|
|
102
|
+
caption: [],
|
|
103
|
+
type: "file",
|
|
104
|
+
file: {
|
|
105
|
+
url: "https://s3.us-west-2.amazonaws.com/secure.notion-static.com/f6bc4746-011e-2124-86ca-ed4337d70891/people_fre_motionAsset_p3.mp4?X-Blah-blah",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
]);
|
|
110
|
+
expect(result).toContain(`import ReactPlayer from "react-player";`);
|
|
111
|
+
expect(result).toContain(`<ReactPlayer controls url="https://s3.us-west-2.amazonaws.com/secure.notion-static.com/f6bc4746-011e-2124-86ca-ed4337d70891/people_fre_motionAsset_p3.mp4?X-Blah-blah" />`);
|
|
112
|
+
}));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.imgurGifEmbed = exports.gifEmbed = void 0;
|
|
4
4
|
exports.gifEmbed = {
|
|
5
5
|
name: "gif",
|
|
6
6
|
regexMarkdownModifications: [
|
|
7
7
|
{
|
|
8
8
|
// I once saw a gif coming from Notion that wasn't a full
|
|
9
9
|
// url, which wouldn't work, hence the "http" requirement
|
|
10
|
-
regex: /\[
|
|
10
|
+
regex: /\[.*?\]\((http.*?(\.(gif|GIF)))\)/,
|
|
11
11
|
replacementPattern: ``,
|
|
12
12
|
},
|
|
13
13
|
],
|
|
@@ -16,31 +16,9 @@ exports.imgurGifEmbed = {
|
|
|
16
16
|
name: "imgur",
|
|
17
17
|
regexMarkdownModifications: [
|
|
18
18
|
{
|
|
19
|
-
regex: /\[
|
|
19
|
+
regex: /\[.*?\]\((.*?imgur\.com\/.*?)\)/,
|
|
20
20
|
// imgur links to gifs need a .gif at the end, but the url they give you doesn't have one.
|
|
21
21
|
replacementPattern: ``,
|
|
22
22
|
},
|
|
23
23
|
],
|
|
24
24
|
};
|
|
25
|
-
exports.youtubeEmbed = {
|
|
26
|
-
name: "youtube",
|
|
27
|
-
regexMarkdownModifications: [
|
|
28
|
-
{
|
|
29
|
-
regex: /\[.*\]\((.*youtube\.com\/watch.*)\)/,
|
|
30
|
-
imports: [`import ReactPlayer from "react-player";`],
|
|
31
|
-
replacementPattern: `<ReactPlayer controls url="$1" />`,
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
};
|
|
35
|
-
exports.vimeoEmbed = {
|
|
36
|
-
name: "vimeo",
|
|
37
|
-
regexMarkdownModifications: [
|
|
38
|
-
{
|
|
39
|
-
regex: /\[.*\]\((https:\/\/.*vimeo.*)\)/,
|
|
40
|
-
// we use to have the following, but the above should handle both the player an not-player urls.
|
|
41
|
-
//regex: /\[.*\]\((.*player\.vimeo.*)\)/gm, // player.vimeo
|
|
42
|
-
imports: [`import ReactPlayer from "react-player";`],
|
|
43
|
-
replacementPattern: `<ReactPlayer controls url="$1" />`,
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
};
|