UNPKG

@taprootio/rollup-plugin-taproot

Version:
264 lines (226 loc) 7.85 kB
import { OutputAsset, OutputBundle, OutputChunk, Plugin } from "rollup" import { TaprootPluginOptions } from "./models/TaprootPluginOptions" import { Template } from "./models/TaprootTemplateParser" import readdir from "recursive-readdir" import fs from "fs" import path from "path" import { Page } from "./models/TaprootPageRenderer" import { BuildHead } from "./head-builder" import { cloneable } from "./helpers.ts/cloneable" interface FileElements { scripts: string links: string } const determinePath = (fileName: string, canonical: string): string => { let emitPath: string if (canonical) { emitPath = canonical } else { emitPath = fileName.split(".")[0] } if (emitPath.endsWith("index")) { emitPath = emitPath.substring(0, emitPath.length - 5) } if (emitPath.endsWith("/")) { emitPath = emitPath.substring(0, emitPath.length - 1) } return emitPath } const buildTagPath = (tagRoot: string, tag: string): string => `${tagRoot ?? "/tag/"}${encodeURI(tag.toLowerCase().replace(" ", "-"))}` const getElements = ( files: Record<string, (OutputChunk | OutputAsset)[]> ): FileElements => { // https://github.com/rollup/plugins/blob/master/packages/html/src/index.ts // TODO: Set these from options... const publicPath = "/" // TODO: Allow specifying attributes const attrs = "" const scripts = (files.js || []) .map(({ fileName }) => { // const attrs = makeHtmlAttributes(attributes.script); return `<script src="${publicPath}${fileName}"${attrs} type="module"></script>` }) .join("\n") const links = (files.css || []) .map(({ fileName }) => { // const attrs = makeHtmlAttributes(attributes.link); return `<link href="${publicPath}${fileName}" rel="stylesheet"${attrs}>` }) .join("\n") return { scripts, links, } } const getFiles = ( bundle: OutputBundle ): Record<string, (OutputChunk | OutputAsset)[]> => { const files = Object.values(bundle).filter( (file) => file.type === "chunk" || file.type === "asset" ) const result = {} as ReturnType<typeof getFiles> for (const file of files) { const { fileName } = file const extension = path.extname(fileName).substring(1) result[extension] = (result[extension] || []).concat(file) } return result } const Taproot = (options: TaprootPluginOptions): Plugin => { const templates = new Map<String, Template>() let pagesToRender: Map<string, Page> return { name: "rollup-plugin-taproot", async buildStart(_options) { pagesToRender = new Map<string, Page>() const templateFiles = await readdir(options.TemplatesPath) for (const templateFile of templateFiles) { for (const templateParser of options.TemplateParsers) { if (!templateParser.FileMatcher.test(templateFile)) { continue } const source = fs.readFileSync(templateFile).toString() const templateKey = path.basename(templateFile).split(".")[0] const compiled = templateParser.CompileTemplate(source) templates.set(templateKey, compiled) } } console.log("Reading pages...") const pageFiles = await readdir(options.PagesPath) for (const pageFile of pageFiles) { for (const pageRenderer of options.PageRenderers) { if (!pageRenderer.FileMatcher.test(pageFile)) { continue } const source = fs.readFileSync(pageFile).toString() const fileName = pageFile.substring( pageFile.indexOf(`${options.PagesPath.substring(1)}/`) + options.PagesPath.length ) // Clone it so any changes made here do not affect cached // data in the renderer. const rendered = cloneable.deepCopy(pageRenderer.Render(source)) rendered.Data.Canonical = determinePath( fileName, rendered.Data.Canonical ?? "" ) pagesToRender.set(fileName, rendered) } } const tags = new Map<string, Array<Page>>() for (const page of pagesToRender.values()) { for (const tag of page.Data.Tags ?? []) { if (!tags.has(tag)) { tags.set(tag, []) } tags.get(tag)?.push(page) } } for (const tag of tags.keys()) { const pages = tags.get(tag) ?? [] const contents = ` <ul class="articles"> ${pages .map( (page) => ` <li> <article> <h2 slot="page-title">${page.Data.Title}</h2> ${ page.Data.Description ? `<p slot="page-description">${page.Data.Description}</p>` : "" } <a href="/${page.Data.Canonical}">Read More</a> </article> </li>` ) .join("\n")} </ul>` const page: Page = { Contents: contents, Data: { Title: tag, Description: `Pages tagged with '${tag}'`, Canonical: determinePath( `${buildTagPath(options.TagRoot, tag).substring(1)}`, "" ), PageType: "website", }, } pagesToRender.set(buildTagPath(options.TagRoot, tag), page) } }, generateBundle(_options, bundle) { const assets = getFiles(bundle) const elements = getElements(assets) const currentYear = new Date().getFullYear().toString() for (const fileName of pagesToRender.keys()) { const taprootPage = pagesToRender.get(fileName) as Page console.log(JSON.stringify(taprootPage.Data)) const outPath = taprootPage.Data.Canonical ?? "" const author = taprootPage.Data.Author ? options.Authors.get(taprootPage.Data.Author) : undefined const head = [ BuildHead({ Author: author, DateModified: taprootPage.Data.DateModified, DatePublished: taprootPage.Data.DatePublished, Publisher: options.Publisher, SocialImage: { Height: 100, Width: 100, MediaType: "image/png", Url: `${options.SiteRootUrl}${ outPath ? `${outPath}/` : "" }social_image.png}`, }, Title: taprootPage.Data.Title, Canonical: `${options.SiteRootUrl}${outPath}`, CharSet: options.CharSet ?? "UTF-8", Description: taprootPage.Data.Description, PageType: taprootPage.Data.PageType, }), elements.links, elements.scripts, ].join("\n") const template = templates.has(taprootPage.Data.Template ?? "") ? (taprootPage.Data.Template as string) : "default" const rendered = templates.get(template)?.Render({ Author: author, Canonical: taprootPage.Data.Canonical, Contents: taprootPage.Contents, CSSVars: taprootPage.Data.CSSVars, CurrentYear: currentYear, DateModified: taprootPage.Data.DateModified, DatePublished: taprootPage.Data.DatePublished, Description: taprootPage.Data.Description, Head: head, HidePageHead: taprootPage.Data.HidePageHead, PageType: taprootPage.Data.PageType, SiteName: options.Publisher.Name, Tags: taprootPage.Data.Tags?.map((tag) => { return { Tag: tag, Url: buildTagPath(options.TagRoot, tag), } }) ?? [], Title: taprootPage.Data.Title, }) as string const htmlPath = `${outPath ? `${outPath}/` : ""}index.html` console.log(`Emitting file ${htmlPath}`) this.emitFile({ type: "asset", fileName: htmlPath, source: rendered, }) } }, } } export { Taproot }