Skip to content

INFO/WARNING/NOTE markers leak into macro body and trigger on prose mentions #131

@devlimits

Description

@devlimits

Summary

The INFO / WARNING / NOTE callout markers in MacroConverter produce two related issues that diverge from the documented behavior:

  1. The marker text (**INFO** / **WARNING** / **NOTE**) leaks into the rendered macro body for the README-documented > **INFO**\n> body form.
  2. Detection wraps any blockquote that merely mentions the marker keyword in prose (e.g. > Use **INFO** at the start.) in a callout macro.

Reproduction

1. Marker text leak

const MacroConverter = require('confluence-cli/lib/macro-converter');
const c = new MacroConverter({ isCloud: true });
console.log(c.markdownToStorage('> **INFO**\n> Heads up.'));

Actual output:

<ac:structured-macro ac:name="info">
  <ac:rich-text-body>
    <p><strong>INFO</strong>
Heads up.</p>
  </ac:rich-text-body>
</ac:structured-macro>

Expected (per README and #5):

<ac:structured-macro ac:name="info">
  <ac:rich-text-body>
    <p>Heads up.</p>
  </ac:rich-text-body>
</ac:structured-macro>

The same single-paragraph shape is what the [!info] preprocessor emits, so [!info]\nbody round-trip is also affected.

The cleanup regex on line 89 currently matches only the separated-paragraph form <p><strong>INFO</strong></p>, but markdown-it parses > **INFO**\n> body as a single paragraph <p><strong>INFO</strong>\nbody</p>, which never matches.

2. False-positive macro wrapping

console.log(c.markdownToStorage('> Use **INFO** at the start of a callout.'));

Actual output:

<ac:structured-macro ac:name="info">
  <ac:rich-text-body>
    <p>Use <strong>INFO</strong> at the start of a callout.</p>
  </ac:rich-text-body>
</ac:structured-macro>

Expected: a plain <blockquote> (the marker keyword is just being mentioned, not used as a callout signal).

The detection step uses content.includes('<strong>INFO</strong>'), which matches any occurrence of the keyword anywhere in the blockquote.

Impact

Root cause

Both stem from the marker handling having different anchor semantics in detection vs. stripping:

  • Detection: content.includes('<strong>MARKER</strong>') — too broad
  • Stripping: <p><strong>MARKER</strong></p> — too narrow (only the separated-paragraph shape)

A single anchor — "the marker is the first thing inside the first paragraph, immediately followed by </p> or \n" — covers both correctly.

Happy to send a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions