confluence-cli 2.1.10 → 2.1.12
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/macro-converter.js +5 -1
- package/lib/storage-walker.js +57 -8
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/lib/macro-converter.js
CHANGED
|
@@ -135,7 +135,11 @@ class MacroConverter {
|
|
|
135
135
|
buildUrl: this.buildUrl,
|
|
136
136
|
webUrlPrefix: this.webUrlPrefix,
|
|
137
137
|
});
|
|
138
|
-
|
|
138
|
+
const result = walker.walk(storage);
|
|
139
|
+
if (typeof options.onWarnings === 'function' && walker.warnings.length > 0) {
|
|
140
|
+
options.onWarnings(walker.warnings);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
139
143
|
}
|
|
140
144
|
}
|
|
141
145
|
|
package/lib/storage-walker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { Parser, DomHandler } = require('htmlparser2');
|
|
2
2
|
const { decodeHTML } = require('entities');
|
|
3
3
|
const { fenceLength, cleanupWithFences } = require('./markdown-cleanup');
|
|
4
4
|
|
|
@@ -65,12 +65,57 @@ class StorageWalker {
|
|
|
65
65
|
|
|
66
66
|
walk(storage) {
|
|
67
67
|
this._depth = 0;
|
|
68
|
-
|
|
68
|
+
this.warnings = [];
|
|
69
|
+
|
|
70
|
+
// htmlparser2 in xmlMode is lenient: malformed input (unclosed tags,
|
|
71
|
+
// crossed nesting) is auto-closed without raising. We need to surface
|
|
72
|
+
// those events so callers can flag pages that were silently repaired.
|
|
73
|
+
//
|
|
74
|
+
// The handler emits onclosetag(name, isImplied) for *every* tag that
|
|
75
|
+
// wasn't matched by an explicit </tag> — including legitimate XML
|
|
76
|
+
// self-closing tags like `<br/>` and `<ri:attachment/>`. We
|
|
77
|
+
// distinguish the two by tracking each open tag's index range: a
|
|
78
|
+
// self-closing tag's open and close events share the same
|
|
79
|
+
// (startIndex, endIndex), while a genuinely auto-closed tag's close
|
|
80
|
+
// event lands at a later position in the source.
|
|
81
|
+
const handler = new DomHandler(null, { xmlMode: true });
|
|
82
|
+
const openStack = [];
|
|
83
|
+
const origOnOpenTag = handler.onopentag.bind(handler);
|
|
84
|
+
const origOnCloseTag = handler.onclosetag.bind(handler);
|
|
85
|
+
handler.onopentag = (...args) => {
|
|
86
|
+
openStack.push({ sIdx: parser.startIndex, eIdx: parser.endIndex });
|
|
87
|
+
origOnOpenTag(...args);
|
|
88
|
+
};
|
|
89
|
+
handler.onclosetag = (...args) => {
|
|
90
|
+
const [name, isImplied] = args;
|
|
91
|
+
const opened = openStack.pop();
|
|
92
|
+
if (isImplied) {
|
|
93
|
+
const selfClosing =
|
|
94
|
+
opened
|
|
95
|
+
&& opened.sIdx === parser.startIndex
|
|
96
|
+
&& opened.eIdx === parser.endIndex;
|
|
97
|
+
if (!selfClosing) {
|
|
98
|
+
const offset = parser.endIndex;
|
|
99
|
+
this.warnings.push({ type: 'implicit-close', tag: name, offset });
|
|
100
|
+
if (process.env.CONFLUENCE_CLI_VERBOSE) {
|
|
101
|
+
process.stderr.write(
|
|
102
|
+
`StorageWalker: auto-closed <${name}> at offset ${offset}\n`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
origOnCloseTag(...args);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const parser = new Parser(handler, {
|
|
69
111
|
xmlMode: true,
|
|
70
112
|
recognizeSelfClosing: true,
|
|
71
113
|
decodeEntities: true,
|
|
72
114
|
});
|
|
73
|
-
|
|
115
|
+
parser.write(storage);
|
|
116
|
+
parser.end();
|
|
117
|
+
|
|
118
|
+
return this.cleanup(this.walkNodes(handler.dom));
|
|
74
119
|
}
|
|
75
120
|
|
|
76
121
|
walkNodes(nodes) {
|
|
@@ -250,9 +295,9 @@ class StorageWalker {
|
|
|
250
295
|
|
|
251
296
|
handleExpand(node) {
|
|
252
297
|
const titleParam = this.findParamByName(node, 'title');
|
|
298
|
+
const title = (titleParam ? this.getTextContent(titleParam) : '').trim();
|
|
253
299
|
const body = this.getMacroBody(node);
|
|
254
|
-
if (
|
|
255
|
-
const title = this.getTextContent(titleParam);
|
|
300
|
+
if (title) {
|
|
256
301
|
return `\n**EXPAND: ${title}**\n\n${this.walkNodes(body).trim()}\n\n**EXPAND_END**\n`;
|
|
257
302
|
}
|
|
258
303
|
return `\n<details>\n<summary>${this.labels.expandDetails || 'Expand Details'}</summary>\n\n${this.walkNodes(body).trim()}\n\n</details>\n`;
|
|
@@ -328,7 +373,7 @@ class StorageWalker {
|
|
|
328
373
|
|
|
329
374
|
handleSharedBlock(node, type) {
|
|
330
375
|
const blockKeyParam = this.findParamByName(node, 'shared-block-key');
|
|
331
|
-
const blockKey = blockKeyParam ? this.getTextContent(blockKeyParam) : '';
|
|
376
|
+
const blockKey = (blockKeyParam ? this.getTextContent(blockKeyParam) : '').trim();
|
|
332
377
|
const pageParam = this.findParamByName(node, 'page');
|
|
333
378
|
if (pageParam && type === 'include-shared-block') {
|
|
334
379
|
const acLink = this.findChildByName(pageParam, 'ac:link');
|
|
@@ -338,7 +383,8 @@ class StorageWalker {
|
|
|
338
383
|
const pageTitle = this.escapeMarkdownText(decodeEntities(riPage.attribs['ri:content-title'] || ''));
|
|
339
384
|
const includeLabel = this.labels.includeSharedBlock || 'Include Shared Block';
|
|
340
385
|
const fromPageLabel = this.labels.fromPage || 'from page';
|
|
341
|
-
|
|
386
|
+
const keyPart = blockKey ? `: ${blockKey} ` : ' ';
|
|
387
|
+
return `\n> 📄 **${includeLabel}**${keyPart}(${fromPageLabel}: ${pageTitle} [link needs manual correction])\n`;
|
|
342
388
|
}
|
|
343
389
|
}
|
|
344
390
|
}
|
|
@@ -346,8 +392,11 @@ class StorageWalker {
|
|
|
346
392
|
// See handlePanel — trim to avoid bracketing `>` blank lines.
|
|
347
393
|
const cleanContent = this.walkNodes(body).trim();
|
|
348
394
|
const sharedLabel = this.labels.sharedBlock || 'Shared Block';
|
|
395
|
+
if (!blockKey && !cleanContent) return '';
|
|
396
|
+
const header = blockKey ? `**${sharedLabel}: ${blockKey}**` : `**${sharedLabel}**`;
|
|
397
|
+
if (!cleanContent) return `\n> ${header}\n`;
|
|
349
398
|
const quoted = cleanContent.split('\n').map((line) => (line ? `> ${line}` : '>')).join('\n');
|
|
350
|
-
return `\n>
|
|
399
|
+
return `\n> ${header}\n>\n${quoted}\n`;
|
|
351
400
|
}
|
|
352
401
|
|
|
353
402
|
handleViewFile(node) {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "confluence-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.12",
|
|
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.12",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"axios": "^1.15.0",
|