Skip to content

A backslash before a pipe in a description breaks the generated table row #672

Description

@Jamie-BitFlight

Reproduction

action.yml:

inputs:
  mode:
    description: 'Accepts a literal backslash-pipe \| in the text.'
    default: 'a'

Generated inputs row:

| <b><code>mode</code></b> | Accepts a literal backslash-pipe \\ | in the text. | <code>a</code> | **false** |

That | is a live cell delimiter. The row now has six cells where the header has five, so every column after the description shifts by one. scripts/verify-readme-contract.mjs catches it:

::error::`mode` description in the inputs table does not match action.yml — want "Accepts a literal backslash-pipe | in the text.", got "Accepts a literal backslash-pipe \\\\"
::error::`mode` default in the inputs table does not match action.yml — want "a", got "in the text."
::error::`mode` required flag in the inputs table does not match action.yml — want "false", got "a"

Cause

src/markdowner/index.ts:26

export function markdownEscapeTableCell(text: string): string {
  return text.replaceAll('\n', '<br />').replaceAll('|', '\\|');
}

The pipe is escaped; the backslash never is. A source \| becomes \\| — an escaped backslash, then an unescaped pipe. The correct output is \\\|: escaped backslash, escaped pipe.

The same omission affects a backslash before any Markdown punctuation. C:\*glob* renders with the * swallowed as an escape.

Fix direction

Escape backslashes before escaping pipes, in that order:

text.replaceAll('\n', '<br />').replaceAll('\\', '\\\\').replaceAll('|', '\\|')

markdownEscapeInlineCode runs afterwards (markdowner/index.ts:109-110) and inserts its own \< for ><!--, so ordering the backslash escape inside markdownEscapeTableCell leaves that escape alone.

Two things travel with the fix:

  1. normalise in scripts/verify-readme-contract.mjs decodes \| and \<. It needs to decode \\ too, or the verifier fails on the newly-correct output. A single backslash-aware unescape pass over cell text is the way to keep the two consistent — but it must be applied to rendered cells only, never to the action.yml source it is compared against, since the source is not escaped.
  2. This changes generated output for any third-party README whose descriptions contain backslashes, so it wants its own integration-test run.

Why it is filed rather than fixed in #634

#634 already changes bundling, config precedence, redaction and the contract verifier. This changes cell escaping for every generated table in every consumer repository, which deserves its own diff and its own third-party run.

Found while verifying a CodeRabbit review comment on #634 that reported the symptom as a verifier normalisation asymmetry. The verifier is behaving correctly here — the row it rejects really is malformed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

releasedThis issue/pull request has been released.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions