diff --git a/.changeset/ten-moons-brake.md b/.changeset/ten-moons-brake.md new file mode 100644 index 0000000000..f034e1319e --- /dev/null +++ b/.changeset/ten-moons-brake.md @@ -0,0 +1,5 @@ +--- +'@astrojs/markdown-remark': patch +--- + +Avoids parsing frontmatter that are not at the top of a file diff --git a/packages/markdown/remark/src/frontmatter.ts b/packages/markdown/remark/src/frontmatter.ts index 60ae0b5da0..94fbec2b33 100644 --- a/packages/markdown/remark/src/frontmatter.ts +++ b/packages/markdown/remark/src/frontmatter.ts @@ -10,7 +10,10 @@ export function isFrontmatterValid(frontmatter: Record) { return typeof frontmatter === 'object' && frontmatter !== null; } -const frontmatterRE = /^---(.*?)^---/ms; +// Capture frontmatter wrapped with `---`, including any characters and new lines within it. +// Only capture if it exists near the top of the file (whitespaces between the start of file and +// the start of `---` are allowed) +const frontmatterRE = /^\s*---([\s\S]*?\n)---/; export function extractFrontmatter(code: string): string | undefined { return frontmatterRE.exec(code)?.[1]; } diff --git a/packages/markdown/remark/test/frontmatter.test.js b/packages/markdown/remark/test/frontmatter.test.js new file mode 100644 index 0000000000..6a8b6b37ca --- /dev/null +++ b/packages/markdown/remark/test/frontmatter.test.js @@ -0,0 +1,79 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { extractFrontmatter, parseFrontmatter } from '../dist/index.js'; + +describe('extractFrontmatter', () => { + it('works', () => { + const yaml = `\nfoo: bar\n`; + assert.equal(extractFrontmatter(`---${yaml}---`), yaml); + assert.equal(extractFrontmatter(` ---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`\n---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`\n \n---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`---${yaml}---\ncontent`), yaml); + assert.equal(extractFrontmatter(`\n\n---${yaml}---\n\ncontent`), yaml); + assert.equal(extractFrontmatter(`\n \n---${yaml}---\n\ncontent`), yaml); + assert.equal(extractFrontmatter(`text\n---${yaml}---\n\ncontent`), undefined); + }); +}); + +describe('parseFrontmatter', () => { + it('works', () => { + const yaml = `\nfoo: bar\n`; + assert.deepEqual(parseFrontmatter(`---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '', + }); + assert.deepEqual(parseFrontmatter(` ---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: ' ', + }); + assert.deepEqual(parseFrontmatter(`\n---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n', + }); + assert.deepEqual(parseFrontmatter(`\n \n---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n \n', + }); + assert.deepEqual(parseFrontmatter(`---${yaml}---\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\ncontent', + }); + assert.deepEqual(parseFrontmatter(`\n\n---${yaml}---\n\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n\n\n\ncontent', + }); + assert.deepEqual(parseFrontmatter(`\n \n---${yaml}---\n\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n \n\n\ncontent', + }); + assert.deepEqual(parseFrontmatter(`text\n---${yaml}---\n\ncontent`), { + frontmatter: {}, + rawFrontmatter: '', + content: `text\n---${yaml}---\n\ncontent`, + }); + }); + + it('frontmatter style', () => { + const yaml = `\nfoo: bar\n`; + const parse1 = (style) => parseFrontmatter(`---${yaml}---`, { frontmatter: style }).content; + assert.deepEqual(parse1('preserve'), `---${yaml}---`); + assert.deepEqual(parse1('remove'), ''); + assert.deepEqual(parse1('empty-with-spaces'), ` \n \n `); + assert.deepEqual(parse1('empty-with-lines'), `\n\n`); + + const parse2 = (style) => + parseFrontmatter(`\n \n---${yaml}---\n\ncontent`, { frontmatter: style }).content; + assert.deepEqual(parse2('preserve'), `\n \n---${yaml}---\n\ncontent`); + assert.deepEqual(parse2('remove'), '\n \n\n\ncontent'); + assert.deepEqual(parse2('empty-with-spaces'), `\n \n \n \n \n\ncontent`); + assert.deepEqual(parse2('empty-with-lines'), `\n \n\n\n\n\ncontent`); + }); +});