confluence-cli 2.1.6 → 2.1.7
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/lib/html-to-markdown.js +3 -52
- package/lib/markdown-cleanup.js +80 -0
- package/lib/storage-walker.js +17 -23
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/lib/html-to-markdown.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { fenceLength, cleanupWithFences } = require('./markdown-cleanup');
|
|
2
|
+
|
|
1
3
|
const NAMED_ENTITIES = {
|
|
2
4
|
aring: 'å', auml: 'ä', ouml: 'ö',
|
|
3
5
|
eacute: 'é', egrave: 'è', ecirc: 'ê', euml: 'ë',
|
|
@@ -165,58 +167,7 @@ function htmlToMarkdown(html) {
|
|
|
165
167
|
|
|
166
168
|
markdown = markdown.replace(/&([a-zA-Z]+);/g, (match, name) => NAMED_ENTITIES[name] || match);
|
|
167
169
|
|
|
168
|
-
|
|
169
|
-
// multi-space collapsing) don't mangle indentation-sensitive code.
|
|
170
|
-
// Backreference matches dynamically-sized fences emitted above when the
|
|
171
|
-
// body itself contains backticks.
|
|
172
|
-
const segments = splitOnFences(markdown);
|
|
173
|
-
markdown = segments
|
|
174
|
-
.map((seg, i) => (i % 2 === 1 ? seg : cleanupOutsideFence(seg)))
|
|
175
|
-
.join('');
|
|
176
|
-
markdown = markdown.trim();
|
|
177
|
-
|
|
178
|
-
return markdown;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// CommonMark allows fenced code with N≥3 backticks where the body contains
|
|
182
|
-
// no run of N+ backticks. Pick the smallest N satisfying both so a code
|
|
183
|
-
// block whose payload itself contains ``` does not close its own fence.
|
|
184
|
-
function fenceLength(body) {
|
|
185
|
-
let max = 0;
|
|
186
|
-
const runs = body.match(/`+/g);
|
|
187
|
-
if (runs) {
|
|
188
|
-
for (const r of runs) if (r.length > max) max = r.length;
|
|
189
|
-
}
|
|
190
|
-
return Math.max(3, max + 1);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function splitOnFences(text) {
|
|
194
|
-
// CommonMark: a fence opens on a line that starts with up to 3 spaces
|
|
195
|
-
// followed by 3+ backticks, and closes on a line of equal-length
|
|
196
|
-
// backticks followed only by whitespace. Anchoring to line boundaries
|
|
197
|
-
// (^ / $ with m flag) prevents prose backticks (e.g. <p>literal ``` x</p>)
|
|
198
|
-
// from being mis-paired with real fence boundaries.
|
|
199
|
-
const result = [];
|
|
200
|
-
const re = /^ {0,3}(`{3,})[^\n]*\n[\s\S]*?\n {0,3}\1[\t ]*$/gm;
|
|
201
|
-
let lastIdx = 0;
|
|
202
|
-
let m;
|
|
203
|
-
while ((m = re.exec(text)) !== null) {
|
|
204
|
-
result.push(text.slice(lastIdx, m.index));
|
|
205
|
-
result.push(m[0]);
|
|
206
|
-
lastIdx = m.index + m[0].length;
|
|
207
|
-
}
|
|
208
|
-
result.push(text.slice(lastIdx));
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function cleanupOutsideFence(text) {
|
|
213
|
-
let out = text;
|
|
214
|
-
out = out.replace(/[ \t]+$/gm, '');
|
|
215
|
-
out = out.replace(/^[ \t]+(?!([`>]|[*+-] |\d+[.)] ))/gm, '');
|
|
216
|
-
out = out.replace(/^(#{1,6}[^\n]+)\n(?!\n)/gm, '$1\n\n');
|
|
217
|
-
out = out.replace(/\n\s*\n\s*\n+/g, '\n\n');
|
|
218
|
-
out = out.replace(/[ \t]+/g, ' ');
|
|
219
|
-
return out;
|
|
170
|
+
return cleanupWithFences(markdown);
|
|
220
171
|
}
|
|
221
172
|
|
|
222
173
|
module.exports = {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Shared fence-aware markdown cleanup for storage-walker and html-to-markdown.
|
|
2
|
+
// Both converters need the same set of operations:
|
|
3
|
+
// 1. Size opening/closing fences against the entity-decoded code body so a
|
|
4
|
+
// payload containing literal backticks (or numeric entities that decode
|
|
5
|
+
// to backticks) does not close its own fence.
|
|
6
|
+
// 2. Split the post-conversion text on fenced code boundaries using a
|
|
7
|
+
// CommonMark line-anchored matcher so prose `\`\`\`` cannot be mis-paired
|
|
8
|
+
// with a real fence opening.
|
|
9
|
+
// 3. Apply a 5-step whitespace cleanup chain only outside fenced code.
|
|
10
|
+
//
|
|
11
|
+
// Keeping these helpers in one place prevents the converters from drifting
|
|
12
|
+
// apart again — see issue #149 for the history.
|
|
13
|
+
|
|
14
|
+
// CommonMark allows fenced code with N≥3 backticks where the body contains
|
|
15
|
+
// no run of N+ backticks. Pick the smallest N satisfying both. Caller must
|
|
16
|
+
// pass an entity-decoded body — numeric entity refs like ``` are
|
|
17
|
+
// backticks once decoded, so sizing before decode would leave the fence
|
|
18
|
+
// breakable when the entities resolve.
|
|
19
|
+
function fenceLength(decodedBody) {
|
|
20
|
+
let max = 0;
|
|
21
|
+
const runs = decodedBody.match(/`+/g);
|
|
22
|
+
if (runs) {
|
|
23
|
+
for (const r of runs) if (r.length > max) max = r.length;
|
|
24
|
+
}
|
|
25
|
+
return Math.max(3, max + 1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Split text on fenced code boundaries. Returns an alternating sequence of
|
|
29
|
+
// segments where even indices are outside-fence text and odd indices are
|
|
30
|
+
// full fenced blocks (delimiters included).
|
|
31
|
+
//
|
|
32
|
+
// CommonMark: a fence opens on a line of up to 3 spaces + 3+ backticks and
|
|
33
|
+
// closes on a line of equal-length backticks followed only by whitespace.
|
|
34
|
+
// Anchoring to line boundaries (^ / $ with the m flag) prevents prose
|
|
35
|
+
// backticks (e.g. a paragraph documenting markdown syntax) from being
|
|
36
|
+
// mis-paired with a real fence opening.
|
|
37
|
+
function splitOnFences(text) {
|
|
38
|
+
const result = [];
|
|
39
|
+
const re = /^ {0,3}(`{3,})[^\n]*\n[\s\S]*?\n {0,3}\1[\t ]*$/gm;
|
|
40
|
+
let lastIdx = 0;
|
|
41
|
+
let m;
|
|
42
|
+
while ((m = re.exec(text)) !== null) {
|
|
43
|
+
result.push(text.slice(lastIdx, m.index));
|
|
44
|
+
result.push(m[0]);
|
|
45
|
+
lastIdx = m.index + m[0].length;
|
|
46
|
+
}
|
|
47
|
+
result.push(text.slice(lastIdx));
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Whitespace cleanup safe to apply to text that sits outside fenced code.
|
|
52
|
+
// Strips trailing whitespace, strips leading whitespace except where it
|
|
53
|
+
// signals a list/blockquote/inline-code marker, ensures a blank line after
|
|
54
|
+
// headers, collapses 3+ blank lines, and squashes runs of inline whitespace.
|
|
55
|
+
function cleanupOutsideFence(text) {
|
|
56
|
+
let out = text;
|
|
57
|
+
out = out.replace(/[ \t]+$/gm, '');
|
|
58
|
+
out = out.replace(/^[ \t]+(?!([`>]|[*+-] |\d+[.)] ))/gm, '');
|
|
59
|
+
out = out.replace(/^(#{1,6}[^\n]+)\n(?!\n)/gm, '$1\n\n');
|
|
60
|
+
out = out.replace(/\n\s*\n\s*\n+/g, '\n\n');
|
|
61
|
+
out = out.replace(/[ \t]+/g, ' ');
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Apply outside-fence cleanup while leaving fenced code untouched, then
|
|
66
|
+
// trim leading and trailing whitespace from the joined result.
|
|
67
|
+
function cleanupWithFences(text) {
|
|
68
|
+
const segments = splitOnFences(text);
|
|
69
|
+
return segments
|
|
70
|
+
.map((seg, i) => (i % 2 === 1 ? seg : cleanupOutsideFence(seg)))
|
|
71
|
+
.join('')
|
|
72
|
+
.trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
fenceLength,
|
|
77
|
+
splitOnFences,
|
|
78
|
+
cleanupOutsideFence,
|
|
79
|
+
cleanupWithFences,
|
|
80
|
+
};
|
package/lib/storage-walker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { parseDocument } = require('htmlparser2');
|
|
2
2
|
const { decodeHTML } = require('entities');
|
|
3
|
+
const { fenceLength, cleanupWithFences } = require('./markdown-cleanup');
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MAX_DEPTH = 256;
|
|
5
6
|
|
|
@@ -260,7 +261,8 @@ class StorageWalker {
|
|
|
260
261
|
const lang = langParam ? this.getTextContent(langParam) : '';
|
|
261
262
|
const plainBody = this.findChildByName(node, 'ac:plain-text-body');
|
|
262
263
|
const code = plainBody ? this.getRawText(plainBody) : '';
|
|
263
|
-
|
|
264
|
+
const fence = '`'.repeat(fenceLength(code));
|
|
265
|
+
return `\n${fence}${lang}\n${code}\n${fence}\n`;
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
handleCallout(node, marker) {
|
|
@@ -296,7 +298,8 @@ class StorageWalker {
|
|
|
296
298
|
handleMermaid(node) {
|
|
297
299
|
const plainBody = this.findChildByName(node, 'ac:plain-text-body');
|
|
298
300
|
const code = plainBody ? this.getRawText(plainBody).trim() : '';
|
|
299
|
-
|
|
301
|
+
const fence = '`'.repeat(fenceLength(code));
|
|
302
|
+
return `\n${fence}mermaid\n${code}\n${fence}\n`;
|
|
300
303
|
}
|
|
301
304
|
|
|
302
305
|
handleInclude(node) {
|
|
@@ -351,9 +354,17 @@ class StorageWalker {
|
|
|
351
354
|
|
|
352
355
|
handleImage(node) {
|
|
353
356
|
const riAttachment = this.findChildByName(node, 'ri:attachment');
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
|
|
357
|
+
if (riAttachment) {
|
|
358
|
+
const filename = decodeEntities(riAttachment.attribs['ri:filename'] || '');
|
|
359
|
+
return ``;
|
|
360
|
+
}
|
|
361
|
+
const riUrl = this.findChildByName(node, 'ri:url');
|
|
362
|
+
if (riUrl) {
|
|
363
|
+
const url = decodeEntities(riUrl.attribs['ri:value'] || '');
|
|
364
|
+
if (!url) return '';
|
|
365
|
+
return ``;
|
|
366
|
+
}
|
|
367
|
+
return '';
|
|
357
368
|
}
|
|
358
369
|
|
|
359
370
|
handleAcLink(node) {
|
|
@@ -465,24 +476,7 @@ class StorageWalker {
|
|
|
465
476
|
}
|
|
466
477
|
|
|
467
478
|
cleanup(text) {
|
|
468
|
-
|
|
469
|
-
// multi-space collapsing) don't mangle indentation-sensitive code.
|
|
470
|
-
// Walker only emits triple-backtick fences (see code/mermaid macros).
|
|
471
|
-
const segments = text.split(/(```[\s\S]*?```)/g);
|
|
472
|
-
const cleaned = segments
|
|
473
|
-
.map((seg, i) => (i % 2 === 1 ? seg : this._cleanupOutsideFence(seg)))
|
|
474
|
-
.join('');
|
|
475
|
-
return cleaned.trim();
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
_cleanupOutsideFence(text) {
|
|
479
|
-
let out = text;
|
|
480
|
-
out = out.replace(/[ \t]+$/gm, '');
|
|
481
|
-
out = out.replace(/^[ \t]+(?!([`>]|[*+-] |\d+[.)] ))/gm, '');
|
|
482
|
-
out = out.replace(/^(#{1,6}[^\n]+)\n(?!\n)/gm, '$1\n\n');
|
|
483
|
-
out = out.replace(/\n\s*\n\s*\n+/g, '\n\n');
|
|
484
|
-
out = out.replace(/[ \t]+/g, ' ');
|
|
485
|
-
return out;
|
|
479
|
+
return cleanupWithFences(text);
|
|
486
480
|
}
|
|
487
481
|
}
|
|
488
482
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "confluence-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "confluence-cli",
|
|
9
|
-
"version": "2.1.
|
|
9
|
+
"version": "2.1.7",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"axios": "^1.15.0",
|