diff --git a/.changeset/nasty-tigers-sip.md b/.changeset/nasty-tigers-sip.md new file mode 100644 index 0000000000..7325f6ccbb --- /dev/null +++ b/.changeset/nasty-tigers-sip.md @@ -0,0 +1,5 @@ +--- +'astro': minor +--- + +Add content parsing helpers to imported markdown files. This exposes both the raw markdown content when using rawContent() and the parsed Astro syntax when using compiledContent() diff --git a/packages/astro/env.d.ts b/packages/astro/env.d.ts index d9dfe741a7..4f40def886 100644 --- a/packages/astro/env.d.ts +++ b/packages/astro/env.d.ts @@ -20,6 +20,8 @@ declare module '*.md' { export const url: MD['url']; export const getHeaders: MD['getHeaders']; export const Content: MD['Content']; + export const rawContent: MD['rawContent']; + export const compiledContent: MD['compiledContent']; const load: MD['default']; export default load; diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 31f9e98f3c..dfd85e9b08 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -753,6 +753,10 @@ export interface MarkdownInstance> { file: string; url: string | undefined; Content: AstroComponentFactory; + /** raw Markdown file content, excluding frontmatter */ + rawContent(): string; + /** Markdown file compiled to valid Astro syntax. Queryable with most HTML parsing libraries */ + compiledContent(): Promise; getHeaders(): Promise; default: () => Promise<{ metadata: MarkdownMetadata; diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index b3c916bcdb..997648a6b4 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -82,28 +82,33 @@ export default function markdown({ config }: AstroPluginOptions): Plugin { const { fileId, fileUrl } = getFileInfo(id, config); const source = await fs.promises.readFile(fileId, 'utf8'); - const { data: frontmatter } = matter(source); + const { data: frontmatter, content: rawContent } = matter(source); return { code: ` // Static export const frontmatter = ${JSON.stringify(frontmatter)}; export const file = ${JSON.stringify(fileId)}; export const url = ${JSON.stringify(fileUrl)}; - + export function rawContent() { + return ${JSON.stringify(rawContent)}; + } + export async function compiledContent() { + return load().then((m) => m.compiledContent()); + } export function $$loadMetadata() { - return load().then((m) => m.$$metadata) + return load().then((m) => m.$$metadata); } // Deferred export default async function load() { return (await import(${JSON.stringify(fileId + MARKDOWN_CONTENT_FLAG)})); - }; + } export function Content(...args) { - return load().then((m) => m.default(...args)) + return load().then((m) => m.default(...args)); } Content.isAstroComponentFactory = true; export function getHeaders() { - return load().then((m) => m.metadata.headers) + return load().then((m) => m.metadata.headers); };`, map: null, }; @@ -171,6 +176,12 @@ ${setup}`.trim(); tsResult = `\nexport const metadata = ${JSON.stringify(metadata)}; export const frontmatter = ${JSON.stringify(content)}; +export function rawContent() { + return ${JSON.stringify(markdownContent)}; +} +export function compiledContent() { + return ${JSON.stringify(renderResult.metadata.html)}; +} ${tsResult}`; // Compile from `.ts` to `.js` diff --git a/packages/astro/test/astro-markdown.test.js b/packages/astro/test/astro-markdown.test.js index 5255223164..d98dabe18c 100644 --- a/packages/astro/test/astro-markdown.test.js +++ b/packages/astro/test/astro-markdown.test.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as cheerio from 'cheerio'; -import { loadFixture } from './test-utils.js'; +import { loadFixture, fixLineEndings } from './test-utils.js'; describe('Astro Markdown', () => { let fixture; @@ -233,4 +233,16 @@ describe('Astro Markdown', () => { expect($('#target > ol > li').children()).to.have.lengthOf(1); expect($('#target > ol > li > ol > li').text()).to.equal('nested hello'); }); + + it('Exposes raw markdown content', async () => { + const { raw } = JSON.parse(await fixture.readFile('/raw-content.json')); + + expect(fixLineEndings(raw)).to.equal(`\n## With components\n\n### Non-hydrated\n\n\n\n### Hydrated\n\n\n\n`); + }); + + it('Exposes HTML parser for raw markdown content', async () => { + const { compiled } = JSON.parse(await fixture.readFile('/raw-content.json')); + + expect(fixLineEndings(compiled)).to.equal(`

With components

\n

Non-hydrated

\n\n

Hydrated

\n\n`); + }) }); diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js new file mode 100644 index 0000000000..21be533e10 --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js @@ -0,0 +1,10 @@ +import { rawContent, compiledContent } from '../imported-md/with-components.md'; + +export async function get() { + return { + body: JSON.stringify({ + raw: rawContent(), + compiled: await compiledContent(), + }), + } +} diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index afac6e2507..01e924a78a 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -254,3 +254,7 @@ export async function cliServerLogSetup(flags = [], cmd = 'dev') { } export const isWindows = os.platform() === 'win32'; + +export function fixLineEndings(str) { + return str.replace(/\r\n/g, '\n'); +}