@sillsdev/docu-notion 0.16.0 → 0.16.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets.d.ts +6 -0
- package/dist/assets.js +50 -0
- package/dist/images.js +8 -20
- package/dist/plugins/VideoTransformer.js +91 -21
- package/dist/plugins/VideoTransformer.spec.js +73 -2
- package/dist/plugins/internalLinks.js +5 -2
- package/dist/plugins/internalLinks.spec.js +110 -2
- package/dist/plugins/pluginTestRun.d.ts +2 -1
- package/dist/plugins/pluginTestRun.js +5 -4
- package/dist/pull.js +1 -1
- package/dist/transform.js +1 -1
- package/package.json +1 -1
package/dist/assets.d.ts
ADDED
package/dist/assets.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.writeAsset = exports.AssetType = void 0;
|
|
27
|
+
const fs = __importStar(require("fs-extra"));
|
|
28
|
+
const Path = __importStar(require("path"));
|
|
29
|
+
const log_1 = require("./log");
|
|
30
|
+
var AssetType;
|
|
31
|
+
(function (AssetType) {
|
|
32
|
+
AssetType["Image"] = "image";
|
|
33
|
+
AssetType["Video"] = "video";
|
|
34
|
+
})(AssetType = exports.AssetType || (exports.AssetType = {}));
|
|
35
|
+
function writeAsset(path, buffer) {
|
|
36
|
+
// Note: it's tempting to not spend time writing this out if we already have
|
|
37
|
+
// it from a previous run. But we don't really know it's the same. A) it
|
|
38
|
+
// could just have the same name, B) it could have been previously
|
|
39
|
+
// unlocalized and thus filled with a copy of the primary language image
|
|
40
|
+
// while and now is localized.
|
|
41
|
+
if (fs.pathExistsSync(path)) {
|
|
42
|
+
(0, log_1.verbose)("Replacing asset " + path);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
(0, log_1.verbose)("Adding asset " + path);
|
|
46
|
+
fs.mkdirsSync(Path.dirname(path));
|
|
47
|
+
}
|
|
48
|
+
fs.createWriteStream(path).write(buffer); // async but we're not waiting
|
|
49
|
+
}
|
|
50
|
+
exports.writeAsset = writeAsset;
|
package/dist/images.js
CHANGED
|
@@ -39,9 +39,9 @@ exports.cleanupOldImages = exports.parseImageBlock = exports.markdownToMDImageTr
|
|
|
39
39
|
const fs = __importStar(require("fs-extra"));
|
|
40
40
|
const file_type_1 = __importDefault(require("file-type"));
|
|
41
41
|
const axios_1 = __importDefault(require("axios"));
|
|
42
|
-
const Path = __importStar(require("path"));
|
|
43
42
|
const MakeImagePersistencePlan_1 = require("./MakeImagePersistencePlan");
|
|
44
43
|
const log_1 = require("./log");
|
|
44
|
+
const assets_1 = require("./assets");
|
|
45
45
|
// We handle several things here:
|
|
46
46
|
// 1) copy images locally instead of leaving them in Notion
|
|
47
47
|
// 2) change the links to point here
|
|
@@ -142,10 +142,12 @@ function readPrimaryImage(imageSet) {
|
|
|
142
142
|
}
|
|
143
143
|
function saveImage(imageSet) {
|
|
144
144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
-
|
|
145
|
+
const path = imageSet.primaryFileOutputPath;
|
|
146
|
+
imageWasSeen(path);
|
|
147
|
+
(0, assets_1.writeAsset)(path, imageSet.primaryBuffer);
|
|
146
148
|
for (const localizedImage of imageSet.localizedUrls) {
|
|
147
149
|
let buffer = imageSet.primaryBuffer;
|
|
148
|
-
// if we have a
|
|
150
|
+
// if we have a url for the localized screenshot, download it
|
|
149
151
|
if ((localizedImage === null || localizedImage === void 0 ? void 0 : localizedImage.url.length) > 0) {
|
|
150
152
|
(0, log_1.verbose)(`Retrieving ${localizedImage.iso632Code} version...`);
|
|
151
153
|
const response = yield fetch(localizedImage.url);
|
|
@@ -157,26 +159,12 @@ function saveImage(imageSet) {
|
|
|
157
159
|
// otherwise, we're going to fall back to outputting the primary image here
|
|
158
160
|
}
|
|
159
161
|
const directory = `./i18n/${localizedImage.iso632Code}/docusaurus-plugin-content-docs/current/${imageSet.pageInfo.relativeFilePathToFolderContainingPage}`;
|
|
160
|
-
|
|
162
|
+
const newPath = (directory + "/" + imageSet.outputFileName).replaceAll("//", "/");
|
|
163
|
+
imageWasSeen(newPath);
|
|
164
|
+
(0, assets_1.writeAsset)(newPath, buffer);
|
|
161
165
|
}
|
|
162
166
|
});
|
|
163
167
|
}
|
|
164
|
-
function writeImageIfNew(path, buffer) {
|
|
165
|
-
imageWasSeen(path);
|
|
166
|
-
// Note: it's tempting to not spend time writing this out if we already have
|
|
167
|
-
// it from a previous run. But we don't really know it's the same. A) it
|
|
168
|
-
// could just have the same name, B) it could have been previously
|
|
169
|
-
// unlocalized and thus filled with a copy of the primary language image
|
|
170
|
-
// while and now is localized.
|
|
171
|
-
if (fs.pathExistsSync(path)) {
|
|
172
|
-
(0, log_1.verbose)("Replacing image " + path);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
(0, log_1.verbose)("Adding image " + path);
|
|
176
|
-
fs.mkdirsSync(Path.dirname(path));
|
|
177
|
-
}
|
|
178
|
-
fs.createWriteStream(path).write(buffer); // async but we're not waiting
|
|
179
|
-
}
|
|
180
168
|
function parseImageBlock(image) {
|
|
181
169
|
var _a;
|
|
182
170
|
if (!locales)
|
|
@@ -1,33 +1,103 @@
|
|
|
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
|
+
};
|
|
2
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
35
|
exports.standardVideoTransformer = void 0;
|
|
36
|
+
const Path = __importStar(require("path"));
|
|
4
37
|
const log_1 = require("../log");
|
|
38
|
+
const assets_1 = require("../assets");
|
|
5
39
|
exports.standardVideoTransformer = {
|
|
6
40
|
name: "video",
|
|
7
41
|
notionToMarkdownTransforms: [
|
|
8
42
|
{
|
|
9
43
|
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
|
-
},
|
|
44
|
+
getStringFromBlock: (context, block) => markdownToMDVideoTransformer(block, context),
|
|
31
45
|
},
|
|
32
46
|
],
|
|
33
47
|
};
|
|
48
|
+
function markdownToMDVideoTransformer(block, context) {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const videoBlock = block;
|
|
51
|
+
const video = videoBlock.video;
|
|
52
|
+
let url = "";
|
|
53
|
+
switch (video.type) {
|
|
54
|
+
case "external":
|
|
55
|
+
url = `"${video.external.url}"`;
|
|
56
|
+
break;
|
|
57
|
+
case "file":
|
|
58
|
+
// The url we get for a Notion-hosted asset expires after an hour, so we have to download it locally.
|
|
59
|
+
url = yield downloadVideoAndConvertUrl(context, video.file.url, videoBlock.id);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
// video.type can only be "external" or "file" as of the writing of this code, so typescript
|
|
63
|
+
// isn't happy trying to turn video.type into a string. But this default in our switch is
|
|
64
|
+
// just attempting some future-proofing. Thus the strange typing/stringifying below.
|
|
65
|
+
(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.`);
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
context.imports.push(`import ReactPlayer from "react-player";`);
|
|
69
|
+
return `<ReactPlayer controls url=${url} />`;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// ENHANCE: One day, we may want to allow for options of where to place the files, how
|
|
73
|
+
// to name them, etc. Or we could at least follow the image options.
|
|
74
|
+
// But for now, I'm just trying to fix the bug that Notion-hosted videos don't work at all.
|
|
75
|
+
function downloadVideoAndConvertUrl(context, notionVideoUrl, blockId) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
// Get the file name from the url. Ignore query parameters and fragments.
|
|
78
|
+
let newFileName = notionVideoUrl.split("?")[0].split("#")[0].split("/").pop();
|
|
79
|
+
if (!newFileName) {
|
|
80
|
+
// If something went wrong, fall back to the block ID.
|
|
81
|
+
// But at least try to get the extension from the url.
|
|
82
|
+
const extension = notionVideoUrl
|
|
83
|
+
.split("?")[0]
|
|
84
|
+
.split("#")[0]
|
|
85
|
+
.split(".")
|
|
86
|
+
.pop();
|
|
87
|
+
newFileName = blockId + (extension ? "." + extension : "");
|
|
88
|
+
}
|
|
89
|
+
const newPath = Path.posix.join(context.pageInfo.directoryContainingMarkdown, newFileName);
|
|
90
|
+
const response = yield fetch(notionVideoUrl);
|
|
91
|
+
const arrayBuffer = yield response.arrayBuffer();
|
|
92
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
93
|
+
(0, assets_1.writeAsset)(newPath, buffer);
|
|
94
|
+
// Add an import statement for the video file.
|
|
95
|
+
// Otherwise, the docusaurus build won't include the video file in the build.
|
|
96
|
+
const countVideoImports = context.imports.filter(i => {
|
|
97
|
+
return /import video\d+/.exec(i);
|
|
98
|
+
}).length;
|
|
99
|
+
const importName = `video${countVideoImports + 1}`;
|
|
100
|
+
context.imports.push(`import ${importName} from "./${newFileName}";`);
|
|
101
|
+
return `{${importName}}`;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
@@ -1,4 +1,27 @@
|
|
|
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
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -9,9 +32,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
32
|
});
|
|
10
33
|
};
|
|
11
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
const fs = __importStar(require("fs-extra"));
|
|
12
36
|
const log_1 = require("../log");
|
|
13
37
|
const VideoTransformer_1 = require("./VideoTransformer");
|
|
14
38
|
const pluginTestRun_1 = require("./pluginTestRun");
|
|
39
|
+
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
try {
|
|
41
|
+
if (yield fs.pathExists(pluginTestRun_1.kTemporaryTestDirectory)) {
|
|
42
|
+
yield fs.emptyDir(pluginTestRun_1.kTemporaryTestDirectory);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
yield fs.mkdirp(pluginTestRun_1.kTemporaryTestDirectory);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error("Error in beforeAll:", err);
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
afterAll(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
try {
|
|
54
|
+
yield fs.remove(pluginTestRun_1.kTemporaryTestDirectory);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error("Error in afterAll:", err);
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
15
60
|
test("youtube embedded", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
61
|
const config = { plugins: [VideoTransformer_1.standardVideoTransformer] };
|
|
17
62
|
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
@@ -89,6 +134,8 @@ test("video link, not embedded", () => __awaiter(void 0, void 0, void 0, functio
|
|
|
89
134
|
test("direct upload to to Notion (embedded)", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
135
|
(0, log_1.setLogLevel)("verbose");
|
|
91
136
|
const config = { plugins: [VideoTransformer_1.standardVideoTransformer] };
|
|
137
|
+
const fileName1 = "first_video.mp4";
|
|
138
|
+
const fileName2 = "second_video.mp4";
|
|
92
139
|
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
93
140
|
{
|
|
94
141
|
object: "block",
|
|
@@ -102,11 +149,35 @@ test("direct upload to to Notion (embedded)", () => __awaiter(void 0, void 0, vo
|
|
|
102
149
|
caption: [],
|
|
103
150
|
type: "file",
|
|
104
151
|
file: {
|
|
105
|
-
url:
|
|
152
|
+
url: `https://s3.us-west-2.amazonaws.com/secure.notion-static.com/f6bc4746-011e-2124-86ca-ed4337d70891/${fileName1}?X-Blah-blah`,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
object: "block",
|
|
158
|
+
id: "12f7db3b-4412-4be9-a3f7-6ac423fee94b",
|
|
159
|
+
parent: {
|
|
160
|
+
type: "page_id",
|
|
161
|
+
page_id: "edaffeb2-ece8-4d44-976f-351e6b5757bb",
|
|
162
|
+
},
|
|
163
|
+
type: "video",
|
|
164
|
+
video: {
|
|
165
|
+
caption: [],
|
|
166
|
+
type: "file",
|
|
167
|
+
file: {
|
|
168
|
+
url: `https://s3.us-west-2.amazonaws.com/secure.notion-static.com/f6bc4746-011e-2124-86ca-ed4337d70891/${fileName2}?X-Blah-blah`,
|
|
106
169
|
},
|
|
107
170
|
},
|
|
108
171
|
},
|
|
109
172
|
]);
|
|
110
173
|
expect(result).toContain(`import ReactPlayer from "react-player";`);
|
|
111
|
-
expect(result).toContain(
|
|
174
|
+
expect(result).toContain(`import video1 from "./${fileName1}";`);
|
|
175
|
+
expect(result).toContain(`import video2 from "./${fileName2}";`);
|
|
176
|
+
expect(result).toContain(`<ReactPlayer controls url={video1} />`);
|
|
177
|
+
expect(result).toContain(`<ReactPlayer controls url={video2} />`);
|
|
178
|
+
// Wait half a second for the files to be written
|
|
179
|
+
yield new Promise(resolve => setTimeout(resolve, 500));
|
|
180
|
+
// We should have actually created files in "tempTestFileDir/"
|
|
181
|
+
expect(yield fs.pathExists("tempTestFileDir/" + fileName1)).toBe(true);
|
|
182
|
+
expect(yield fs.pathExists("tempTestFileDir/" + fileName2)).toBe(true);
|
|
112
183
|
}));
|
|
@@ -29,7 +29,8 @@ function convertInternalUrl(context, url) {
|
|
|
29
29
|
exports.convertInternalUrl = convertInternalUrl;
|
|
30
30
|
// handles the whole markdown link, including the label
|
|
31
31
|
function convertInternalLink(context, markdownLink) {
|
|
32
|
-
|
|
32
|
+
// match both [foo](/123) and [bar](https://www.notion.so/123) <-- the "mention" link style
|
|
33
|
+
const linkRegExp = /\[([^\]]+)?\]\((?:https?:\/\/www\.notion\.so\/|\/)?([^),^/]+)\)/g;
|
|
33
34
|
const match = linkRegExp.exec(markdownLink);
|
|
34
35
|
if (match === null) {
|
|
35
36
|
(0, log_1.warning)(`[standardInternalLinkConversion] Could not parse link ${markdownLink}`);
|
|
@@ -96,7 +97,9 @@ exports.standardInternalLinkConversion = {
|
|
|
96
97
|
// (has some other text that's been turned into a link) or "raw".
|
|
97
98
|
// Raw links come in without a leading slash, e.g. [link_to_page](4a6de8c0-b90b-444b-8a7b-d534d6ec71a4)
|
|
98
99
|
// Inline links come in with a leading slash, e.g. [pointer to the introduction](/4a6de8c0b90b444b8a7bd534d6ec71a4)
|
|
99
|
-
|
|
100
|
+
// "Mention" links come in as full URLs, e.g. [link_to_page](https://www.notion.so/62f1187010214b0883711a1abb277d31)
|
|
101
|
+
// YOu can create them either with @+the name of a page, or by pasting a URL and then selecting the "Mention" option.
|
|
102
|
+
match: /\[([^\]]+)?\]\((?!mailto:)(https:\/\/www\.notion\.so\/[^),^/]+|\/?[^),^/]+)\)/,
|
|
100
103
|
convert: convertInternalLink,
|
|
101
104
|
},
|
|
102
105
|
};
|
|
@@ -38,6 +38,43 @@ test("urls that show up as raw text get left that way", () => __awaiter(void 0,
|
|
|
38
38
|
});
|
|
39
39
|
expect(results.trim()).toBe("https://github.com");
|
|
40
40
|
}));
|
|
41
|
+
// See https://github.com/sillsdev/docu-notion/issues/97
|
|
42
|
+
test("mention-style link to an existing page", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
const targetPageId = "123";
|
|
44
|
+
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
45
|
+
slug: undefined,
|
|
46
|
+
name: "Hello World",
|
|
47
|
+
id: targetPageId,
|
|
48
|
+
});
|
|
49
|
+
const results = yield getMarkdown({
|
|
50
|
+
type: "paragraph",
|
|
51
|
+
paragraph: {
|
|
52
|
+
rich_text: [
|
|
53
|
+
{
|
|
54
|
+
type: "mention",
|
|
55
|
+
mention: {
|
|
56
|
+
type: "page",
|
|
57
|
+
page: {
|
|
58
|
+
id: `${targetPageId}`,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
annotations: {
|
|
62
|
+
bold: false,
|
|
63
|
+
italic: false,
|
|
64
|
+
strikethrough: false,
|
|
65
|
+
underline: false,
|
|
66
|
+
code: false,
|
|
67
|
+
color: "default",
|
|
68
|
+
},
|
|
69
|
+
plain_text: "foo",
|
|
70
|
+
href: `https://www.notion.so/${targetPageId}`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
color: "default",
|
|
74
|
+
},
|
|
75
|
+
}, targetPage);
|
|
76
|
+
expect(results.trim()).toBe(`[foo](/${targetPageId})`);
|
|
77
|
+
}));
|
|
41
78
|
test("link to an existing page on this site that has no slug", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
79
|
const targetPageId = "123";
|
|
43
80
|
const targetPage = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
@@ -517,7 +554,78 @@ test("internal link inside codeblock ignored", () => __awaiter(void 0, void 0, v
|
|
|
517
554
|
}, targetPage);
|
|
518
555
|
expect(results.trim()).toContain("this should not change [link](https://www.notion.so/native/metapages/mypage)");
|
|
519
556
|
}));
|
|
520
|
-
|
|
557
|
+
test("multiple internal links in a paragraph", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
558
|
+
const targetPageAId = "123";
|
|
559
|
+
const targetPageA = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
560
|
+
slug: undefined,
|
|
561
|
+
name: "Hello World A",
|
|
562
|
+
id: targetPageAId,
|
|
563
|
+
});
|
|
564
|
+
const targetPageBId = "456";
|
|
565
|
+
const targetPageB = (0, pluginTestRun_1.makeSamplePageObject)({
|
|
566
|
+
slug: undefined,
|
|
567
|
+
name: "Hello World B",
|
|
568
|
+
id: targetPageBId,
|
|
569
|
+
});
|
|
570
|
+
const results = yield getMarkdown({
|
|
571
|
+
type: "paragraph",
|
|
572
|
+
paragraph: {
|
|
573
|
+
rich_text: [
|
|
574
|
+
{
|
|
575
|
+
type: "text",
|
|
576
|
+
text: {
|
|
577
|
+
content: "A",
|
|
578
|
+
link: { url: `/${targetPageAId}` },
|
|
579
|
+
},
|
|
580
|
+
annotations: {
|
|
581
|
+
bold: false,
|
|
582
|
+
italic: false,
|
|
583
|
+
strikethrough: false,
|
|
584
|
+
underline: false,
|
|
585
|
+
code: false,
|
|
586
|
+
color: "default",
|
|
587
|
+
},
|
|
588
|
+
plain_text: "A",
|
|
589
|
+
href: `/${targetPageAId}`,
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
type: "text",
|
|
593
|
+
text: { content: " ", link: null },
|
|
594
|
+
annotations: {
|
|
595
|
+
bold: false,
|
|
596
|
+
italic: false,
|
|
597
|
+
strikethrough: false,
|
|
598
|
+
underline: false,
|
|
599
|
+
code: false,
|
|
600
|
+
color: "default",
|
|
601
|
+
},
|
|
602
|
+
plain_text: " ",
|
|
603
|
+
href: null,
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
type: "text",
|
|
607
|
+
text: {
|
|
608
|
+
content: "B",
|
|
609
|
+
link: { url: `/${targetPageBId}` },
|
|
610
|
+
},
|
|
611
|
+
annotations: {
|
|
612
|
+
bold: false,
|
|
613
|
+
italic: false,
|
|
614
|
+
strikethrough: false,
|
|
615
|
+
underline: false,
|
|
616
|
+
code: false,
|
|
617
|
+
color: "default",
|
|
618
|
+
},
|
|
619
|
+
plain_text: "B",
|
|
620
|
+
href: `/${targetPageBId}`,
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
color: "default",
|
|
624
|
+
},
|
|
625
|
+
}, targetPageA, targetPageB);
|
|
626
|
+
expect(results.trim()).toBe(`[A](/${targetPageAId}) [B](/${targetPageBId})`);
|
|
627
|
+
}));
|
|
628
|
+
function getMarkdown(block, targetPage, targetPage2) {
|
|
521
629
|
return __awaiter(this, void 0, void 0, function* () {
|
|
522
630
|
const config = {
|
|
523
631
|
plugins: [
|
|
@@ -526,6 +634,6 @@ function getMarkdown(block, targetPage) {
|
|
|
526
634
|
externalLinks_1.standardExternalLinkConversion,
|
|
527
635
|
],
|
|
528
636
|
};
|
|
529
|
-
return yield (0, pluginTestRun_1.oneBlockToMarkdown)(config, block, targetPage);
|
|
637
|
+
return yield (0, pluginTestRun_1.oneBlockToMarkdown)(config, block, targetPage, targetPage2);
|
|
530
638
|
});
|
|
531
639
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { NotionPage } from "../NotionPage";
|
|
2
2
|
import { IDocuNotionConfig } from "../config/configuration";
|
|
3
3
|
import { NotionBlock } from "../types";
|
|
4
|
+
export declare const kTemporaryTestDirectory = "tempTestFileDir";
|
|
4
5
|
export declare function blocksToMarkdown(config: IDocuNotionConfig, blocks: NotionBlock[], pages?: NotionPage[], children?: NotionBlock[], validApiKey?: string): Promise<string>;
|
|
5
6
|
export declare function makeSamplePageObject(options: {
|
|
6
7
|
slug?: string;
|
|
7
8
|
name?: string;
|
|
8
9
|
id?: string;
|
|
9
10
|
}): NotionPage;
|
|
10
|
-
export declare function oneBlockToMarkdown(config: IDocuNotionConfig, block: Record<string, unknown>, targetPage?: NotionPage): Promise<string>;
|
|
11
|
+
export declare function oneBlockToMarkdown(config: IDocuNotionConfig, block: Record<string, unknown>, targetPage?: NotionPage, targetPage2?: NotionPage): Promise<string>;
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.oneBlockToMarkdown = exports.makeSamplePageObject = exports.blocksToMarkdown = void 0;
|
|
12
|
+
exports.oneBlockToMarkdown = exports.makeSamplePageObject = exports.blocksToMarkdown = exports.kTemporaryTestDirectory = void 0;
|
|
13
13
|
const client_1 = require("@notionhq/client");
|
|
14
14
|
const notion_to_md_1 = require("notion-to-md");
|
|
15
15
|
const HierarchicalNamedLayoutStrategy_1 = require("../HierarchicalNamedLayoutStrategy");
|
|
@@ -17,6 +17,7 @@ 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
|
+
exports.kTemporaryTestDirectory = "tempTestFileDir";
|
|
20
21
|
function blocksToMarkdown(config, blocks, pages,
|
|
21
22
|
// Notes on children:
|
|
22
23
|
// - These children will apply to each block in blocks. (could enhance but not needed yet)
|
|
@@ -50,7 +51,7 @@ children, validApiKey) {
|
|
|
50
51
|
imports: [],
|
|
51
52
|
//TODO might be needed for some tests, e.g. the image transformer...
|
|
52
53
|
pageInfo: {
|
|
53
|
-
directoryContainingMarkdown:
|
|
54
|
+
directoryContainingMarkdown: exports.kTemporaryTestDirectory,
|
|
54
55
|
relativeFilePathToFolderContainingPage: "not yet",
|
|
55
56
|
slug: "not yet",
|
|
56
57
|
},
|
|
@@ -226,7 +227,7 @@ function makeSamplePageObject(options) {
|
|
|
226
227
|
return p;
|
|
227
228
|
}
|
|
228
229
|
exports.makeSamplePageObject = makeSamplePageObject;
|
|
229
|
-
function oneBlockToMarkdown(config, block, targetPage) {
|
|
230
|
+
function oneBlockToMarkdown(config, block, targetPage, targetPage2) {
|
|
230
231
|
return __awaiter(this, void 0, void 0, function* () {
|
|
231
232
|
// just in case someone expects these other properties that aren't normally relevant,
|
|
232
233
|
// we merge the given block properties into an actual, full block
|
|
@@ -258,7 +259,7 @@ function oneBlockToMarkdown(config, block, targetPage) {
|
|
|
258
259
|
slug: "dummy2",
|
|
259
260
|
name: "Dummy2",
|
|
260
261
|
});
|
|
261
|
-
return yield blocksToMarkdown(config, [fullBlock], targetPage ? [dummyPage1, targetPage, dummyPage2] : undefined);
|
|
262
|
+
return yield blocksToMarkdown(config, [fullBlock], targetPage ? [dummyPage1, targetPage, targetPage2 !== null && targetPage2 !== void 0 ? targetPage2 : dummyPage2] : undefined);
|
|
262
263
|
});
|
|
263
264
|
}
|
|
264
265
|
exports.oneBlockToMarkdown = oneBlockToMarkdown;
|
package/dist/pull.js
CHANGED
|
@@ -163,7 +163,7 @@ function getPagesRecursively(options, incomingContext, pageIdOfThisParent, order
|
|
|
163
163
|
if (!rootLevel &&
|
|
164
164
|
pageInfo.hasParagraphs &&
|
|
165
165
|
pageInfo.childPageIdsAndOrder.length) {
|
|
166
|
-
(0, log_1.error)(`Skipping "${pageInTheOutline.nameOrTitle}" and its children. docu-notion does not support pages that are both levels and have content at the same time.`);
|
|
166
|
+
(0, log_1.error)(`Skipping "${pageInTheOutline.nameOrTitle}" and its children. docu-notion does not support pages that are both levels and have text content (paragraphs) at the same time. Normally outline pages should just be composed of 1) links to other pages and 2) child pages (other levels of the outline). Note that @-mention style links appear as text paragraphs to docu-notion so must not be used to form the outline.`);
|
|
167
167
|
++counts.skipped_because_level_cannot_have_content;
|
|
168
168
|
return;
|
|
169
169
|
}
|
package/dist/transform.js
CHANGED
|
@@ -149,7 +149,7 @@ function doNotionToMarkdown(docunotionContext, blocks) {
|
|
|
149
149
|
// Raw links come in without a leading slash, e.g. [link_to_page](4a6de8c0-b90b-444b-8a7b-d534d6ec71a4)
|
|
150
150
|
// Inline links come in with a leading slash, e.g. [pointer to the introduction](/4a6de8c0b90b444b8a7bd534d6ec71a4)
|
|
151
151
|
function doLinkFixes(context, markdown, config) {
|
|
152
|
-
const linkRegExp = /\[
|
|
152
|
+
const linkRegExp = /\[.*?\]\([^\)]*?\)/g;
|
|
153
153
|
(0, log_1.logDebug)("markdown before link fixes", markdown);
|
|
154
154
|
let match;
|
|
155
155
|
// since we're going to make changes to the markdown,
|
package/package.json
CHANGED