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: 18 additions & 0 deletions src/frontmatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ describe('frontmatter round-trip', () => {
expect(roundTrip(values)).toEqual(values)
})

it('keeps the last character of a bare scalar that ends in a quote', () => {
// A leading and a trailing quote were stripped independently, so a value that merely ended in
// one lost a character. `check: python3 -c "print(1)"` read back unterminated and every claim
// it graded became unrunnable.
expect(
parseFrontmatter(
`---\ncheck: python3 -c "print(1)"\ntitle: the symbol '7'\nquoted: "still unwrapped"\n---\nBody\n`,
),
).toEqual({
frontmatter: {
check: 'python3 -c "print(1)"',
title: "the symbol '7'",
quoted: 'still unwrapped',
},
body: 'Body\n',
})
})

it('keeps reading the existing simple frontmatter syntax', () => {
expect(
parseFrontmatter(
Expand Down
17 changes: 16 additions & 1 deletion src/frontmatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ function stringNeedsJsonEncoding(value: string): boolean {
return /^[[{"']/.test(value) || /["']$/.test(value)
}

/**
* Strip a MATCHED surrounding quote pair, and nothing else.
*
* The previous rule stripped a leading or a trailing quote independently, so a bare scalar that
* merely ended in one lost its last character: `check: python3 -c "print(1)"` read back as
* `python3 -c "print(1)` — an unterminated shell quote that no longer runs. Measured on one
* corpus, 1,378 of 3,655 pages carried that shape, 1,376 of them in a `check` field.
*
* `formatYamlScalar` already refuses to WRITE the shape (`stringNeedsJsonEncoding` returns true
* for a value matching /["']$/), so no page this writer produced was ever affected; the loss fell
* on frontmatter written by any other hand.
*/
function unquote(value: string): string {
return value.replace(/^['"]|['"]$/g, '')
if (value.length < 2) return value
const first = value[0]
if ((first === '"' || first === "'") && value.endsWith(first)) return value.slice(1, -1)
return value
}