UNPKG

@taprootio/rollup-plugin-taproot

Version:
298 lines (289 loc) 13.8 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var readdir = require('recursive-readdir'); var fs = require('fs'); var path = require('path'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var readdir__default = /*#__PURE__*/_interopDefaultLegacy(readdir); var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); class cloneable { static deepCopy(source) { return Array.isArray(source) ? source.map((item) => this.deepCopy(item)) : source instanceof Date ? new Date(source.getTime()) : source && typeof source === "object" ? Object.getOwnPropertyNames(source).reduce((o, prop) => { Object.defineProperty(o, prop, Object.getOwnPropertyDescriptor(source, prop)); // eslint-disable-next-line @typescript-eslint/no-explicit-any o[prop] = this.deepCopy(source[prop]); return o; }, Object.create(Object.getPrototypeOf(source))) : source; } } // NOTE: Flash of unstyled content can happen when resources, // such as fonts and other styles, are loaded after // initial render. This waits for things to load before // showing the page. Template body needs an initial // class of "loading" for it to work. const FUCFix = [ `<style> body { transition: opacity ease-in 500ms; } body.loading { opacity: 0; } body.loading.loaded { opacity: 100; } </style>`, `<script type="text/javascript"> window.onload = () => { document.body.classList.add("loaded") } </script>`, ]; const BuildHead = (data) => { var _a, _b, _c, _d, _e; if (!data.Canonical) { throw "No canonical specified"; } // Clone it so any changes made here do not bubble up. data = cloneable.deepCopy(data); console.log(JSON.stringify(data)); data.Canonical = (_a = data.Canonical) === null || _a === void 0 ? void 0 : _a.replace("index.html", ""); data.CharSet = (_b = data.CharSet) !== null && _b !== void 0 ? _b : "UTF-8"; data.Locale = (_c = data.Locale) !== null && _c !== void 0 ? _c : "en_US"; data.PageType = (_e = (_d = data.PageType) === null || _d === void 0 ? void 0 : _d.toLowerCase()) !== null && _e !== void 0 ? _e : "article"; if (!data.Title) { throw `No title for ${data.Canonical}`; } if (!data.Description) { throw `No description for ${data.Canonical}`; } const validPageTypes = ["article", "website"]; if (validPageTypes.indexOf(data.PageType) < 0) { throw `Invalid page type for ${data.Canonical}`; } if (data.PageType == "article" && !data.Author) { throw `Article must have an author for ${data.Canonical}`; } const tags = [ `<meta charset="${data.CharSet}" />`, `<meta name="viewport" content="width=device-width, initial-scale=1" />`, ]; if (!data.NoRobots) { tags.push(`<meta name="robots" content="index, follow, max-image-preview:large" />`); } tags.push(...FUCFix); tags.push(`<title>${data.Title}${data.Publisher.Name}</title>`, `<meta name="description" content="${data.Description}" />`, `<link rel="canonical" href="${data.Canonical}" />`, `<meta property="og:locale" content="${data.Locale}" />`, `<meta property="og:type" content="${data.PageType}" />`, `<meta property="og:title" content="${data.Title}" />`, `<meta property="og:description" content="${data.Description}" />`, `<meta property="og:url" content="${data.Canonical}" />`, `<meta property="og:site_name" content="${data.Publisher.Name}" />`, `<meta property="article:publisher" content="${data.Publisher.FacebookPage}" />`, `<meta property="og:image" content="${data.SocialImage.Url}" />`, `<meta property="og:image:width" content="${data.SocialImage.Width}" />`, `<meta property="og:image:height" content="${data.SocialImage.Height}" />`, `<meta property="og:image:type" content="${data.SocialImage.MediaType}" />`, // e.g. "image/png" `<meta name="twitter:card" content="summary_large_image" />`, `<meta name="twitter:site" content="${data.Publisher.TwitterHandle}" />`); if (data.DatePublished) { tags.push(`<meta property="article:published_time" content="${data.DatePublished}" />`); } if (data.DateModified) { tags.push(`<meta property="article:modified_time" content="${data.DateModified}" />`); } if (data.Author) { tags.push(`<meta property="article:author" content="${data.Author.FacebookPage}" />`, `<meta name="author" content="${data.Author.Name}" />`, `<meta name="twitter:creator" content="${data.Author.TwitterHandle}" />`, `<meta name="twitter:label1" content="Written by" />`, `<meta name="twitter:data1" content="${data.Author.Name}" />`); } return tags.join("\n"); }; const determinePath = (fileName, canonical) => { let emitPath; 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, tag) => `${tagRoot !== null && tagRoot !== void 0 ? tagRoot : "/tag/"}${encodeURI(tag.toLowerCase().replace(" ", "-"))}`; const getElements = (files) => { // 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) => { const files = Object.values(bundle).filter((file) => file.type === "chunk" || file.type === "asset"); const result = {}; for (const file of files) { const { fileName } = file; const extension = path__default["default"].extname(fileName).substring(1); result[extension] = (result[extension] || []).concat(file); } return result; }; const Taproot = (options) => { const templates = new Map(); let pagesToRender; return { name: "rollup-plugin-taproot", async buildStart(_options) { var _a, _b, _c, _d; pagesToRender = new Map(); const templateFiles = await readdir__default["default"](options.TemplatesPath); for (const templateFile of templateFiles) { for (const templateParser of options.TemplateParsers) { if (!templateParser.FileMatcher.test(templateFile)) { continue; } const source = fs__default["default"].readFileSync(templateFile).toString(); const templateKey = path__default["default"].basename(templateFile).split(".")[0]; const compiled = templateParser.CompileTemplate(source); templates.set(templateKey, compiled); } } console.log("Reading pages..."); const pageFiles = await readdir__default["default"](options.PagesPath); for (const pageFile of pageFiles) { for (const pageRenderer of options.PageRenderers) { if (!pageRenderer.FileMatcher.test(pageFile)) { continue; } const source = fs__default["default"].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, (_a = rendered.Data.Canonical) !== null && _a !== void 0 ? _a : ""); pagesToRender.set(fileName, rendered); } } const tags = new Map(); for (const page of pagesToRender.values()) { for (const tag of (_b = page.Data.Tags) !== null && _b !== void 0 ? _b : []) { if (!tags.has(tag)) { tags.set(tag, []); } (_c = tags.get(tag)) === null || _c === void 0 ? void 0 : _c.push(page); } } for (const tag of tags.keys()) { const pages = (_d = tags.get(tag)) !== null && _d !== void 0 ? _d : []; 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 = { 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) { var _a, _b, _c, _d, _e, _f; 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); console.log(JSON.stringify(taprootPage.Data)); const outPath = (_a = taprootPage.Data.Canonical) !== null && _a !== void 0 ? _a : ""; 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: (_b = options.CharSet) !== null && _b !== void 0 ? _b : "UTF-8", Description: taprootPage.Data.Description, PageType: taprootPage.Data.PageType, }), elements.links, elements.scripts, ].join("\n"); const template = templates.has((_c = taprootPage.Data.Template) !== null && _c !== void 0 ? _c : "") ? taprootPage.Data.Template : "default"; const rendered = (_d = templates.get(template)) === null || _d === void 0 ? void 0 : _d.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: (_f = (_e = taprootPage.Data.Tags) === null || _e === void 0 ? void 0 : _e.map((tag) => { return { Tag: tag, Url: buildTagPath(options.TagRoot, tag), }; })) !== null && _f !== void 0 ? _f : [], Title: taprootPage.Data.Title, }); const htmlPath = `${outPath ? `${outPath}/` : ""}index.html`; console.log(`Emitting file ${htmlPath}`); this.emitFile({ type: "asset", fileName: htmlPath, source: rendered, }); } }, }; }; exports.Taproot = Taproot; //# sourceMappingURL=index.js.map