182 lines
6.2 KiB
JavaScript
182 lines
6.2 KiB
JavaScript
import { renderMarkdown } from "@astrojs/markdown-remark";
|
|
import {
|
|
InvalidAstroDataError,
|
|
safelyGetAstroData
|
|
} from "@astrojs/markdown-remark/dist/internal.js";
|
|
import matter from "gray-matter";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { normalizePath } from "vite";
|
|
import { AstroError, AstroErrorData, MarkdownError } from "../core/errors/index.js";
|
|
import { warn } from "../core/logger/core.js";
|
|
import { isMarkdownFile, rootRelativePath } from "../core/util.js";
|
|
import { escapeViteEnvReferences, getFileInfo } from "../vite-plugin-utils/index.js";
|
|
function safeMatter(source, id) {
|
|
try {
|
|
return matter(source);
|
|
} catch (err) {
|
|
const markdownError = new MarkdownError({
|
|
message: err.message,
|
|
stack: err.stack,
|
|
location: {
|
|
file: id
|
|
}
|
|
});
|
|
if (err.name === "YAMLException") {
|
|
markdownError.setLocation({
|
|
file: id,
|
|
line: err.mark.line,
|
|
column: err.mark.column
|
|
});
|
|
markdownError.setMessage(err.reason);
|
|
}
|
|
throw markdownError;
|
|
}
|
|
}
|
|
const astroJsxRuntimeModulePath = normalizePath(
|
|
fileURLToPath(new URL("../jsx-runtime/index.js", import.meta.url))
|
|
);
|
|
const astroServerRuntimeModulePath = normalizePath(
|
|
fileURLToPath(new URL("../runtime/server/index.js", import.meta.url))
|
|
);
|
|
const astroErrorModulePath = normalizePath(
|
|
fileURLToPath(new URL("../core/errors/index.js", import.meta.url))
|
|
);
|
|
function markdown({ settings, logging }) {
|
|
return {
|
|
enforce: "pre",
|
|
name: "astro:markdown",
|
|
// Why not the "transform" hook instead of "load" + readFile?
|
|
// A: Vite transforms all "import.meta.env" references to their values before
|
|
// passing to the transform hook. This lets us get the truly raw value
|
|
// to escape "import.meta.env" ourselves.
|
|
async load(id) {
|
|
var _a;
|
|
if (isMarkdownFile(id)) {
|
|
const { fileId, fileUrl } = getFileInfo(id, settings.config);
|
|
const rawFile = await fs.promises.readFile(fileId, "utf-8");
|
|
const raw = safeMatter(rawFile, id);
|
|
const renderResult = await renderMarkdown(raw.content, {
|
|
...settings.config.markdown,
|
|
fileURL: new URL(`file://${fileId}`),
|
|
frontmatter: raw.data,
|
|
experimentalAssets: settings.config.experimental.assets
|
|
});
|
|
let html = renderResult.code;
|
|
const { headings } = renderResult.metadata;
|
|
let imagePaths = [];
|
|
if (settings.config.experimental.assets && renderResult.vfile.data.imagePaths) {
|
|
for (let imagePath of renderResult.vfile.data.imagePaths.values()) {
|
|
imagePaths.push({
|
|
raw: imagePath,
|
|
resolved: ((_a = await this.resolve(imagePath, id)) == null ? void 0 : _a.id) ?? path.join(path.dirname(id), imagePath)
|
|
});
|
|
}
|
|
}
|
|
const astroData = safelyGetAstroData(renderResult.vfile.data);
|
|
if (astroData instanceof InvalidAstroDataError) {
|
|
throw new AstroError(AstroErrorData.InvalidFrontmatterInjectionError);
|
|
}
|
|
const { frontmatter } = astroData;
|
|
const { layout } = frontmatter;
|
|
if (frontmatter.setup) {
|
|
warn(
|
|
logging,
|
|
"markdown",
|
|
`[${id}] Astro now supports MDX! Support for components in ".md" (or alternative extensions like ".markdown") files using the "setup" frontmatter is no longer enabled by default. Migrate this file to MDX.`
|
|
);
|
|
}
|
|
const code = escapeViteEnvReferences(`
|
|
import { Fragment, jsx as h } from ${JSON.stringify(astroJsxRuntimeModulePath)};
|
|
import { spreadAttributes } from ${JSON.stringify(astroServerRuntimeModulePath)};
|
|
import { AstroError, AstroErrorData } from ${JSON.stringify(astroErrorModulePath)};
|
|
|
|
${layout ? `import Layout from ${JSON.stringify(layout)};` : ""}
|
|
${settings.config.experimental.assets ? 'import { getImage } from "astro:assets";' : ""}
|
|
|
|
export const images = {
|
|
${imagePaths.map(
|
|
(entry) => `'${entry.raw}': await getImageSafely((await import("${entry.raw}")).default, "${entry.raw}", "${rootRelativePath(settings.config.root, entry.resolved)}")`
|
|
)}
|
|
}
|
|
|
|
async function getImageSafely(imageSrc, imagePath, resolvedImagePath) {
|
|
if (!imageSrc) {
|
|
throw new AstroError({
|
|
...AstroErrorData.MarkdownImageNotFound,
|
|
message: AstroErrorData.MarkdownImageNotFound.message(
|
|
imagePath,
|
|
resolvedImagePath
|
|
),
|
|
location: { file: "${id}" },
|
|
});
|
|
}
|
|
|
|
return await getImage({src: imageSrc})
|
|
}
|
|
|
|
function updateImageReferences(html) {
|
|
return html.replaceAll(
|
|
/__ASTRO_IMAGE_="(.+)"/gm,
|
|
(full, imagePath) => spreadAttributes({src: images[imagePath].src, ...images[imagePath].attributes})
|
|
);
|
|
}
|
|
|
|
const html = updateImageReferences(${JSON.stringify(html)});
|
|
|
|
export const frontmatter = ${JSON.stringify(frontmatter)};
|
|
export const file = ${JSON.stringify(fileId)};
|
|
export const url = ${JSON.stringify(fileUrl)};
|
|
export function rawContent() {
|
|
return ${JSON.stringify(raw.content)};
|
|
}
|
|
export function compiledContent() {
|
|
return html;
|
|
}
|
|
export function getHeadings() {
|
|
return ${JSON.stringify(headings)};
|
|
}
|
|
export async function Content() {
|
|
const { layout, ...content } = frontmatter;
|
|
content.file = file;
|
|
content.url = url;
|
|
const contentFragment = h(Fragment, { 'set:html': html });
|
|
return ${layout ? `h(Layout, {
|
|
file,
|
|
url,
|
|
content,
|
|
frontmatter: content,
|
|
headings: getHeadings(),
|
|
rawContent,
|
|
compiledContent,
|
|
'server:root': true,
|
|
children: contentFragment
|
|
})` : `contentFragment`};
|
|
}
|
|
Content[Symbol.for('astro.needsHeadRendering')] = ${layout ? "false" : "true"};
|
|
export default Content;
|
|
`);
|
|
return {
|
|
code,
|
|
meta: {
|
|
astro: {
|
|
hydratedComponents: [],
|
|
clientOnlyComponents: [],
|
|
scripts: [],
|
|
propagation: "none",
|
|
containsHead: false,
|
|
pageOptions: {}
|
|
},
|
|
vite: {
|
|
lang: "ts"
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
};
|
|
}
|
|
export {
|
|
markdown as default
|
|
};
|