|
|
@@ -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
|