@sillsdev/docu-notion 1.0.0-alpha.2 → 1.0.0-alpha.5
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/pull.js +20 -17
- package/dist/transform.js +18 -8
- package/dist/transform.spec.d.ts +1 -0
- package/dist/transform.spec.js +63 -0
- package/package.json +10 -2
package/dist/pull.js
CHANGED
|
@@ -66,16 +66,19 @@ function getOptionsForLogging(options) {
|
|
|
66
66
|
const kNotionApiVersion = "2026-03-11";
|
|
67
67
|
let layoutStrategy;
|
|
68
68
|
let notionToMarkdown;
|
|
69
|
-
const pages = new Array();
|
|
70
|
-
const counts = {
|
|
71
|
-
output_normally: 0,
|
|
72
|
-
skipped_because_empty: 0,
|
|
73
|
-
skipped_because_status: 0,
|
|
74
|
-
skipped_because_level_cannot_have_content: 0,
|
|
75
|
-
error_because_no_slug: 0,
|
|
76
|
-
};
|
|
77
69
|
function notionPull(options) {
|
|
78
70
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
// These are local to each pull so that repeated calls (e.g. in tests or
|
|
72
|
+
// programmatic multi-run scenarios) don't accumulate pages or counts from
|
|
73
|
+
// previous runs.
|
|
74
|
+
const pages = new Array();
|
|
75
|
+
const counts = {
|
|
76
|
+
output_normally: 0,
|
|
77
|
+
skipped_because_empty: 0,
|
|
78
|
+
skipped_because_status: 0,
|
|
79
|
+
skipped_because_level_cannot_have_content: 0,
|
|
80
|
+
error_because_no_slug: 0,
|
|
81
|
+
};
|
|
79
82
|
// It's helpful when troubleshooting CI secrets and environment variables to see what options actually made it to docu-notion.
|
|
80
83
|
const optionsForLogging = getOptionsForLogging(options);
|
|
81
84
|
const config = yield (0, configuration_1.loadConfigAsync)();
|
|
@@ -97,12 +100,12 @@ function notionPull(options) {
|
|
|
97
100
|
(0, process_1.exit)(1);
|
|
98
101
|
}
|
|
99
102
|
(0, log_1.group)("Stage 1: walk children of the page named 'Outline', looking for pages...");
|
|
100
|
-
yield getPagesRecursively(options, "", options.rootPage, 0, true);
|
|
103
|
+
yield getPagesRecursively(options, "", options.rootPage, 0, true, pages, counts);
|
|
101
104
|
(0, log_1.logDebug)("getPagesRecursively", JSON.stringify(pages, null, 2));
|
|
102
105
|
(0, log_1.info)(`Found ${pages.length} pages`);
|
|
103
106
|
(0, log_1.endGroup)();
|
|
104
107
|
(0, log_1.group)(`Stage 2: convert ${pages.length} Notion pages to markdown and save locally...`);
|
|
105
|
-
yield outputPages(options, config, pages);
|
|
108
|
+
yield outputPages(options, config, pages, counts);
|
|
106
109
|
(0, log_1.endGroup)();
|
|
107
110
|
(0, log_1.group)("Stage 3: clean up old files & images...");
|
|
108
111
|
yield layoutStrategy.cleanupOldFiles();
|
|
@@ -110,7 +113,7 @@ function notionPull(options) {
|
|
|
110
113
|
(0, log_1.endGroup)();
|
|
111
114
|
});
|
|
112
115
|
}
|
|
113
|
-
function outputPages(options, config, pages) {
|
|
116
|
+
function outputPages(options, config, pages, counts) {
|
|
114
117
|
return __awaiter(this, void 0, void 0, function* () {
|
|
115
118
|
const context = {
|
|
116
119
|
getBlockChildren: getBlockChildren,
|
|
@@ -124,7 +127,7 @@ function outputPages(options, config, pages) {
|
|
|
124
127
|
notionToMarkdown: notionToMarkdown,
|
|
125
128
|
options: options,
|
|
126
129
|
pages: pages,
|
|
127
|
-
counts: counts,
|
|
130
|
+
counts: counts,
|
|
128
131
|
imports: [],
|
|
129
132
|
convertNotionLinkToLocalDocusaurusLink: (url) => (0, internalLinks_1.convertInternalUrl)(context, url),
|
|
130
133
|
};
|
|
@@ -141,7 +144,7 @@ function outputPages(options, config, pages) {
|
|
|
141
144
|
context.options.statusTag != "*" &&
|
|
142
145
|
page.status !== context.options.statusTag) {
|
|
143
146
|
(0, log_1.verbose)(`Skipping page because status is not '${context.options.statusTag}': ${page.nameOrTitle}`);
|
|
144
|
-
++
|
|
147
|
+
++counts.skipped_because_status;
|
|
145
148
|
}
|
|
146
149
|
else {
|
|
147
150
|
if (options.requireSlugs && !page.hasExplicitSlug) {
|
|
@@ -149,7 +152,7 @@ function outputPages(options, config, pages) {
|
|
|
149
152
|
++counts.error_because_no_slug;
|
|
150
153
|
}
|
|
151
154
|
const markdown = yield (0, transform_1.getMarkdownForPage)(config, context, page);
|
|
152
|
-
writePage(page, markdown);
|
|
155
|
+
writePage(page, markdown, counts);
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
if (counts.error_because_no_slug > 0)
|
|
@@ -164,7 +167,7 @@ function outputPages(options, config, pages) {
|
|
|
164
167
|
// outline that contain content (which we call "Simple" pages). Later, we can
|
|
165
168
|
// then step through this list creating the files we need, and, crucially, be
|
|
166
169
|
// able to figure out what the url will be for any links between content pages.
|
|
167
|
-
function getPagesRecursively(options, incomingContext, pageIdOfThisParent, orderOfThisParent, rootLevel) {
|
|
170
|
+
function getPagesRecursively(options, incomingContext, pageIdOfThisParent, orderOfThisParent, rootLevel, pages, counts) {
|
|
168
171
|
return __awaiter(this, void 0, void 0, function* () {
|
|
169
172
|
var _a;
|
|
170
173
|
const pageInTheOutline = yield fromPageId(incomingContext, pageIdOfThisParent, orderOfThisParent, true);
|
|
@@ -197,7 +200,7 @@ function getPagesRecursively(options, incomingContext, pageIdOfThisParent, order
|
|
|
197
200
|
layoutContext = layoutStrategy.newLevel(options.markdownOutputPath, pageInTheOutline.order, incomingContext, pageInTheOutline.nameOrTitle);
|
|
198
201
|
}
|
|
199
202
|
for (const childPageInfo of pageInfo.childPageIdsAndOrder) {
|
|
200
|
-
yield getPagesRecursively(options, layoutContext, childPageInfo.id, childPageInfo.order, false);
|
|
203
|
+
yield getPagesRecursively(options, layoutContext, childPageInfo.id, childPageInfo.order, false, pages, counts);
|
|
201
204
|
}
|
|
202
205
|
for (const linkPageInfo of pageInfo.linksPageIdsAndOrder) {
|
|
203
206
|
pages.push(yield fromPageId(layoutContext, linkPageInfo.id, linkPageInfo.order, false));
|
|
@@ -209,7 +212,7 @@ function getPagesRecursively(options, incomingContext, pageIdOfThisParent, order
|
|
|
209
212
|
}
|
|
210
213
|
});
|
|
211
214
|
}
|
|
212
|
-
function writePage(page, finalMarkdown) {
|
|
215
|
+
function writePage(page, finalMarkdown, counts) {
|
|
213
216
|
const mdPath = layoutStrategy.getPathForPage(page, ".md");
|
|
214
217
|
(0, log_1.verbose)(`writing ${mdPath}`);
|
|
215
218
|
fs.writeFileSync(mdPath, finalMarkdown, {});
|
package/dist/transform.js
CHANGED
|
@@ -113,13 +113,19 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
113
113
|
let body = input;
|
|
114
114
|
//console.log("body before regex: " + body);
|
|
115
115
|
let match;
|
|
116
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
117
116
|
for (const mod of regexMods) {
|
|
118
|
-
let replacement = undefined;
|
|
119
117
|
// regex.exec is stateful, so we don't want to mess up the plugin's use of its own regex, so we clone it.
|
|
120
118
|
// we also add the "g" flag to make sure we get all matches
|
|
121
119
|
const regex = new RegExp(`${codeBlocks.source}|(${mod.regex.source})`, "g");
|
|
122
|
-
|
|
120
|
+
// We match against `source` (a snapshot of the current body) and build up a
|
|
121
|
+
// fresh `result`. Mutating the string we're iterating over would invalidate
|
|
122
|
+
// `match.index` for every subsequent match once a replacement changes the
|
|
123
|
+
// length, so instead we copy the spans between matches using indices that
|
|
124
|
+
// always refer to the unchanged `source`.
|
|
125
|
+
const source = body;
|
|
126
|
+
let result = "";
|
|
127
|
+
let lastIndex = 0;
|
|
128
|
+
while ((match = regex.exec(source)) !== null) {
|
|
123
129
|
if (match[0]) {
|
|
124
130
|
const original = match[0];
|
|
125
131
|
if (original.startsWith("```") &&
|
|
@@ -127,6 +133,7 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
127
133
|
!mod.includeCodeBlocks) {
|
|
128
134
|
continue; // code block, and they didn't say to include them
|
|
129
135
|
}
|
|
136
|
+
let replacement = undefined;
|
|
130
137
|
if (mod.getReplacement) {
|
|
131
138
|
// our match here has an extra group, which is an implementation detail
|
|
132
139
|
// that shouldn't be made visible to the plugin
|
|
@@ -138,11 +145,11 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
138
145
|
}
|
|
139
146
|
if (replacement !== undefined) {
|
|
140
147
|
(0, log_1.verbose)(`[${mod.name}] ${original} --> ${replacement}`);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
// copy everything since the last match, then the replacement (the
|
|
149
|
+
// replacement is appended literally, so `$` sequences in it are not
|
|
150
|
+
// given any special meaning)
|
|
151
|
+
result += source.substring(lastIndex, match.index) + replacement;
|
|
152
|
+
lastIndex = match.index + original.length;
|
|
146
153
|
// add any library imports
|
|
147
154
|
if (!context.imports)
|
|
148
155
|
context.imports = [];
|
|
@@ -150,6 +157,9 @@ function doTransformsOnMarkdown(context, config, input) {
|
|
|
150
157
|
}
|
|
151
158
|
}
|
|
152
159
|
}
|
|
160
|
+
// copy whatever follows the last replacement (including any skipped code blocks)
|
|
161
|
+
result += source.substring(lastIndex);
|
|
162
|
+
body = result;
|
|
153
163
|
}
|
|
154
164
|
(0, log_1.logDebug)("doTransformsOnMarkdown", "body after regex: " + body);
|
|
155
165
|
return body;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const log_1 = require("./log");
|
|
13
|
+
const pluginTestRun_1 = require("./plugins/pluginTestRun");
|
|
14
|
+
function paragraph(text) {
|
|
15
|
+
return {
|
|
16
|
+
type: "paragraph",
|
|
17
|
+
paragraph: {
|
|
18
|
+
rich_text: [
|
|
19
|
+
{
|
|
20
|
+
type: "text",
|
|
21
|
+
text: { content: text, link: null },
|
|
22
|
+
annotations: {
|
|
23
|
+
bold: false,
|
|
24
|
+
italic: false,
|
|
25
|
+
strikethrough: false,
|
|
26
|
+
underline: false,
|
|
27
|
+
code: false,
|
|
28
|
+
color: "default",
|
|
29
|
+
},
|
|
30
|
+
plain_text: text,
|
|
31
|
+
href: null,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Regression test for the bug where doTransformsOnMarkdown matched against the
|
|
38
|
+
// original `input` but sliced/replaced the mutating `body`. Once the first
|
|
39
|
+
// replacement changes the string length, every subsequent match.index is wrong.
|
|
40
|
+
// We make the first replacement SHRINK the text so the second match.index lands
|
|
41
|
+
// past the real match, and the inner .replace silently drops the transformation.
|
|
42
|
+
test("applies every match even after a length-changing replacement", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
(0, log_1.setLogLevel)("verbose");
|
|
44
|
+
const p = {
|
|
45
|
+
name: "test",
|
|
46
|
+
regexMarkdownModifications: [
|
|
47
|
+
{
|
|
48
|
+
regex: /REMOVE/,
|
|
49
|
+
replacementPattern: `X`, // shorter than the match, so the body shrinks
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
const config = { plugins: [p] };
|
|
54
|
+
// Produces markdown: "REMOVE\n\nREMOVE keep"
|
|
55
|
+
const result = yield (0, pluginTestRun_1.blocksToMarkdown)(config, [
|
|
56
|
+
paragraph("REMOVE"),
|
|
57
|
+
paragraph("REMOVE keep"),
|
|
58
|
+
]);
|
|
59
|
+
// Both occurrences must be transformed; none should survive.
|
|
60
|
+
expect(result).not.toContain("REMOVE");
|
|
61
|
+
expect((result.match(/X/g) || []).length).toBe(2);
|
|
62
|
+
expect(result).toContain("keep");
|
|
63
|
+
}));
|
package/package.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"lint-staged": "^10.5.4",
|
|
55
55
|
"prettier": "^2.2.1",
|
|
56
56
|
"rimraf": "^4.1.2",
|
|
57
|
+
"semantic-release": "25.0.3",
|
|
57
58
|
"typescript": "^5.9.3",
|
|
58
59
|
"vite": "^4.2.1",
|
|
59
60
|
"vitest": "^0.30.1"
|
|
@@ -87,8 +88,15 @@
|
|
|
87
88
|
"publishConfig": {
|
|
88
89
|
"access": "public"
|
|
89
90
|
},
|
|
91
|
+
"packageManager": "npm@11.16.0",
|
|
90
92
|
"volta": {
|
|
91
|
-
"node": "
|
|
93
|
+
"node": "24.16.0",
|
|
94
|
+
"npm": "11.16.0"
|
|
92
95
|
},
|
|
93
|
-
"
|
|
96
|
+
"allowScripts": {
|
|
97
|
+
"core-js@2.6.12": true,
|
|
98
|
+
"esbuild@0.17.17": true,
|
|
99
|
+
"fsevents": false
|
|
100
|
+
},
|
|
101
|
+
"version": "1.0.0-alpha.5"
|
|
94
102
|
}
|