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 url: MD['url'];
|
||||||
export const getHeaders: MD['getHeaders'];
|
export const getHeaders: MD['getHeaders'];
|
||||||
export const Content: MD['Content'];
|
export const Content: MD['Content'];
|
||||||
|
export const rawContent: MD['rawContent'];
|
||||||
|
export const compiledContent: MD['compiledContent'];
|
||||||
|
|
||||||
const load: MD['default'];
|
const load: MD['default'];
|
||||||
export default load;
|
export default load;
|
||||||
|
|
|
@ -753,6 +753,10 @@ export interface MarkdownInstance<T extends Record<string, any>> {
|
||||||
file: string;
|
file: string;
|
||||||
url: string | undefined;
|
url: string | undefined;
|
||||||
Content: AstroComponentFactory;
|
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[]>;
|
getHeaders(): Promise<MarkdownHeader[]>;
|
||||||
default: () => Promise<{
|
default: () => Promise<{
|
||||||
metadata: MarkdownMetadata;
|
metadata: MarkdownMetadata;
|
||||||
|
|
|
@ -82,28 +82,33 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
||||||
const { fileId, fileUrl } = getFileInfo(id, config);
|
const { fileId, fileUrl } = getFileInfo(id, config);
|
||||||
|
|
||||||
const source = await fs.promises.readFile(fileId, 'utf8');
|
const source = await fs.promises.readFile(fileId, 'utf8');
|
||||||
const { data: frontmatter } = matter(source);
|
const { data: frontmatter, content: rawContent } = matter(source);
|
||||||
return {
|
return {
|
||||||
code: `
|
code: `
|
||||||
// Static
|
// Static
|
||||||
export const frontmatter = ${JSON.stringify(frontmatter)};
|
export const frontmatter = ${JSON.stringify(frontmatter)};
|
||||||
export const file = ${JSON.stringify(fileId)};
|
export const file = ${JSON.stringify(fileId)};
|
||||||
export const url = ${JSON.stringify(fileUrl)};
|
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() {
|
export function $$loadMetadata() {
|
||||||
return load().then((m) => m.$$metadata)
|
return load().then((m) => m.$$metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deferred
|
// Deferred
|
||||||
export default async function load() {
|
export default async function load() {
|
||||||
return (await import(${JSON.stringify(fileId + MARKDOWN_CONTENT_FLAG)}));
|
return (await import(${JSON.stringify(fileId + MARKDOWN_CONTENT_FLAG)}));
|
||||||
};
|
}
|
||||||
export function Content(...args) {
|
export function Content(...args) {
|
||||||
return load().then((m) => m.default(...args))
|
return load().then((m) => m.default(...args));
|
||||||
}
|
}
|
||||||
Content.isAstroComponentFactory = true;
|
Content.isAstroComponentFactory = true;
|
||||||
export function getHeaders() {
|
export function getHeaders() {
|
||||||
return load().then((m) => m.metadata.headers)
|
return load().then((m) => m.metadata.headers);
|
||||||
};`,
|
};`,
|
||||||
map: null,
|
map: null,
|
||||||
};
|
};
|
||||||
|
@ -171,6 +176,12 @@ ${setup}`.trim();
|
||||||
|
|
||||||
tsResult = `\nexport const metadata = ${JSON.stringify(metadata)};
|
tsResult = `\nexport const metadata = ${JSON.stringify(metadata)};
|
||||||
export const frontmatter = ${JSON.stringify(content)};
|
export const frontmatter = ${JSON.stringify(content)};
|
||||||
|
export function rawContent() {
|
||||||
|
return ${JSON.stringify(markdownContent)};
|
||||||
|
}
|
||||||
|
export function compiledContent() {
|
||||||
|
return ${JSON.stringify(renderResult.metadata.html)};
|
||||||
|
}
|
||||||
${tsResult}`;
|
${tsResult}`;
|
||||||
|
|
||||||
// Compile from `.ts` to `.js`
|
// Compile from `.ts` to `.js`
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
import { loadFixture } from './test-utils.js';
|
import { loadFixture, fixLineEndings } from './test-utils.js';
|
||||||
|
|
||||||
describe('Astro Markdown', () => {
|
describe('Astro Markdown', () => {
|
||||||
let fixture;
|
let fixture;
|
||||||
|
@ -233,4 +233,16 @@ describe('Astro Markdown', () => {
|
||||||
expect($('#target > ol > li').children()).to.have.lengthOf(1);
|
expect($('#target > ol > li').children()).to.have.lengthOf(1);
|
||||||
expect($('#target > ol > li > ol > li').text()).to.equal('nested hello');
|
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 const isWindows = os.platform() === 'win32';
|
||||||
|
|
||||||
|
export function fixLineEndings(str) {
|
||||||
|
return str.replace(/\r\n/g, '\n');
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue