Browse Source

Refactor and tidy:

* Make all methods async
* Add template cache
* clone for better sharp performance
Craig Fletcher 5 months ago
parent
commit
0131d3cf53
1 changed files with 77 additions and 63 deletions
  1. 77 63
      src/processors.js

+ 77 - 63
src/processors.js

@@ -1,6 +1,6 @@
 import * as sass from "sass";
 import { firstFound } from "./util.js";
-import fs from "fs";
+import fs from "fs/promises";
 import handlebars from "handlebars";
 import { marked } from "marked";
 import markedCodePreview from "marked-code-preview";
@@ -18,98 +18,109 @@ const imageSizes = [
   "1920w",
   "2560w"
 ];
+const templateCache = new Map();
 
 function stripFileExtension(filePath) {
-  if (typeof filePath !== "string") return "";
-
-  const parts = filePath.split("/");
-  const fileName = parts.pop();
-
-  const nameWithoutExt = fileName.replace(/\.[^/.]+$/, "");
-
-  return [...parts, nameWithoutExt].join("/");
+  return path.join(
+    path.dirname(filePath),
+    path.basename(filePath, path.extname(filePath))
+  );
 }
 
-const renderer = meta => ({
-  image({ href, title, text }) {
-    const hrefWithoutFileExtension = stripFileExtension(href);
-    const attrs = [`alt="${text}"`];
+function getCleanPath(filePath, meta) {
+  return filePath.replace(meta.opts.runDir, "").replace(meta.opts.baseDir, "/");
+}
 
-    const foundSrcSet = meta.resources.images.find(imageResource => {
-      return (
-        stripFileExtension(imageResource.path[0]) === hrefWithoutFileExtension
-      );
+function createMarkdownRenderer(meta) {
+  return marked
+    .use({ gfm: true })
+    .use(markedCodePreview)
+    .use({
+      renderer: {
+        image({ href, title, text }) {
+          const hrefWithoutExt = stripFileExtension(href);
+          const attrs = [`alt="${text}"`];
+
+          const foundSrcSet = meta.resources.images.find(({ path }) => {
+            return stripFileExtension(path[0]) === hrefWithoutExt;
+          });
+
+          if (foundSrcSet) {
+            const srcSetString = foundSrcSet.path[1]
+              .map(src => src.join(" "))
+              .join(", ");
+            const defaultSrc = foundSrcSet.path[1][0][0];
+            attrs.push(`src="${defaultSrc}"`);
+            attrs.push(`srcset="${srcSetString}"`);
+            attrs.push(
+              `sizes="(min-width: 1600px) 25vw, (min-width: 800px) 50vw, 100vw"`
+            );
+            attrs.push(
+              `style="aspect-ratio: ${foundSrcSet.path[2].aspectRatio}"`
+            );
+          } else {
+            attrs.push(`src="${href}"`);
+          }
+
+          if (title) {
+            attrs.push(`title="${title}"`);
+          }
+
+          return `<img ${attrs.join(" ")} >`;
+        }
+      }
     });
-    if (foundSrcSet) {
-      const srcSetString = foundSrcSet.path[1]
-        .map(src => src.join(" "))
-        .join(", ");
-      const defaultSrc = foundSrcSet.path[1][0][0];
-      attrs.push(`src="${defaultSrc}"`);
-      attrs.push(`srcset="${srcSetString}"`);
-      attrs.push(
-        `sizes="(min-width: 1600px) 25vw, (min-width: 800px) 50vw, 100vw"`
-      );
-      attrs.push(`style="aspect-ratio: ${foundSrcSet.path[2].aspectRatio}"`);
-    } else {
-      attrs.push(`src="${href}"`);
-    }
-
-    if (title) {
-      attrs.push(`title="${title}"`);
-    }
-
-    return `<img ${attrs.join(" ")} >`;
-  }
-});
-
-const markedRenderer = marked.use({ gfm: true }).use(markedCodePreview);
+}
 
-export function renderMarkdownWithTemplate(filePath, meta) {
-  const content = fs.readFileSync(filePath, "utf8");
+export async function renderMarkdownWithTemplate(filePath, meta) {
+  const content = await fs.readFile(filePath, "utf8");
   const { data, content: markdown } = matter(content);
   const templateName = data.template || meta.opts.defaultTemplate;
 
-  const template = handlebars.compile(
-    fs.readFileSync(
-      firstFound(meta.opts.templateDirs, `${templateName}.hbs`),
-      "utf8"
-    )
-  );
+  if (!templateCache.has(templateName)) {
+    const templatePath = firstFound(
+      meta.opts.templateDirs,
+      `${templateName}.hbs`
+    );
+    if (!templatePath) throw new Error(`Template not found: ${templateName}`);
+    const templateContent = await fs.readFile(templatePath, "utf8");
+    templateCache.set(templateName, handlebars.compile(templateContent));
+  }
+
+  const template = templateCache.get(templateName);
+  const renderer = createMarkdownRenderer(meta);
   const html = template({
     ...data,
     ...meta,
-    content: markedRenderer.use({ renderer: renderer(meta) })(markdown)
+    content: renderer(markdown)
   });
+
   return {
     detail: data,
     result: html
   };
 }
 
-export function compileSass(filePath) {
-  return {
-    result: sass.compile(filePath, { style: "compressed" }).css.toString()
-  };
+export async function compileSass(filePath) {
+  const result = await sass.compileAsync(filePath, { style: "compressed" });
+  return { result: result.css.toString() };
 }
 
-export function optimiseSvg(filePath) {
-  const svgString = fs.readFileSync(filePath, "utf8");
+export async function optimiseSvg(filePath) {
+  const svgString = await fs.readFile(filePath, "utf8");
   const result = optimize(svgString, {
     plugins: ["preset-default"]
   });
   return { result: result.data };
 }
 
-function getCleanPath(path, meta) {
-  return path.replace(meta.opts.runDir, "").replace(meta.opts.baseDir, "/");
-}
-
 export async function optimiseImage(filePath, meta, fileOutputPath) {
   const sourceExtension = path.extname(filePath);
   const outputExtension = ".webp";
-  const base = path.basename(filePath).slice(0, -sourceExtension.length);
-  const metadata = await sharp(filePath).metadata();
+  const base = path.basename(filePath, sourceExtension);
+
+  const original = sharp(filePath);
+  const metadata = await original.metadata();
   const { width, height } = metadata;
 
   if (!width || !height) {
@@ -126,7 +137,8 @@ export async function optimiseImage(filePath, meta, fileOutputPath) {
         `${base}-${sizeNum}${outputExtension}`
       );
 
-      await sharp(filePath)
+      await original
+        .clone()
         .resize(sizeNum)
         .webp({ quality: 80 })
         .toFile(outputFile);
@@ -134,10 +146,12 @@ export async function optimiseImage(filePath, meta, fileOutputPath) {
       return [getCleanPath(outputFile, meta), size];
     })
   );
+
   const imageRef = getCleanPath(
     path.join(fileOutputPath, `${base}${outputExtension}`),
     meta
   );
+
   return {
     result: [imageRef, srcset, { aspectRatio }],
     written: true