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
This commit is contained in:
Ben Holmes 2022-05-27 16:56:08 -04:00 committed by GitHub
parent ffe5cf0312
commit 47d1a8d59c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 7 deletions

View file

@ -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()

View file

@ -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;

View file

@ -753,6 +753,10 @@ export interface MarkdownInstance<T extends Record<string, any>> {
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<string>;
getHeaders(): Promise<MarkdownHeader[]>;
default: () => Promise<{
metadata: MarkdownMetadata;

View file

@ -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`

View file

@ -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<Hello name="Astro Naut" />\n\n### Hydrated\n\n<Counter client:load />\n<SvelteButton client:load />\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(`<h2 id="with-components">With components</h2>\n<h3 id="non-hydrated">Non-hydrated</h3>\n<Hello name="Astro Naut" />\n<h3 id="hydrated">Hydrated</h3>\n<Counter client:load />\n<SvelteButton client:load />`);
})
});

View file

@ -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(),
}),
}
}

View file

@ -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');
}