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:
parent
ffe5cf0312
commit
47d1a8d59c
7 changed files with 55 additions and 7 deletions
5
.changeset/nasty-tigers-sip.md
Normal file
5
.changeset/nasty-tigers-sip.md
Normal 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()
|
2
packages/astro/env.d.ts
vendored
2
packages/astro/env.d.ts
vendored
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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 />`);
|
||||
})
|
||||
});
|
||||
|
|
10
packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js
vendored
Normal file
10
packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js
vendored
Normal 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(),
|
||||
}),
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue