aboutsummaryrefslogtreecommitdiff
path: root/packages/integrations/markdoc/src/load-config.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/markdoc/src/load-config.ts')
-rw-r--r--packages/integrations/markdoc/src/load-config.ts118
1 files changed, 118 insertions, 0 deletions
diff --git a/packages/integrations/markdoc/src/load-config.ts b/packages/integrations/markdoc/src/load-config.ts
new file mode 100644
index 000000000..ce181e604
--- /dev/null
+++ b/packages/integrations/markdoc/src/load-config.ts
@@ -0,0 +1,118 @@
+import * as fs from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import type { AstroConfig } from 'astro';
+import { build as esbuild } from 'esbuild';
+import type { AstroMarkdocConfig } from './config.js';
+import { MarkdocError } from './utils.js';
+
+export const SUPPORTED_MARKDOC_CONFIG_FILES = [
+ 'markdoc.config.js',
+ 'markdoc.config.mjs',
+ 'markdoc.config.mts',
+ 'markdoc.config.ts',
+];
+
+export type MarkdocConfigResult = {
+ config: AstroMarkdocConfig;
+ fileUrl: URL;
+};
+
+export async function loadMarkdocConfig(
+ astroConfig: Pick<AstroConfig, 'root'>,
+): Promise<MarkdocConfigResult | undefined> {
+ let markdocConfigUrl: URL | undefined;
+ for (const filename of SUPPORTED_MARKDOC_CONFIG_FILES) {
+ const filePath = new URL(filename, astroConfig.root);
+ if (!fs.existsSync(filePath)) continue;
+
+ markdocConfigUrl = filePath;
+ break;
+ }
+ if (!markdocConfigUrl) return;
+
+ const { code } = await bundleConfigFile({
+ markdocConfigUrl,
+ astroConfig,
+ });
+ const config: AstroMarkdocConfig = await loadConfigFromBundledFile(astroConfig.root, code);
+
+ return {
+ config,
+ fileUrl: markdocConfigUrl,
+ };
+}
+
+/**
+ * Bundle config file to support `.ts` files.
+ * Simplified fork from Vite's `bundleConfigFile` function:
+ * @see https://github.com/vitejs/vite/blob/main/packages/vite/src/node/config.ts#L961
+ */
+async function bundleConfigFile({
+ markdocConfigUrl,
+ astroConfig,
+}: {
+ markdocConfigUrl: URL;
+ astroConfig: Pick<AstroConfig, 'root'>;
+}): Promise<{ code: string; dependencies: string[] }> {
+ let markdocError: MarkdocError | undefined;
+
+ const result = await esbuild({
+ absWorkingDir: fileURLToPath(astroConfig.root),
+ entryPoints: [fileURLToPath(markdocConfigUrl)],
+ outfile: 'out.js',
+ write: false,
+ target: ['node16'],
+ platform: 'node',
+ packages: 'external',
+ bundle: true,
+ format: 'esm',
+ sourcemap: 'inline',
+ metafile: true,
+ plugins: [
+ {
+ name: 'stub-astro-imports',
+ setup(build) {
+ build.onResolve({ filter: /.*\.astro$/ }, () => {
+ // Avoid throwing within esbuild.
+ // This swallows the `hint` and blows up the stacktrace.
+ markdocError = new MarkdocError({
+ message: '`.astro` files are no longer supported in the Markdoc config.',
+ hint: 'Use the `component()` utility to specify a component path instead. See https://docs.astro.build/en/guides/integrations-guide/markdoc/',
+ });
+ return {
+ // Stub with an unused default export.
+ path: 'data:text/javascript,export default true',
+ external: true,
+ };
+ });
+ },
+ },
+ ],
+ });
+ if (markdocError) throw markdocError;
+ const { text } = result.outputFiles[0];
+ return {
+ code: text,
+ dependencies: result.metafile ? Object.keys(result.metafile.inputs) : [],
+ };
+}
+
+/**
+ * Forked from Vite config loader, replacing CJS-based path concat
+ * with ESM only
+ * @see https://github.com/vitejs/vite/blob/main/packages/vite/src/node/config.ts#L1074
+ */
+async function loadConfigFromBundledFile(root: URL, code: string): Promise<AstroMarkdocConfig> {
+ // Write it to disk, load it with native Node ESM, then delete the file.
+ const tmpFileUrl = new URL(`markdoc.config.timestamp-${Date.now()}.mjs`, root);
+ fs.writeFileSync(tmpFileUrl, code);
+ try {
+ return (await import(tmpFileUrl.pathname)).default;
+ } finally {
+ try {
+ fs.unlinkSync(tmpFileUrl);
+ } catch {
+ // already removed if this function is called twice simultaneously
+ }
+ }
+}