confluence-cli 2.1.11 → 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.
@@ -135,7 +135,11 @@ class MacroConverter {
135
135
  buildUrl: this.buildUrl,
136
136
  webUrlPrefix: this.webUrlPrefix,
137
137
  });
138
- return walker.walk(storage);
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
 
@@ -1,4 +1,4 @@
1
- const { parseDocument } = require('htmlparser2');
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
- const dom = parseDocument(storage, {
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
- return this.cleanup(this.walkNodes(dom.children));
115
+ parser.write(storage);
116
+ parser.end();
117
+
118
+ return this.cleanup(this.walkNodes(handler.dom));
74
119
  }
75
120
 
76
121
  walkNodes(nodes) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "confluence-cli",
3
- "version": "2.1.11",
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.11",
9
+ "version": "2.1.12",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "axios": "^1.15.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluence-cli",
3
- "version": "2.1.11",
3
+ "version": "2.1.12",
4
4
  "description": "A command-line interface for Atlassian Confluence with page creation and editing capabilities",
5
5
  "main": "index.js",
6
6
  "bin": {