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
8 changes: 6 additions & 2 deletions packages/core/src/config/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ export * as ConfigMarkdown from "./markdown.js"

import matter from "gray-matter"
export function parse(content: string) {
// Passing options bypasses gray-matter's module-global content cache, which
// it populates before parsing: a failed YAML parse poisons the entry and
// every later parse of the same content replays it without throwing, so the
// sanitize fallback below never runs. Upstream: jonschlinkert/gray-matter#166.
try {
return matter(content)
return matter(content, {})
} catch {
return matter(sanitize(content))
return matter(sanitize(content), {})
}
}

Expand Down
35 changes: 35 additions & 0 deletions packages/core/test/config/markdown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, test } from "bun:test"
import { ConfigMarkdown } from "@opencode/core/config/markdown"

const invalidYaml = `---
description: Use when the user needs to crawl pages. Keywords: crawl, scrape
---
body`

describe("ConfigMarkdown.parse", () => {
test("recovers unquoted-colon frontmatter via the sanitize fallback", () => {
const parsed = ConfigMarkdown.parse(invalidYaml)
expect(parsed.data.description).toBe("Use when the user needs to crawl pages. Keywords: crawl, scrape")
expect(parsed.content.trim()).toBe("body")
})

test("recovers the same content again after a previous failed parse", () => {
// gray-matter caches by content before parsing; a poisoned entry used to
// make every later parse of the same text return silently without data.
expect(() => ConfigMarkdown.parse("---\ndescription: [unclosed\n---\nbody")).toThrow()
const parsed = ConfigMarkdown.parse(invalidYaml)
expect(parsed.data.description).toBe("Use when the user needs to crawl pages. Keywords: crawl, scrape")
})

test("keeps throwing for the same unparseable content on every call", () => {
const input = "---\ndescription: [unclosed\n---\nbody"
expect(() => ConfigMarkdown.parse(input)).toThrow()
expect(() => ConfigMarkdown.parse(input)).toThrow()
})

test("parses plain content without frontmatter", () => {
const parsed = ConfigMarkdown.parse("just body")
expect(parsed.content).toBe("just body")
expect(parsed.data).toEqual({})
})
})
Loading