fumadocs-mdx
Version:
The built-in source for Fumadocs
264 lines (261 loc) • 9.79 kB
JavaScript
import { t as fumaMatter } from "./fuma-matter-CHgJa_-B.js";
import { n as ident, r as slash, t as createCodegen } from "./codegen-DleOVLNr.js";
import fs from "node:fs/promises";
import path from "node:path";
import { glob } from "tinyglobby";
import path$1 from "path";
import { createHash } from "crypto";
//#region src/utils/fs-cache.ts
const map = /* @__PURE__ */ new Map();
function createFSCache() {
return {
read(file) {
const fullPath = toFullPath(file);
const cached = map.get(fullPath);
if (cached) return cached;
const read = fs.readFile(fullPath).then((s) => s.toString());
map.set(fullPath, read);
return read;
},
delete(file) {
map.delete(toFullPath(file));
}
};
}
/**
* make file paths relative to cwd
*/
function toFullPath(file) {
if (path.isAbsolute(file)) return path.relative(process.cwd(), file);
return file;
}
//#endregion
//#region src/plugins/index-file.ts
const indexFileCache = createFSCache();
function indexFile(options = {}) {
const { target = "default", addJsExtension, browser = true, dynamic = true } = options;
let dynamicCollections;
function isDynamic(collection) {
return collection.type === "docs" && collection.docs.dynamic || collection.type === "doc" && collection.dynamic;
}
function generateConfigs(core) {
const serverOptions = {};
const typeConfigs = ["import(\"fumadocs-mdx/runtime/types\").InternalTypeConfig"];
const ctx = core.getPluginContext();
for (const plugin of core.getPlugins()) {
const indexFilePlugin = plugin["index-file"];
if (!indexFilePlugin) continue;
indexFilePlugin.serverOptions?.call(ctx, serverOptions);
const config = indexFilePlugin.generateTypeConfig?.call(ctx);
if (config) typeConfigs.push(config);
}
return {
serverOptions,
tc: typeConfigs.join(" & ")
};
}
return {
name: "index-file",
config() {
dynamicCollections = this.core.getCollections().filter(isDynamic);
},
configureServer(server) {
if (!server.watcher) return;
server.watcher.on("all", async (event, file) => {
indexFileCache.delete(file);
if (dynamicCollections.length === 0) {
if (target === "vite") return;
if (target === "default" && event === "change") return;
}
const updatedCollection = this.core.getCollections().find((collection) => collection.hasFile(file));
if (!updatedCollection) return;
if (!isDynamic(updatedCollection)) {
if (target === "vite") return;
if (target === "default" && event === "change") return;
}
await this.core.emit({
filterPlugin: (plugin) => plugin.name === "index-file",
filterWorkspace: () => false,
write: true
});
});
},
async emit() {
const globCache = /* @__PURE__ */ new Map();
const { workspace, outDir } = this.core.getOptions();
const { serverOptions, tc } = generateConfigs(this.core);
const toEmitEntry = async (path$2, content) => {
const codegen = createCodegen({
target,
outDir,
jsExtension: addJsExtension,
globCache
});
await content({
core: this.core,
codegen,
serverOptions,
tc,
workspace: workspace?.name
});
return {
path: path$2,
content: codegen.toString()
};
};
const out = [toEmitEntry("server.ts", generateServerIndexFile)];
if (dynamic) out.push(toEmitEntry("dynamic.ts", generateDynamicIndexFile));
if (browser) out.push(toEmitEntry("browser.ts", generateBrowserIndexFile));
return await Promise.all(out);
}
};
}
async function generateServerIndexFile(ctx) {
const { core, codegen, serverOptions, tc } = ctx;
codegen.lines.push(`import { server } from 'fumadocs-mdx/runtime/server';`, `import type * as Config from '${codegen.formatImportPath(core.getOptions().configPath)}';`, "", `const create = server<typeof Config, ${tc}>(${JSON.stringify(serverOptions)});`);
async function generateCollectionObject(collection) {
const base = getBase(collection);
switch (collection.type) {
case "docs": {
if (collection.docs.dynamic) return;
if (collection.docs.async) {
const [metaGlob$1, headGlob, bodyGlob] = await Promise.all([
generateMetaCollectionGlob(ctx, collection.meta, true),
generateDocCollectionFrontmatterGlob(ctx, collection.docs, true),
generateDocCollectionGlob(ctx, collection.docs)
]);
return `await create.docsLazy("${collection.name}", "${base}", ${metaGlob$1}, ${headGlob}, ${bodyGlob})`;
}
const [metaGlob, docGlob] = await Promise.all([generateMetaCollectionGlob(ctx, collection.meta, true), generateDocCollectionGlob(ctx, collection.docs, true)]);
return `await create.docs("${collection.name}", "${base}", ${metaGlob}, ${docGlob})`;
}
case "doc":
if (collection.dynamic) return;
if (collection.async) {
const [headGlob, bodyGlob] = await Promise.all([generateDocCollectionFrontmatterGlob(ctx, collection, true), generateDocCollectionGlob(ctx, collection)]);
return `await create.docLazy("${collection.name}", "${base}", ${headGlob}, ${bodyGlob})`;
}
return `await create.doc("${collection.name}", "${base}", ${await generateDocCollectionGlob(ctx, collection, true)})`;
case "meta": return `await create.meta("${collection.name}", "${base}", ${await generateMetaCollectionGlob(ctx, collection, true)})`;
}
}
await codegen.pushAsync(core.getCollections().map(async (collection) => {
const obj = await generateCollectionObject(collection);
if (!obj) return;
return `\nexport const ${collection.name} = ${obj};`;
}));
}
async function generateDynamicIndexFile(ctx) {
const { core, codegen, serverOptions, tc } = ctx;
const { configPath, environment, outDir } = core.getOptions();
const partialOptions = {
configPath,
environment,
outDir
};
codegen.lines.push(`import { dynamic } from 'fumadocs-mdx/runtime/dynamic';`, `import * as Config from '${codegen.formatImportPath(configPath)}';`, "", `const create = await dynamic<typeof Config, ${tc}>(Config, ${JSON.stringify(partialOptions)}, ${JSON.stringify(serverOptions)});`);
async function generateCollectionObjectEntry(collection, absolutePath) {
const fullPath = path$1.relative(process.cwd(), absolutePath);
const content = await indexFileCache.read(fullPath).catch(() => "");
const parsed = fumaMatter(content);
const data = await core.transformFrontmatter({
collection,
filePath: fullPath,
source: content
}, parsed.data);
const hash = createHash("md5").update(content).digest("hex");
const infoStr = [`absolutePath: path.resolve(${JSON.stringify(fullPath)})`];
for (const [k, v] of Object.entries({
info: {
fullPath,
path: path$1.relative(collection.dir, absolutePath)
},
data,
hash
})) infoStr.push(`${k}: ${JSON.stringify(v)}`);
return `{ ${infoStr.join(", ")} }`;
}
async function generateCollectionObject(parent) {
let collection;
if (parent.type === "doc") collection = parent;
else if (parent.type === "docs") collection = parent.docs;
if (!collection || !collection.dynamic) return;
const files = await glob(collection.patterns, {
cwd: collection.dir,
absolute: true
});
const entries = await Promise.all(files.map((file) => generateCollectionObjectEntry(collection, file)));
switch (parent.type) {
case "docs": {
const metaGlob = await generateMetaCollectionGlob(ctx, parent.meta, true);
return `await create.docs("${parent.name}", "${getBase(parent)}", ${metaGlob}, ${entries.join(", ")})`;
}
case "doc": return `await create.doc("${collection.name}", "${getBase(collection)}", ${entries.join(", ")})`;
}
}
await codegen.pushAsync(core.getCollections().map(async (collection) => {
const obj = await generateCollectionObject(collection);
if (!obj) return;
return `\nexport const ${collection.name} = ${obj};`;
}));
}
async function generateBrowserIndexFile(ctx) {
const { core, codegen, tc } = ctx;
codegen.lines.push(`import { browser } from 'fumadocs-mdx/runtime/browser';`, `import type * as Config from '${codegen.formatImportPath(core.getOptions().configPath)}';`, "", `const create = browser<typeof Config, ${tc}>();`);
async function generateCollectionObject(collection) {
switch (collection.type) {
case "docs":
if (collection.docs.dynamic) return;
return generateCollectionObject(collection.docs);
case "doc":
if (collection.dynamic) return;
return `create.doc("${collection.name}", ${await generateDocCollectionGlob(ctx, collection)})`;
}
}
codegen.lines.push("const browserCollections = {");
await codegen.pushAsync(core.getCollections().map(async (collection) => {
const obj = await generateCollectionObject(collection);
if (!obj) return;
return ident(`${collection.name}: ${obj},`);
}));
codegen.lines.push("};", "export default browserCollections;");
}
function getBase(collection) {
return slash(path$1.relative(process.cwd(), collection.dir));
}
function generateDocCollectionFrontmatterGlob({ codegen, workspace }, collection, eager = false) {
return codegen.generateGlobImport(collection.patterns, {
query: {
collection: collection.name,
only: "frontmatter",
workspace
},
import: "frontmatter",
base: collection.dir,
eager
});
}
function generateDocCollectionGlob({ codegen, workspace }, collection, eager = false) {
return codegen.generateGlobImport(collection.patterns, {
query: {
collection: collection.name,
workspace
},
base: collection.dir,
eager
});
}
function generateMetaCollectionGlob({ codegen, workspace }, collection, eager = false) {
return codegen.generateGlobImport(collection.patterns, {
query: {
collection: collection.name,
workspace
},
import: "default",
base: collection.dir,
eager
});
}
//#endregion
export { indexFile as t };
//# sourceMappingURL=index-file-D9HsrWU_.js.map