From 47d1a8d59cb3d88655de3d7658026e84843cb043 Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Fri, 27 May 2022 16:56:08 -0400 Subject: [PATCH] Feat: markdown `content.raw()` and `content.compiled()` helpers (#3452) * feat: add rawContent obj with html helper * refactor: change toString to function call * test: rawContent helpers * chore: update MarkdownInstance type * refactor: parseHtml -> html * chore: changeset * fix: remove needless async heading on content version * fix: fixLineEndings helper on unit tests * refactor: change api to raw and compiled * chore: add new type to env.d.ts * docs: JSdocs for raw and compiled * refactor: change API AGAIN to rawContent, compiledContent * chore: update changeset --- .changeset/nasty-tigers-sip.md | 5 ++++ packages/astro/env.d.ts | 2 ++ packages/astro/src/@types/astro.ts | 4 ++++ .../astro/src/vite-plugin-markdown/index.ts | 23 ++++++++++++++----- packages/astro/test/astro-markdown.test.js | 14 ++++++++++- .../src/pages/raw-content.json.js | 10 ++++++++ packages/astro/test/test-utils.js | 4 ++++ 7 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 .changeset/nasty-tigers-sip.md create mode 100644 packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js diff --git a/.changeset/nasty-tigers-sip.md b/.changeset/nasty-tigers-sip.md new file mode 100644 index 000000000..7325f6ccb --- /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 d9dfe741a..4f40def88 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 31f9e98f3..dfd85e9b0 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 b3c916bcd..997648a6b 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 525522316..d98dabe18 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 000000000..21be533e1 --- /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 afac6e250..01e924a78 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'); +}