/** * lib/cv-markdown.mjs — read cv.md's structure through Pandoc-flavored * Markdown (#4879). * * cv-title-check.mjs, verify-cv-structure.mjs and jd-skill-gap.mjs each find * cv.md's sections and entries with regexes. Those regexes were written * against examples/cv-example.md, and a cv.md converted from a Word file with * Pandoc carries the same content in a different shape: * * ## **[Professional Experience]{.smallcaps}** * * ### **Acme Health, Inc.** --- New York, NY (Remote) * * **Senior Director, Platform Engineering**\ * **09/2021 -- Present** * * Every check read that as "no Experience section" and degraded without a * word: cv-title-check compared 0 entries and still printed ✅. This module is * the one place that knows how to see through that shape, so the three checks * cannot drift apart again. * * Normalization is deliberately split in two: * - normalizePandocLine() keeps structure: it drops hard breaks, unwraps * `[text]{.class}` spans and turns Pandoc's `---` / `--` into the em / en * dash they render as, but leaves `**bold**` and backslash escapes alone, * so a caller can still ask "is this whole line bold?". * - toPlainText() is for a value the caller has already located (a heading, * a title, a dates line): it removes emphasis markers and unescapes. * Unescaping last matters: `\*\*` is a literal pair of asterisks, and turning * it into `**` before the bold test would invent emphasis that isn't there. * * Imports nothing, so doctor.mjs can use it without pulling in a check. */ // A whole line of three or more `-`, `*` or `_` is a thematic break (or YAML // front matter), and a line starting with `|` is a table row. Neither is prose // Pandoc would have smartened, so their dashes are left alone. const THEMATIC_BREAK_RE = /^\s*([-*_])(?:\s*\1){2,}\s*$/; const TABLE_ROW_RE = /^\s*\|/; /** * Normalize one line of Pandoc Markdown without losing its structure. * * @param {string} line * @returns {string} */ export function normalizePandocLine(line) { let s = String(line ?? '').replace(/\r$/, ''); // Hard line break: a single trailing backslash. `\\` at the end is an // escaped literal backslash, not a break. s = s.replace(/(?