summaryrefslogtreecommitdiff
path: root/packages/markdown/remark/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/markdown/remark/src')
-rw-r--r--packages/markdown/remark/src/frontmatter-injection.ts34
-rw-r--r--packages/markdown/remark/src/frontmatter.ts9
-rw-r--r--packages/markdown/remark/src/index.ts33
-rw-r--r--packages/markdown/remark/src/internal.ts1
-rw-r--r--packages/markdown/remark/src/rehype-collect-headings.ts28
-rw-r--r--packages/markdown/remark/src/rehype-images.ts6
-rw-r--r--packages/markdown/remark/src/remark-collect-images.ts7
-rw-r--r--packages/markdown/remark/src/types.ts24
8 files changed, 55 insertions, 87 deletions
diff --git a/packages/markdown/remark/src/frontmatter-injection.ts b/packages/markdown/remark/src/frontmatter-injection.ts
deleted file mode 100644
index 91b98ebcb..000000000
--- a/packages/markdown/remark/src/frontmatter-injection.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import type { VFileData as Data, VFile } from 'vfile';
-import type { MarkdownAstroData } from './types.js';
-
-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;
-}
-
-export class InvalidAstroDataError extends TypeError {}
-
-export function safelyGetAstroData(vfileData: Data): MarkdownAstroData | InvalidAstroDataError {
- const { astro } = vfileData;
-
- if (!astro || !isValidAstroData(astro)) {
- return new InvalidAstroDataError();
- }
-
- return astro;
-}
-
-export function setVfileFrontmatter(vfile: VFile, frontmatter: Record<string, any>) {
- vfile.data ??= {};
- vfile.data.astro ??= {};
- (vfile.data.astro as any).frontmatter = frontmatter;
-}
diff --git a/packages/markdown/remark/src/frontmatter.ts b/packages/markdown/remark/src/frontmatter.ts
new file mode 100644
index 000000000..d8828a6fe
--- /dev/null
+++ b/packages/markdown/remark/src/frontmatter.ts
@@ -0,0 +1,9 @@
+export function isFrontmatterValid(frontmatter: Record<string, any>) {
+ try {
+ // ensure frontmatter is JSON-serializable
+ JSON.stringify(frontmatter);
+ } catch {
+ return false;
+ }
+ return typeof frontmatter === 'object' && frontmatter !== null;
+}
diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts
index 0ed2aaca1..ad52dc16f 100644
--- a/packages/markdown/remark/src/index.ts
+++ b/packages/markdown/remark/src/index.ts
@@ -1,10 +1,5 @@
-import type { AstroMarkdownOptions, MarkdownProcessor, MarkdownVFile } from './types.js';
+import type { AstroMarkdownOptions, MarkdownProcessor } from './types.js';
-import {
- InvalidAstroDataError,
- safelyGetAstroData,
- setVfileFrontmatter,
-} from './frontmatter-injection.js';
import { loadPlugins } from './load-plugins.js';
import { rehypeHeadingIds } from './rehype-collect-headings.js';
import { rehypePrism } from './rehype-prism.js';
@@ -21,11 +16,11 @@ import { unified } from 'unified';
import { VFile } from 'vfile';
import { rehypeImages } from './rehype-images.js';
-export { InvalidAstroDataError, setVfileFrontmatter } from './frontmatter-injection.js';
export { rehypeHeadingIds } from './rehype-collect-headings.js';
export { remarkCollectImages } from './remark-collect-images.js';
export { rehypePrism } from './rehype-prism.js';
export { rehypeShiki } from './rehype-shiki.js';
+export { isFrontmatterValid } from './frontmatter.js';
export {
createShikiHighlighter,
type ShikiHighlighter,
@@ -128,10 +123,17 @@ export async function createMarkdownProcessor(
return {
async render(content, renderOpts) {
- const vfile = new VFile({ value: content, path: renderOpts?.fileURL });
- setVfileFrontmatter(vfile, renderOpts?.frontmatter ?? {});
+ const vfile = new VFile({
+ value: content,
+ path: renderOpts?.fileURL,
+ data: {
+ astro: {
+ frontmatter: renderOpts?.frontmatter ?? {},
+ },
+ },
+ });
- const result: MarkdownVFile = await parser.process(vfile).catch((err) => {
+ const result = await parser.process(vfile).catch((err) => {
// Ensure that the error message contains the input filename
// to make it easier for the user to fix the issue
err = prefixError(err, `Failed to parse Markdown file "${vfile.path}"`);
@@ -140,17 +142,12 @@ export async function createMarkdownProcessor(
throw err;
});
- const astroData = safelyGetAstroData(result.data);
- if (astroData instanceof InvalidAstroDataError) {
- throw astroData;
- }
-
return {
code: String(result.value),
metadata: {
- headings: result.data.__astroHeadings ?? [],
- imagePaths: result.data.imagePaths ?? new Set(),
- frontmatter: astroData.frontmatter ?? {},
+ headings: result.data.astro?.headings ?? [],
+ imagePaths: result.data.astro?.imagePaths ?? [],
+ frontmatter: result.data.astro?.frontmatter ?? {},
},
};
},
diff --git a/packages/markdown/remark/src/internal.ts b/packages/markdown/remark/src/internal.ts
deleted file mode 100644
index 6201ef62f..000000000
--- a/packages/markdown/remark/src/internal.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { InvalidAstroDataError, safelyGetAstroData } from './frontmatter-injection.js';
diff --git a/packages/markdown/remark/src/rehype-collect-headings.ts b/packages/markdown/remark/src/rehype-collect-headings.ts
index 05afae1ba..ab2113f49 100644
--- a/packages/markdown/remark/src/rehype-collect-headings.ts
+++ b/packages/markdown/remark/src/rehype-collect-headings.ts
@@ -3,19 +3,18 @@ import Slugger from 'github-slugger';
import type { MdxTextExpression } from 'mdast-util-mdx-expression';
import type { Node } from 'unist';
import { visit } from 'unist-util-visit';
-
-import { InvalidAstroDataError, safelyGetAstroData } from './frontmatter-injection.js';
-import type { MarkdownAstroData, MarkdownHeading, MarkdownVFile, RehypePlugin } from './types.js';
+import type { VFile } from 'vfile';
+import type { MarkdownHeading, RehypePlugin } from './types.js';
const rawNodeTypes = new Set(['text', 'raw', 'mdxTextExpression']);
const codeTagNames = new Set(['code', 'pre']);
export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
- return function (tree, file: MarkdownVFile) {
+ return function (tree, file) {
const headings: MarkdownHeading[] = [];
+ const frontmatter = file.data.astro?.frontmatter;
const slugger = new Slugger();
const isMDX = isMDXFile(file);
- const astroData = safelyGetAstroData(file.data);
visit(tree, (node) => {
if (node.type !== 'element') return;
const { tagName } = node;
@@ -37,10 +36,13 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
if (rawNodeTypes.has(child.type)) {
if (isMDX || codeTagNames.has(parent.tagName)) {
let value = child.value;
- if (isMdxTextExpression(child) && !(astroData instanceof InvalidAstroDataError)) {
+ if (isMdxTextExpression(child) && frontmatter) {
const frontmatterPath = getMdxFrontmatterVariablePath(child);
if (Array.isArray(frontmatterPath) && frontmatterPath.length > 0) {
- const frontmatterValue = getMdxFrontmatterVariableValue(astroData, frontmatterPath);
+ const frontmatterValue = getMdxFrontmatterVariableValue(
+ frontmatter,
+ frontmatterPath,
+ );
if (typeof frontmatterValue === 'string') {
value = frontmatterValue;
}
@@ -65,11 +67,12 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
headings.push({ depth, slug: node.properties.id, text });
});
- file.data.__astroHeadings = headings;
+ file.data.astro ??= {};
+ file.data.astro.headings = headings;
};
}
-function isMDXFile(file: MarkdownVFile) {
+function isMDXFile(file: VFile) {
return Boolean(file.history[0]?.endsWith('.mdx'));
}
@@ -109,8 +112,8 @@ function getMdxFrontmatterVariablePath(node: MdxTextExpression): string[] | Erro
return expressionPath.reverse();
}
-function getMdxFrontmatterVariableValue(astroData: MarkdownAstroData, path: string[]) {
- let value: MdxFrontmatterVariableValue = astroData.frontmatter;
+function getMdxFrontmatterVariableValue(frontmatter: Record<string, any>, path: string[]) {
+ let value = frontmatter;
for (const key of path) {
if (!value[key]) return undefined;
@@ -124,6 +127,3 @@ function getMdxFrontmatterVariableValue(astroData: MarkdownAstroData, path: stri
function isMdxTextExpression(node: Node): node is MdxTextExpression {
return node.type === 'mdxTextExpression';
}
-
-type MdxFrontmatterVariableValue =
- MarkdownAstroData['frontmatter'][keyof MarkdownAstroData['frontmatter']];
diff --git a/packages/markdown/remark/src/rehype-images.ts b/packages/markdown/remark/src/rehype-images.ts
index 01e5aa6d6..11d33df9c 100644
--- a/packages/markdown/remark/src/rehype-images.ts
+++ b/packages/markdown/remark/src/rehype-images.ts
@@ -1,9 +1,9 @@
import { visit } from 'unist-util-visit';
-import type { MarkdownVFile } from './types.js';
+import type { VFile } from 'vfile';
export function rehypeImages() {
return () =>
- function (tree: any, file: MarkdownVFile) {
+ function (tree: any, file: VFile) {
const imageOccurrenceMap = new Map();
visit(tree, (node) => {
@@ -13,7 +13,7 @@ export function rehypeImages() {
if (node.properties?.src) {
node.properties.src = decodeURI(node.properties.src);
- if (file.data.imagePaths?.has(node.properties.src)) {
+ if (file.data.astro?.imagePaths?.includes(node.properties.src)) {
const { ...props } = node.properties;
// Initialize or increment occurrence count for this image
diff --git a/packages/markdown/remark/src/remark-collect-images.ts b/packages/markdown/remark/src/remark-collect-images.ts
index 22774d5f1..f09f1c580 100644
--- a/packages/markdown/remark/src/remark-collect-images.ts
+++ b/packages/markdown/remark/src/remark-collect-images.ts
@@ -1,10 +1,10 @@
import type { Image, ImageReference } from 'mdast';
import { definitions } from 'mdast-util-definitions';
import { visit } from 'unist-util-visit';
-import type { MarkdownVFile } from './types.js';
+import type { VFile } from 'vfile';
export function remarkCollectImages() {
- return function (tree: any, vfile: MarkdownVFile) {
+ return function (tree: any, vfile: VFile) {
if (typeof vfile?.path !== 'string') return;
const definition = definitions(tree);
@@ -22,7 +22,8 @@ export function remarkCollectImages() {
}
});
- vfile.data.imagePaths = imagePaths;
+ vfile.data.astro ??= {};
+ vfile.data.astro.imagePaths = Array.from(imagePaths);
};
}
diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts
index 4a1263e50..6134bdf8a 100644
--- a/packages/markdown/remark/src/types.ts
+++ b/packages/markdown/remark/src/types.ts
@@ -3,14 +3,19 @@ import type * as mdast from 'mdast';
import type { Options as RemarkRehypeOptions } from 'remark-rehype';
import type { BuiltinTheme } from 'shiki';
import type * as unified from 'unified';
-import type { DataMap, VFile } from 'vfile';
import type { CreateShikiHighlighterOptions, ShikiHighlighterHighlightOptions } from './shiki.js';
export type { Node } from 'unist';
-export type MarkdownAstroData = {
- frontmatter: Record<string, any>;
-};
+declare module 'vfile' {
+ interface DataMap {
+ astro: {
+ headings?: MarkdownHeading[];
+ imagePaths?: string[];
+ frontmatter?: Record<string, any>;
+ };
+ }
+}
export type RemarkPlugin<PluginParameters extends any[] = any[]> = unified.Plugin<
PluginParameters,
@@ -62,7 +67,7 @@ export interface MarkdownProcessorRenderResult {
code: string;
metadata: {
headings: MarkdownHeading[];
- imagePaths: Set<string>;
+ imagePaths: string[];
frontmatter: Record<string, any>;
};
}
@@ -72,12 +77,3 @@ export interface MarkdownHeading {
slug: string;
text: string;
}
-
-// TODO: Remove `MarkdownVFile` and move all additional properties to `DataMap` instead
-export interface MarkdownVFile extends VFile {
- data: Record<string, unknown> &
- Partial<DataMap> & {
- __astroHeadings?: MarkdownHeading[];
- imagePaths?: Set<string>;
- };
-}