summaryrefslogtreecommitdiff
path: root/packages/integrations/mdx/src/astro-data-utils.ts
diff options
context:
space:
mode:
authorGravatar Ben Holmes <hey@bholmes.dev> 2022-08-05 18:55:38 -0500
committerGravatar GitHub <noreply@github.com> 2022-08-05 18:55:38 -0500
commit2675b8633c5d5c45b237ec87940d5eaf1bfb1b4b (patch)
tree171d8ff13cb81ed038ac52dc34ed2a313ae78903 /packages/integrations/mdx/src/astro-data-utils.ts
parent4678a3f358840db853db55b753b329ae592a589c (diff)
downloadastro-2675b8633c5d5c45b237ec87940d5eaf1bfb1b4b.tar.gz
astro-2675b8633c5d5c45b237ec87940d5eaf1bfb1b4b.tar.zst
astro-2675b8633c5d5c45b237ec87940d5eaf1bfb1b4b.zip
Frontmatter injection for MD and MDX (#4176)
* feat: inject vfile data as exports * feat: add vfile to renderMarkdown output * feat: add safe astroExports parser to utils * refactor: expose vite-plugin-utils on astro package * feat: handle astroExports in mdx * deps: vfile * chore: lockfile * test: astroExports in mdx * refactor: merge plugin exports into forntmatter * refactor: astroExports -> astro.frontmatter * refactor: md astroExports -> astro.frontmatter * feat: astro.frontmatter vite-plugin-markdown * chore: remove unused import * fix: inline safelyGetAstroData in MDX integration * chore: check that frontmatter export is valid export name * chore: error log naming * test: mdx remark frontmatter injection * fix: inconsistent shiki mod resolution * fix: add new frontmatter and heading props * test: remark vdata * fix: spread astro.data.frontmatter * test deps: mdast-util-to-string, reading-time * fix: astro-md test package name * test: md frontmatter injection * fix: layouts * deps: remove vite-plugin-utils export * fix: package lock * chore: remove dup import * chore: changeset * chore: add comment on safelyGetAstroData source * deps: move mdast-util-to-string + reading-time to test fixture * chore: move remark plugins to test fixture * fix: override plugin frontmatter with user frontmatter * test: md injected frontmatter overrides * test: frontmatter injection overrides mdx
Diffstat (limited to 'packages/integrations/mdx/src/astro-data-utils.ts')
-rw-r--r--packages/integrations/mdx/src/astro-data-utils.ts82
1 files changed, 82 insertions, 0 deletions
diff --git a/packages/integrations/mdx/src/astro-data-utils.ts b/packages/integrations/mdx/src/astro-data-utils.ts
new file mode 100644
index 000000000..bfbc74461
--- /dev/null
+++ b/packages/integrations/mdx/src/astro-data-utils.ts
@@ -0,0 +1,82 @@
+import { name as isValidIdentifierName } from 'estree-util-is-identifier-name';
+import type { VFile } from 'vfile';
+import type { MdxjsEsm } from 'mdast-util-mdx';
+import type { MarkdownAstroData } from 'astro';
+import type { Data } from 'vfile';
+import { jsToTreeNode } from './utils.js';
+
+export function remarkInitializeAstroData() {
+ return function (tree: any, vfile: VFile) {
+ if (!vfile.data.astro) {
+ vfile.data.astro = { frontmatter: {} };
+ }
+ };
+}
+
+export function rehypeApplyFrontmatterExport(
+ pageFrontmatter: Record<string, any>,
+ exportName = 'frontmatter'
+) {
+ return function (tree: any, vfile: VFile) {
+ if (!isValidIdentifierName(exportName)) {
+ throw new Error(
+ `[MDX] ${JSON.stringify(
+ exportName
+ )} is not a valid frontmatter export name! Make sure "frontmatterOptions.name" could be used as a JS export (i.e. "export const frontmatterName = ...")`
+ );
+ }
+ const { frontmatter: injectedFrontmatter } = safelyGetAstroData(vfile.data);
+ const frontmatter = { ...injectedFrontmatter, ...pageFrontmatter };
+ let exportNodes: MdxjsEsm[] = [];
+ if (!exportName) {
+ exportNodes = Object.entries(frontmatter).map(([k, v]) => {
+ if (!isValidIdentifierName(k)) {
+ throw new Error(
+ `[MDX] A remark or rehype plugin tried to inject ${JSON.stringify(
+ k
+ )} as a top-level export, which is not a valid export name.`
+ );
+ }
+ return jsToTreeNode(`export const ${k} = ${JSON.stringify(v)};`);
+ });
+ } else {
+ exportNodes = [jsToTreeNode(`export const ${exportName} = ${JSON.stringify(frontmatter)};`)];
+ }
+ tree.children = exportNodes.concat(tree.children);
+ };
+}
+
+/**
+ * Copied from markdown utils
+ * @see "vite-plugin-utils"
+ */
+function isValidAstroData(obj: unknown): obj is MarkdownAstroData {
+ if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) {
+ const { frontmatter } = obj as any;
+ try {
+ // ensure frontmatter is JSON-serializable
+ JSON.stringify(frontmatter);
+ } catch {
+ return false;
+ }
+ return typeof frontmatter === 'object' && frontmatter !== null;
+ }
+ return false;
+}
+
+/**
+ * Copied from markdown utils
+ * @see "vite-plugin-utils"
+ */
+export function safelyGetAstroData(vfileData: Data): MarkdownAstroData {
+ const { astro } = vfileData;
+
+ if (!astro) return { frontmatter: {} };
+ if (!isValidAstroData(astro)) {
+ throw Error(
+ `[MDX] A remark or rehype plugin tried to add invalid frontmatter. Ensure "astro.frontmatter" is a JSON object!`
+ );
+ }
+
+ return astro;
+}