confluence-cli 2.0.2 → 2.1.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/README.md +18 -0
- package/lib/macro-converter.js +50 -16
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -809,6 +809,24 @@ A paragraph containing only `**ANCHOR: my-section**` becomes a Confluence anchor
|
|
|
809
809
|
**ANCHOR: my-section**
|
|
810
810
|
```
|
|
811
811
|
|
|
812
|
+
### `**EXPAND: title** … **EXPAND_END**` — collapsible expand macro
|
|
813
|
+
|
|
814
|
+
Wrap a block of content between `**EXPAND: title**` and `**EXPAND_END**` markers (each on its own paragraph) to render it as a Confluence expand macro with a collapsible body:
|
|
815
|
+
|
|
816
|
+
````markdown
|
|
817
|
+
**EXPAND: Show generated code**
|
|
818
|
+
|
|
819
|
+
```js
|
|
820
|
+
const x = 1;
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
**EXPAND_END**
|
|
824
|
+
````
|
|
825
|
+
|
|
826
|
+
The body may contain any content that is converted earlier in the pipeline (code blocks, tables, callout blockquotes). The reverse direction emits the same `**EXPAND: title**` / `**EXPAND_END**` markers so the conversion round-trips.
|
|
827
|
+
|
|
828
|
+
Inline markdown inside the title (`*em*`, backtick code, links, `~~strike~~`) is stripped at capture time — Confluence's storage normalizer treats macro titles as plain text and will silently truncate or reject HTML in a `<ac:parameter>`. Title-less expand macros created in the Confluence UI still convert to `<details>/<summary>` blocks.
|
|
829
|
+
|
|
812
830
|
### `[text](#id)` — same-page anchor link
|
|
813
831
|
|
|
814
832
|
A standard markdown link whose href starts with `#` becomes an `ac:link` with `ac:anchor`, rendering as an in-page jump in Confluence:
|
package/lib/macro-converter.js
CHANGED
|
@@ -2,6 +2,7 @@ const MarkdownIt = require('markdown-it');
|
|
|
2
2
|
const { htmlToMarkdown } = require('./html-to-markdown');
|
|
3
3
|
|
|
4
4
|
const VALID_LINK_STYLES = ['smart', 'plain', 'wiki'];
|
|
5
|
+
const CALLOUT_MARKERS = ['info', 'warning', 'note'];
|
|
5
6
|
|
|
6
7
|
class MacroConverter {
|
|
7
8
|
constructor({ isCloud = false, webUrlPrefix = '', buildUrl = null, linkStyle = null } = {}) {
|
|
@@ -89,27 +90,36 @@ class MacroConverter {
|
|
|
89
90
|
);
|
|
90
91
|
|
|
91
92
|
storage = storage.replace(/<blockquote>(.*?)<\/blockquote>/gs, (_, content) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const cleanContent = content.replace(/<p><strong>NOTE<\/strong><\/p>\s*/, '');
|
|
104
|
-
return `<ac:structured-macro ac:name="note">
|
|
105
|
-
<ac:rich-text-body>${cleanContent}</ac:rich-text-body>
|
|
106
|
-
</ac:structured-macro>`;
|
|
107
|
-
} else {
|
|
93
|
+
// Detect the marker only when it sits at the very start of the first
|
|
94
|
+
// paragraph, immediately followed by a `</p>` close (separated form) or
|
|
95
|
+
// a `\n` (same-line body form). This is the same anchor condition the
|
|
96
|
+
// strip step uses below, so detection and stripping stay in sync.
|
|
97
|
+
// Without this anchor, a quotation that merely *mentions* `**INFO**` —
|
|
98
|
+
// e.g. `> Use **INFO** at the start.` — would be silently wrapped in an
|
|
99
|
+
// info macro, surprising the author.
|
|
100
|
+
const marker = CALLOUT_MARKERS.find((m) =>
|
|
101
|
+
new RegExp(`<p><strong>${m.toUpperCase()}<\\/strong>(<\\/p>|\\s*\\n)`).test(content)
|
|
102
|
+
);
|
|
103
|
+
if (!marker) {
|
|
108
104
|
// Plain blockquote — `> …` is a quotation, not an alert. Use the
|
|
109
105
|
// `> **INFO**` / `> **WARNING**` / `> **NOTE**` markers above to
|
|
110
106
|
// produce a Confluence info / warning / note macro instead.
|
|
111
107
|
return `<blockquote>${content}</blockquote>`;
|
|
112
108
|
}
|
|
109
|
+
// Strip the leading `<strong>MARKER</strong>`. markdown-it produces two
|
|
110
|
+
// shapes depending on whether a blank `>` line separates marker and body:
|
|
111
|
+
// case A (separated): `<p><strong>MARKER</strong></p>\n<p>body</p>`
|
|
112
|
+
// case B (same-line): `<p><strong>MARKER</strong>\nbody</p>`
|
|
113
|
+
// The original cleanup only handled case A, so case B leaked the marker
|
|
114
|
+
// into the rendered macro body. README's recommended `> **INFO**\n> body`
|
|
115
|
+
// form parses as case B — exactly the form that broke.
|
|
116
|
+
const cleanContent = content.replace(
|
|
117
|
+
new RegExp(`<p><strong>${marker.toUpperCase()}<\\/strong>(<\\/p>\\s*|\\s*\\n)`),
|
|
118
|
+
(_, tail) => tail.startsWith('</p>') ? '' : '<p>'
|
|
119
|
+
);
|
|
120
|
+
return `<ac:structured-macro ac:name="${marker}">
|
|
121
|
+
<ac:rich-text-body>${cleanContent}</ac:rich-text-body>
|
|
122
|
+
</ac:structured-macro>`;
|
|
113
123
|
});
|
|
114
124
|
|
|
115
125
|
storage = storage.replace(/<table>(.*?)<\/table>/gs, '<table>$1</table>');
|
|
@@ -125,6 +135,20 @@ class MacroConverter {
|
|
|
125
135
|
'<ac:structured-macro ac:name="anchor"><ac:parameter ac:name="">$1</ac:parameter></ac:structured-macro>'
|
|
126
136
|
);
|
|
127
137
|
|
|
138
|
+
// **EXPAND: title** … **EXPAND_END** → Confluence expand macro. Runs
|
|
139
|
+
// after code/blockquote/table conversion so the body can contain those
|
|
140
|
+
// macros. Strips inline HTML from the title because Confluence's storage
|
|
141
|
+
// normalizer treats <ac:parameter> as text-only — it silently truncates
|
|
142
|
+
// at the first '<' and rejects <s> outright with HTTP 500. Entities
|
|
143
|
+
// (&, <) survive because the regex requires a literal '<'.
|
|
144
|
+
storage = storage.replace(
|
|
145
|
+
/<p><strong>EXPAND: (.*?)<\/strong><\/p>\s*([\s\S]*?)\s*<p><strong>EXPAND_END<\/strong><\/p>/g,
|
|
146
|
+
(_, title, body) => {
|
|
147
|
+
const cleanTitle = title.replace(/<[^>]+>/g, '').trim();
|
|
148
|
+
return `<ac:structured-macro ac:name="expand"><ac:parameter ac:name="title">${cleanTitle}</ac:parameter><ac:rich-text-body>${body.trim()}</ac:rich-text-body></ac:structured-macro>`;
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
|
|
128
152
|
// Same-page anchor links (href="#id") → ac:link with ac:anchor. Must run
|
|
129
153
|
// before the general link conversion below so the #id pattern is not
|
|
130
154
|
// consumed by the generic <a href> replacement (and so it works under
|
|
@@ -235,6 +259,16 @@ class MacroConverter {
|
|
|
235
259
|
return `\n\`\`\`mermaid\n${code.trim()}\n\`\`\`\n`;
|
|
236
260
|
});
|
|
237
261
|
|
|
262
|
+
// Titled expand macros → **EXPAND: title** / **EXPAND_END** markers
|
|
263
|
+
// (round-trip with markdownToStorage). Must run before the generic
|
|
264
|
+
// <details> fallback below, which would otherwise drop the title.
|
|
265
|
+
markdown = markdown.replace(
|
|
266
|
+
/<ac:structured-macro ac:name="expand"[^>]*>[\s\S]*?<ac:parameter ac:name="title">([\s\S]*?)<\/ac:parameter>[\s\S]*?<ac:rich-text-body>([\s\S]*?)<\/ac:rich-text-body>[\s\S]*?<\/ac:structured-macro>/g,
|
|
267
|
+
'\n**EXPAND: $1**\n\n$2\n\n**EXPAND_END**\n'
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// Title-less expand macros (e.g. created in the Confluence UI without a
|
|
271
|
+
// title) fall back to <details>/<summary> with a localized default label.
|
|
238
272
|
markdown = markdown.replace(/<ac:structured-macro ac:name="expand"[^>]*>[\s\S]*?<ac:rich-text-body>([\s\S]*?)<\/ac:rich-text-body>[\s\S]*?<\/ac:structured-macro>/g, (_, content) => {
|
|
239
273
|
return `\n<details>\n<summary>${labels.expandDetails}</summary>\n\n${content}\n\n</details>\n`;
|
|
240
274
|
});
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "confluence-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "confluence-cli",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.1.1",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"axios": "^1.15.0",
|