UNPKG

iles

Version:

Vite & Vue powered static site generator with partial hydration

120 lines (111 loc) 4.41 kB
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 };