Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion web/scripts/blog-announce-discussion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ export function splitFrontmatter(content) {
return { fm: m[1], rest: content.slice(m[0].length) };
}

/**
* Strip one layer of YAML quoting from a scalar and unescape that style.
* Single quotes double an apostrophe (`''` -> `'`); double quotes use `\"`
* and `\\`. The style is decided once, so a value like `'"x"'` is stripped
* exactly one layer rather than twice.
*/
function unquoteScalar(raw) {
if (raw.length >= 2 && raw.startsWith("'") && raw.endsWith("'")) {
return raw.slice(1, -1).replace(/''/g, "'");
}
if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"')) {
return raw.slice(1, -1).replace(/\\(["\\])/g, '$1');
}
return raw;
}

/**
* Extract { title, body, category, discussionUrl } from an `announcement:`
* block, or return null when the post has none.
Expand Down Expand Up @@ -99,7 +115,7 @@ export function extractAnnouncement(fm) {
}

if (raw !== '') {
out[key] = raw.replace(/^'(.*)'$/, '$1').replace(/^"(.*)"$/, '$1');
out[key] = unquoteScalar(raw);
}
i++;
}
Expand Down
20 changes: 20 additions & 0 deletions web/scripts/blog-announce-discussion.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ test('extractAnnouncement returns null when the block is missing', () => {
assert.equal(extractAnnouncement('title: hello\n'), null);
});

test("extractAnnouncement unescapes doubled single quotes inside a single-quoted scalar", () => {
const fm = `announcement:\n title: 'the upgrade doesn''t touch'\n body: |\n body text\n`;
assert.equal(extractAnnouncement(fm).title, "the upgrade doesn't touch");
});

test('extractAnnouncement unescapes backslash escapes in a double-quoted scalar', () => {
const fm = `announcement:\n title: "say \\"hi\\" and back\\\\slash"\n body: |\n body text\n`;
assert.equal(extractAnnouncement(fm).title, 'say "hi" and back\\slash');
});

test('extractAnnouncement strips a single-quoted scalar only once', () => {
const fm = `announcement:\n title: '"x"'\n body: |\n body text\n`;
assert.equal(extractAnnouncement(fm).title, '"x"');
});

test('extractAnnouncement leaves a plain unquoted scalar unchanged', () => {
const fm = `announcement:\n title: Plain title\n body: |\n body text\n`;
assert.equal(extractAnnouncement(fm).title, 'Plain title');
});

test('extractSlug reads an unquoted slug', () => {
assert.equal(extractSlug(SAMPLE_FM), 'pretty-urls-with-route-bindby');
});
Expand Down
Loading