iles
Version:
Vite & Vue powered static site generator with partial hydration
120 lines (111 loc) • 4.41 kB
JavaScript
import {
parseId
} from "./chunk-6FAOHHHS.js";
import {
debug,
default as default2
} from "./chunk-ROUSHGC2.js";
// src/node/plugin/documents.ts
import { resolve } from "path";
import glob from "fast-glob";
import micromatch from "micromatch";
import { relative } from "pathe";
var definitionRegex = /(function|const|let|var)[\s\n]+\buseDocuments\b/;
var usageRegex = /\buseDocuments[\s\n]*\(([^)]+)\)/g;
var fileCanUseDocuments = /(\.vue|\.[tj]sx?)$/;
var DOCS_VIRTUAL_ID = "/@islands/documents";
function documentsPlugin(config) {
const { root, drafts, namedPlugins: { pages } } = config;
let server;
const modulesById = /* @__PURE__ */ Object.create(null);
return {
name: "iles:documents",
configureServer(devServer) {
server = devServer;
},
resolveId(id) {
if (id.startsWith(DOCS_VIRTUAL_ID))
return id;
},
// Extract frontmatter for each file in the matching pattern, and create a
// module where the default export is an array with each matching document.
async load(id, options) {
if (!id.startsWith(DOCS_VIRTUAL_ID)) return;
const { query: { pattern: rawPath } } = parseId(id);
const path = relative(root, await config.resolvePath(rawPath) || rawPath);
const pattern = path.includes("*") ? path : `${path}/**/*.{md,mdx}`;
if (server)
modulesById[id] = { pattern, hasDocument: (path2) => micromatch.isMatch(path2, pattern) };
const files = await glob(pattern, { cwd: root });
debug.documents("%s %O", rawPath, { path, pattern, files });
let data = await Promise.all(files.map(async (file) => {
const frontmatter = await pages.api.frontmatterForPageOrFile(file);
frontmatter.meta.filename ||= file;
return frontmatter;
}));
if (!drafts)
data = data.filter((page) => !page.draft);
debug.documents(`${files.length} files, ${data.length} documents, drafts: ${drafts}`);
const documents = data.map(({ route: _, meta, layout, ...frontmatter }, index) => {
return { ...meta, ...frontmatter, meta, frontmatter, component: `${index}_component` };
});
const serialized = default2(documents).replace(/component:"(\w+)"/g, (_, id2) => {
const index = id2.split("_component")[0];
return `component: unwrapDefault(() => import('/${documents[index].filename}'))`;
});
return `
import { shallowRef, defineAsyncComponent } from 'vue'
export const documents = ${serialized}
.map(doc => ({ ...doc, ...defineAsyncComponent(doc.component) }))
export default documents.ref = shallowRef(documents)
function unwrapDefault (fn) {
let cached
return () => cached ||= fn().then(mod => mod.default)
}
if (import.meta.hot) {
import.meta.hot.accept(mod => {
const docs = documents.ref.value
const oldDocsByFile = {}
docs.forEach(doc => { oldDocsByFile[doc.filename] = doc })
documents.ref.value = mod.documents.map(newDoc => {
const oldDoc = oldDocsByFile[newDoc.filename]
if (!oldDoc) return newDoc
const { meta, frontmatter } = newDoc
return Object.assign(oldDoc, { ...meta, ...frontmatter, meta, frontmatter })
})
mod.documents.ref = documents.ref
})
}
`;
},
async transform(code, id) {
if (server && id.startsWith(DOCS_VIRTUAL_ID)) {
server._importGlobMap.set(id, [resolve(root, modulesById[id].pattern)]);
return;
}
if (fileCanUseDocuments.test(id) && !definitionRegex.test(code)) {
const paths = [];
code = code.replace(usageRegex, (_, path) => {
path = path.trim().slice(1, -1);
const id2 = `_documents_${paths.length}`;
paths.push([id2, path]);
return id2;
});
if (paths.length) {
const imports = paths.map(([id2, path]) => `import ${id2} from '${DOCS_VIRTUAL_ID}?pattern=${path}'`);
return `${code};${imports.join(";")}`;
}
}
},
handleHotUpdate(ctx) {
const file = relative(root, ctx.file);
for (const id in modulesById) {
if (modulesById[id].hasDocument(file))
ctx.modules.push(server.moduleGraph.getModuleById(id));
}
}
};
}
export {
documentsPlugin
};