diff --git a/packages/parser/src/core.ts b/packages/parser/src/core.ts index 82fbd22a09..2d1f91b828 100644 --- a/packages/parser/src/core.ts +++ b/packages/parser/src/core.ts @@ -1,6 +1,6 @@ import YAML from 'js-yaml' import { isObject, isTruthy, objectMap } from '@antfu/utils' -import type { PreparserExtensionFromHeadmatter, SlideInfo, SlideInfoBase, SlidevFeatureFlags, SlidevMarkdown, SlidevPreparserExtension, SlidevThemeMeta } from '@slidev/types' +import type { FrontmatterStyle, PreparserExtensionFromHeadmatter, SlideInfo, SlideInfoBase, SlidevFeatureFlags, SlidevMarkdown, SlidevPreparserExtension, SlidevThemeMeta } from '@slidev/types' import { resolveConfig } from './config' export function stringify(data: SlidevMarkdown) { @@ -30,7 +30,9 @@ export function stringifySlide(data: SlideInfoBase, idx = 0) { export function prettifySlide(data: SlideInfoBase) { data.content = `\n${data.content.trim()}\n` data.raw = Object.keys(data.frontmatter || {}).length - ? `---\n${YAML.dump(data.frontmatter).trim()}\n---\n${data.content}` + ? data.frontmatterStyle === 'yaml' + ? `\`\`\`yaml\n${YAML.dump(data.frontmatter).trim()}\n\`\`\`\n${data.content}` + : `---\n${YAML.dump(data.frontmatter).trim()}\n---\n${data.content}` : data.content if (data.note) data.raw += `\n\n` @@ -44,18 +46,37 @@ export function prettify(data: SlidevMarkdown) { return data } +function safeParseYAML(str: string) { + const res = YAML.load(str) + return isObject(res) ? res : {} +} + function matter(code: string) { - let data: any = {} - const content = code.replace( - /^---.*\r?\n([\s\S]*?)---/, - (_, d) => { - data = YAML.load(d) - if (!isObject(data)) - data = {} + let type: FrontmatterStyle | undefined + + const data: any = {} + + let content = code + .replace(/^---.*\r?\n([\s\S]*?)---/, (_, f) => { + type = 'frontmatter' + Object.assign(data, safeParseYAML(f)) return '' - }, - ) - return { data, content } + }) + + if (type !== 'frontmatter') { + content = content + .replace(/^\s*```ya?ml([\s\S]*?)```/, (_, d) => { + type = 'yaml' + Object.assign(data, safeParseYAML(d)) + return '' + }) + } + + return { + type, + data, + content, + } } export function detectFeatures(code: string): SlidevFeatureFlags { @@ -68,10 +89,10 @@ export function detectFeatures(code: string): SlidevFeatureFlags { } export function parseSlide(raw: string): SlideInfoBase { - const result = matter(raw) + const matterResult = matter(raw) let note: string | undefined - const frontmatter = result.data || {} - let content = result.content.trim() + const frontmatter = matterResult.data || {} + let content = matterResult.content.trim() const comments = Array.from(content.matchAll(//g)) if (comments.length) { @@ -101,6 +122,7 @@ export function parseSlide(raw: string): SlideInfoBase { level, content, frontmatter, + frontmatterStyle: matterResult.type, note, } } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 96502df5f5..105e54bfbb 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -1,10 +1,13 @@ import type { SlidevConfig } from './config' +export type FrontmatterStyle = 'frontmatter' | 'yaml' + export interface SlideInfoBase { raw: string content: string note?: string frontmatter: Record + frontmatterStyle?: FrontmatterStyle title?: string level?: number } diff --git a/test/__snapshots__/parser.test.ts.snap b/test/__snapshots__/parser.test.ts.snap index fae7ef653a..b4eaacfe8d 100644 --- a/test/__snapshots__/parser.test.ts.snap +++ b/test/__snapshots__/parser.test.ts.snap @@ -92,6 +92,7 @@ exports[`md parser > frontmatter.md > slides 1`] = ` "layout": "cover", "title": "Hi", }, + "frontmatterStyle": "frontmatter", "index": 0, "level": 1, "note": undefined, @@ -122,6 +123,7 @@ title: Hi "title": "FooBar", }, }, + "frontmatterStyle": "frontmatter", "index": 1, "level": 1, "note": "This is note", @@ -147,6 +149,7 @@ This is note ", "end": 19, "frontmatter": {}, + "frontmatterStyle": undefined, "index": 2, "level": 1, "note": undefined, @@ -166,6 +169,7 @@ Hey "frontmatter": { "layout": "text", }, + "frontmatterStyle": "frontmatter", "index": 3, "level": undefined, "note": "This is note", @@ -194,6 +198,7 @@ this should be treated as code block ", "end": 35, "frontmatter": {}, + "frontmatterStyle": undefined, "index": 4, "level": undefined, "note": undefined, @@ -209,6 +214,91 @@ this should be treated as code block "start": 27, "title": undefined, }, + { + "content": " +Content 1 +", + "end": 44, + "frontmatter": { + "layout": "from yaml", + }, + "frontmatterStyle": "yaml", + "index": 5, + "level": undefined, + "note": undefined, + "raw": "\`\`\`yaml +layout: from yaml +\`\`\` + +Content 1 + +", + "start": 36, + "title": undefined, + }, + { + "content": " +\`\`\`yaml +# When there is already a frontmatter, the first yaml block should be treated as content +layout: should not from yaml 1 +\`\`\` + +Content 2 +", + "end": 55, + "frontmatter": { + "layout": "cover", + }, + "frontmatterStyle": "frontmatter", + "index": 6, + "level": 1, + "note": undefined, + "raw": "--- +layout: cover +--- + +\`\`\`yaml +# When there is already a frontmatter, the first yaml block should be treated as content +layout: should not from yaml 1 +\`\`\` + +Content 2 + +", + "start": 44, + "title": "When there is already a frontmatter, the first yaml block should be treated as content", + }, + { + "content": " +# Title + +\`\`\`yaml +# When there is already a frontmatter, the first yaml block should be treated as content +layout: should not from yaml 2 +\`\`\` + +Content 3 +", + "end": 66, + "frontmatter": {}, + "frontmatterStyle": undefined, + "index": 7, + "level": 1, + "note": undefined, + "raw": " +# Title + +\`\`\`yaml +# When there is already a frontmatter, the first yaml block should be treated as content +layout: should not from yaml 2 +\`\`\` + +Content 3 + +", + "start": 56, + "title": "Title", + }, ] `; @@ -292,6 +382,7 @@ exports[`md parser > mdc.md > slides 1`] = ` "mdc": true, "title": "MDC{style=\\"color:red\\"}", }, + "frontmatterStyle": "frontmatter", "index": 0, "level": 1, "note": undefined, @@ -395,6 +486,7 @@ console.log('Hello World') "frontmatter": { "title": "H1", }, + "frontmatterStyle": undefined, "index": 0, "level": 1, "note": undefined, @@ -427,6 +519,7 @@ console.log('Hello World') ", "end": 18, "frontmatter": {}, + "frontmatterStyle": undefined, "index": 1, "level": 1, "note": undefined, @@ -448,6 +541,7 @@ Nice to meet you ", "end": 22, "frontmatter": {}, + "frontmatterStyle": undefined, "index": 2, "level": undefined, "note": undefined, @@ -539,6 +633,7 @@ exports[`md parser > multi-entries.md > slides 1`] = ` "srcSequence": "sub/page1.md", "title": undefined, }, + "frontmatterStyle": undefined, "index": 0, "inline": { "content": "", @@ -546,6 +641,7 @@ exports[`md parser > multi-entries.md > slides 1`] = ` "frontmatter": { "title": undefined, }, + "frontmatterStyle": "frontmatter", "index": 0, "level": undefined, "note": undefined, @@ -571,6 +667,7 @@ srcSequence: sub/page1.md "frontmatter": { "title": "Page 1", }, + "frontmatterStyle": undefined, "index": 0, "level": 1, "note": undefined, @@ -595,6 +692,7 @@ srcSequence: sub/page1.md "srcSequence": "/sub/page2.md", "title": "Page 2", }, + "frontmatterStyle": "frontmatter", "index": 1, "inline": { "content": "", @@ -602,6 +700,7 @@ srcSequence: sub/page1.md "frontmatter": { "background": "https://sli.dev/demo-cover.png#2", }, + "frontmatterStyle": "frontmatter", "index": 1, "level": undefined, "note": undefined, @@ -636,6 +735,7 @@ srcSequence: /sub/page2.md "layout": "cover", "title": "Page 2", }, + "frontmatterStyle": "frontmatter", "index": 0, "level": 1, "note": undefined, @@ -663,6 +763,7 @@ layout: cover "srcSequence": "./sub/pages3-4.md", "title": "Page 3", }, + "frontmatterStyle": undefined, "index": 2, "inline": { "content": "", @@ -670,6 +771,7 @@ layout: cover "frontmatter": { "background": "https://sli.dev/demo-cover.png#34", }, + "frontmatterStyle": "frontmatter", "index": 2, "level": undefined, "note": undefined, @@ -698,6 +800,7 @@ srcSequence: ./sub/pages3-4.md "frontmatter": { "title": "Page 3", }, + "frontmatterStyle": undefined, "index": 0, "level": 1, "note": undefined, @@ -721,6 +824,7 @@ srcSequence: ./sub/pages3-4.md "layout": "cover", "srcSequence": "./sub/pages3-4.md", }, + "frontmatterStyle": "frontmatter", "index": 3, "level": 1, "note": undefined, @@ -743,6 +847,7 @@ srcSequence: ./sub/pages3-4.md "frontmatter": { "layout": "cover", }, + "frontmatterStyle": "frontmatter", "index": 1, "level": 1, "note": undefined, @@ -770,6 +875,7 @@ layout: cover "srcSequence": "sub/nested1-4.md,/sub/page1.md", "title": undefined, }, + "frontmatterStyle": undefined, "index": 4, "inline": { "content": "", @@ -777,6 +883,7 @@ layout: cover "frontmatter": { "background": "https://sli.dev/demo-cover.png#14", }, + "frontmatterStyle": "frontmatter", "index": 3, "level": undefined, "note": undefined, @@ -804,6 +911,7 @@ srcSequence: sub/nested1-4.md,/sub/page1.md "frontmatter": { "title": "Page 1", }, + "frontmatterStyle": undefined, "index": 0, "level": 1, "note": undefined, @@ -828,6 +936,7 @@ srcSequence: sub/nested1-4.md,/sub/page1.md "srcSequence": "sub/nested1-4.md,page2.md", "title": "Page 2", }, + "frontmatterStyle": "frontmatter", "index": 5, "level": 1, "note": undefined, @@ -852,6 +961,7 @@ srcSequence: sub/nested1-4.md,page2.md "layout": "cover", "title": "Page 2", }, + "frontmatterStyle": "frontmatter", "index": 0, "level": 1, "note": undefined, @@ -879,6 +989,7 @@ layout: cover "srcSequence": "sub/nested1-4.md,../sub/pages3-4.md", "title": "Page 3", }, + "frontmatterStyle": undefined, "index": 6, "level": 1, "note": undefined, @@ -897,6 +1008,7 @@ srcSequence: sub/nested1-4.md,../sub/pages3-4.md "frontmatter": { "title": "Page 3", }, + "frontmatterStyle": undefined, "index": 0, "level": 1, "note": undefined, @@ -920,6 +1032,7 @@ srcSequence: sub/nested1-4.md,../sub/pages3-4.md "layout": "cover", "srcSequence": "sub/nested1-4.md,../sub/pages3-4.md", }, + "frontmatterStyle": "frontmatter", "index": 7, "level": 1, "note": undefined, @@ -942,6 +1055,7 @@ srcSequence: sub/nested1-4.md,../sub/pages3-4.md "frontmatter": { "layout": "cover", }, + "frontmatterStyle": "frontmatter", "index": 1, "level": 1, "note": undefined, @@ -967,6 +1081,7 @@ $x+2$ ", "end": 25, "frontmatter": {}, + "frontmatterStyle": undefined, "index": 8, "level": 1, "note": undefined, diff --git a/test/fixtures/markdown/frontmatter.md b/test/fixtures/markdown/frontmatter.md index 4b50f7f78f..235aab9852 100644 --- a/test/fixtures/markdown/frontmatter.md +++ b/test/fixtures/markdown/frontmatter.md @@ -32,3 +32,34 @@ this should be treated as code block --- ---- ``` + +--- + +```yaml +# The first yaml block should be treated as frontmatter +layout: from yaml +``` + +Content 1 + +--- +layout: cover +--- + +```yaml +# When there is already a frontmatter, the first yaml block should be treated as content +layout: should not from yaml 1 +``` + +Content 2 + +--- + +# Title + +```yaml +# When there is already a frontmatter, the first yaml block should be treated as content +layout: should not from yaml 2 +``` + +Content 3