|
|
@@ -1,24 +1,24 @@
|
|
|
-import * as sass from "sass"
|
|
|
+import * as sass from "sass";
|
|
|
import {
|
|
|
firstFound,
|
|
|
generateRandomId,
|
|
|
getCleanPath,
|
|
|
getHref,
|
|
|
slugifyString,
|
|
|
- writeFile,
|
|
|
-} from "./util/index.js"
|
|
|
-import fs from "fs/promises"
|
|
|
-import handlebars from "handlebars"
|
|
|
-import { marked } from "marked"
|
|
|
-import markedCodePreview from "marked-code-preview"
|
|
|
-import matter from "gray-matter"
|
|
|
-import { optimize } from "svgo"
|
|
|
-import sharp from "sharp"
|
|
|
-import path from "path"
|
|
|
-import { minify } from "html-minifier-terser"
|
|
|
-import favicons from "favicons"
|
|
|
+ writeFile
|
|
|
+} from "./util/index.js";
|
|
|
+import fs from "fs/promises";
|
|
|
+import handlebars from "handlebars";
|
|
|
+import { marked } from "marked";
|
|
|
+import markedCodePreview from "marked-code-preview";
|
|
|
+import matter from "gray-matter";
|
|
|
+import { optimize } from "svgo";
|
|
|
+import sharp from "sharp";
|
|
|
+import path from "path";
|
|
|
+import { minify } from "html-minifier-terser";
|
|
|
+import favicons from "favicons";
|
|
|
|
|
|
-const templateCache = new Map()
|
|
|
+const templateCache = new Map();
|
|
|
|
|
|
function createMarkdownRenderer(meta) {
|
|
|
return marked
|
|
|
@@ -27,71 +27,64 @@ function createMarkdownRenderer(meta) {
|
|
|
.use({
|
|
|
renderer: {
|
|
|
image({ href, title, text }) {
|
|
|
- const attrs = [`alt="${text}"`]
|
|
|
+ const attrs = [`alt="${text}"`];
|
|
|
|
|
|
- const foundSrcSet = meta.resources.images[slugifyString(href)]
|
|
|
+ const foundSrcSet = meta.resources.images?.[slugifyString(href)];
|
|
|
|
|
|
if (foundSrcSet) {
|
|
|
const srcSetString = foundSrcSet.detail.srcSet
|
|
|
.map(src => src.join(" "))
|
|
|
- .join(", ")
|
|
|
- const defaultSrc = foundSrcSet.detail.srcSet[0][0]
|
|
|
- attrs.push(`src="${defaultSrc}"`)
|
|
|
- attrs.push(`srcset="${srcSetString}"`)
|
|
|
- attrs.push("sizes=\"(min-width: 800px) 40vw, 100vw\"")
|
|
|
+ .join(", ");
|
|
|
+ const defaultSrc = foundSrcSet.detail.srcSet[0][0];
|
|
|
+ attrs.push(`src="${defaultSrc}"`);
|
|
|
+ attrs.push(`srcset="${srcSetString}"`);
|
|
|
+ attrs.push('sizes="(min-width: 800px) 40vw, 100vw"');
|
|
|
attrs.push(
|
|
|
- `style="aspect-ratio: ${foundSrcSet.detail.aspectRatio}"`,
|
|
|
- )
|
|
|
+ `style="aspect-ratio: ${foundSrcSet.detail.aspectRatio}"`
|
|
|
+ );
|
|
|
} else {
|
|
|
- attrs.push(`src="${href}"`)
|
|
|
+ attrs.push(`src="${href}"`);
|
|
|
}
|
|
|
|
|
|
if (title) {
|
|
|
- attrs.push(`title="${title}"`)
|
|
|
+ attrs.push(`title="${title}"`);
|
|
|
}
|
|
|
|
|
|
- return `<img ${attrs.join(" ")} >`
|
|
|
- },
|
|
|
- },
|
|
|
- })
|
|
|
+ return `<img ${attrs.join(" ")} >`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
async function findTemplatePath(templateDirs, templateName) {
|
|
|
- const templatePath = await firstFound(
|
|
|
- templateDirs,
|
|
|
- `${templateName}.hbs`,
|
|
|
- )
|
|
|
- if (!templatePath) throw new Error(`Template not found: ${templateName}`)
|
|
|
- return templatePath
|
|
|
+ const templatePath = await firstFound(templateDirs, `${templateName}.hbs`);
|
|
|
+ if (!templatePath) throw new Error(`Template not found: ${templateName}`);
|
|
|
+ return templatePath;
|
|
|
}
|
|
|
|
|
|
async function getTemplate(templatePath) {
|
|
|
if (!templateCache.has(templatePath)) {
|
|
|
- const templateContent = await fs.readFile(templatePath, "utf8")
|
|
|
+ const templateContent = await fs.readFile(templatePath, "utf8");
|
|
|
templateCache.set(templatePath, {
|
|
|
path: templatePath,
|
|
|
- renderer: handlebars.compile(templateContent),
|
|
|
- })
|
|
|
+ renderer: handlebars.compile(templateContent)
|
|
|
+ });
|
|
|
}
|
|
|
- return templateCache.get(templatePath)
|
|
|
+ return templateCache.get(templatePath);
|
|
|
}
|
|
|
|
|
|
-export async function renderTemplate({
|
|
|
- config,
|
|
|
- meta,
|
|
|
-}) {
|
|
|
- const templatePath = config.filePath || await findTemplatePath(
|
|
|
- config.templateDirs,
|
|
|
- config.template,
|
|
|
- )
|
|
|
- const fileOutputPath = config.fileOutputPath
|
|
|
- const href = getHref(fileOutputPath, meta)
|
|
|
+export async function renderTemplate({ config, meta }) {
|
|
|
+ const templatePath =
|
|
|
+ config.filePath ||
|
|
|
+ (await findTemplatePath(config.templateDirs, config.template));
|
|
|
+ const fileOutputPath = config.fileOutputPath;
|
|
|
+ const href = getHref(fileOutputPath, meta);
|
|
|
|
|
|
- const template = await getTemplate(templatePath)
|
|
|
+ const template = await getTemplate(templatePath);
|
|
|
const html = template.renderer({
|
|
|
...meta,
|
|
|
href,
|
|
|
- ...config,
|
|
|
- })
|
|
|
+ ...config
|
|
|
+ });
|
|
|
if (config.writeOut) {
|
|
|
const minifiedHtml = await minify(html, {
|
|
|
collapseWhitespace: true,
|
|
|
@@ -99,188 +92,182 @@ export async function renderTemplate({
|
|
|
removeRedundantAttributes: true,
|
|
|
removeEmptyAttributes: true,
|
|
|
minifyCSS: true,
|
|
|
- minifyJS: true,
|
|
|
- })
|
|
|
+ minifyJS: true
|
|
|
+ });
|
|
|
|
|
|
- await writeFile(fileOutputPath, minifiedHtml)
|
|
|
+ await writeFile(fileOutputPath, minifiedHtml);
|
|
|
return {
|
|
|
detail: { html },
|
|
|
deps: {
|
|
|
- paths: [template.path],
|
|
|
+ paths: [template.path]
|
|
|
},
|
|
|
paths: [fileOutputPath],
|
|
|
- ref: slugifyString(href),
|
|
|
- }
|
|
|
+ ref: slugifyString(href)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
detail: { html },
|
|
|
deps: {
|
|
|
- paths: [template.path],
|
|
|
+ paths: [template.path]
|
|
|
},
|
|
|
- ref: slugifyString(href),
|
|
|
- }
|
|
|
+ ref: slugifyString(href)
|
|
|
+ };
|
|
|
}
|
|
|
-export async function renderMarkdownToHtml({
|
|
|
- config,
|
|
|
- meta,
|
|
|
-}) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputPath = config.fileOutputPath
|
|
|
- const content = await fs.readFile(filePath, "utf8")
|
|
|
- const { data, content: markdown } = matter(content)
|
|
|
- const href = getHref(fileOutputPath, meta)
|
|
|
+export async function renderMarkdownToHtml({ config, meta }) {
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputPath = config.fileOutputPath;
|
|
|
+ const content = await fs.readFile(filePath, "utf8");
|
|
|
+ const { data, content: markdown } = matter(content);
|
|
|
+ const href = getHref(fileOutputPath, meta);
|
|
|
|
|
|
- const renderer = createMarkdownRenderer(meta)
|
|
|
- const html = renderer(markdown)
|
|
|
+ const renderer = createMarkdownRenderer(meta);
|
|
|
+ const html = renderer(markdown);
|
|
|
|
|
|
return {
|
|
|
detail: { ...data, href, content: html, fileOutputPath },
|
|
|
- ref: slugifyString(filePath),
|
|
|
- }
|
|
|
+ ref: slugifyString(filePath)
|
|
|
+ };
|
|
|
}
|
|
|
-export async function renderMarkdownWithTemplate({
|
|
|
- config,
|
|
|
- meta,
|
|
|
-}) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputPath = config.fileOutputPath
|
|
|
- const content = await fs.readFile(filePath, "utf8")
|
|
|
- const { data, content: markdown } = matter(content)
|
|
|
- const templateName = data.template || config.defaultTemplate
|
|
|
- const href = getHref(fileOutputPath, meta)
|
|
|
+export async function renderMarkdownWithTemplate({ config, meta }) {
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputPath = config.fileOutputPath;
|
|
|
+ const content = await fs.readFile(filePath, "utf8");
|
|
|
+ const { data, content: markdown } = matter(content);
|
|
|
+ const templateName = data.template || config.defaultTemplate;
|
|
|
+ const href = getHref(fileOutputPath, meta);
|
|
|
|
|
|
if (!templateCache.has(templateName)) {
|
|
|
const templatePath = await firstFound(
|
|
|
config.templateDirs,
|
|
|
- `${templateName}.hbs`,
|
|
|
- )
|
|
|
- if (!templatePath) throw new Error(`Template not found: ${templateName}`)
|
|
|
- const templateContent = await fs.readFile(templatePath, "utf8")
|
|
|
+ `${templateName}.hbs`
|
|
|
+ );
|
|
|
+ if (!templatePath) throw new Error(`Template not found: ${templateName}`);
|
|
|
+ const templateContent = await fs.readFile(templatePath, "utf8");
|
|
|
templateCache.set(templateName, {
|
|
|
path: templatePath,
|
|
|
- renderer: handlebars.compile(templateContent),
|
|
|
- })
|
|
|
+ renderer: handlebars.compile(templateContent)
|
|
|
+ });
|
|
|
}
|
|
|
- const template = templateCache.get(templateName)
|
|
|
- const renderer = createMarkdownRenderer(meta)
|
|
|
+ const template = templateCache.get(templateName);
|
|
|
+ const renderer = createMarkdownRenderer(meta);
|
|
|
const html = template.renderer({
|
|
|
...data,
|
|
|
...meta,
|
|
|
href,
|
|
|
- content: renderer(markdown),
|
|
|
- })
|
|
|
+ content: renderer(markdown)
|
|
|
+ });
|
|
|
const minifiedHtml = await minify(html, {
|
|
|
collapseWhitespace: true,
|
|
|
removeComments: true,
|
|
|
removeRedundantAttributes: true,
|
|
|
removeEmptyAttributes: true,
|
|
|
minifyCSS: true,
|
|
|
- minifyJS: true,
|
|
|
- })
|
|
|
+ minifyJS: true
|
|
|
+ });
|
|
|
|
|
|
- await writeFile(fileOutputPath, minifiedHtml)
|
|
|
+ await writeFile(fileOutputPath, minifiedHtml);
|
|
|
|
|
|
return {
|
|
|
detail: { ...data, href },
|
|
|
paths: [fileOutputPath],
|
|
|
deps: {
|
|
|
- paths: [template.path],
|
|
|
+ paths: [template.path]
|
|
|
},
|
|
|
- ref: slugifyString(fileOutputPath),
|
|
|
- }
|
|
|
+ ref: slugifyString(fileOutputPath)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
export async function compileSass({ config, meta }) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputPath = config.fileOutputPath
|
|
|
- const result = await sass.compileAsync(filePath, { style: "compressed" })
|
|
|
- await writeFile(fileOutputPath, result.css)
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputPath = config.fileOutputPath;
|
|
|
+ const result = await sass.compileAsync(filePath, { style: "compressed" });
|
|
|
+ await writeFile(fileOutputPath, result.css);
|
|
|
return {
|
|
|
paths: [fileOutputPath],
|
|
|
ref: slugifyString(fileOutputPath),
|
|
|
detail: {
|
|
|
- href: fileOutputPath.replace(meta.opts.outDir, ""),
|
|
|
+ href: fileOutputPath.replace(meta.opts.outDir, "")
|
|
|
},
|
|
|
deps: {
|
|
|
- paths: [...result.loadedUrls.map(item => item.pathname)],
|
|
|
- },
|
|
|
- }
|
|
|
+ paths: [...result.loadedUrls.map(item => item.pathname)]
|
|
|
+ }
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
export async function optimiseSvg({ config }) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputPath = config.fileOutputPath
|
|
|
- const svgString = await fs.readFile(filePath, "utf8")
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputPath = config.fileOutputPath;
|
|
|
+ const svgString = await fs.readFile(filePath, "utf8");
|
|
|
const result = optimize(svgString, {
|
|
|
- plugins: ["preset-default"],
|
|
|
- })
|
|
|
- await writeFile(fileOutputPath, result.data)
|
|
|
+ plugins: ["preset-default"]
|
|
|
+ });
|
|
|
+ await writeFile(fileOutputPath, result.data);
|
|
|
return {
|
|
|
paths: [fileOutputPath],
|
|
|
- ref: slugifyString(fileOutputPath),
|
|
|
- }
|
|
|
+ ref: slugifyString(fileOutputPath)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
export async function copy({ config }) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputPath = config.fileOutputPath
|
|
|
- await fs.mkdir(config.fileOutputDir, { recursive: true })
|
|
|
- await fs.copyFile(filePath, fileOutputPath)
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputPath = config.fileOutputPath;
|
|
|
+ await fs.mkdir(config.fileOutputDir, { recursive: true });
|
|
|
+ await fs.copyFile(filePath, fileOutputPath);
|
|
|
return {
|
|
|
paths: [fileOutputPath],
|
|
|
- ref: slugifyString(fileOutputPath),
|
|
|
- }
|
|
|
+ ref: slugifyString(fileOutputPath)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
export async function imageToWebP({ meta, config }) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputDir = config.fileOutputDir
|
|
|
- const sourceExtension = path.extname(filePath)
|
|
|
- const outputExtension = config.outputFileExtension
|
|
|
- const base = path.basename(filePath, sourceExtension)
|
|
|
- await fs.mkdir(fileOutputDir, { recursive: true })
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputDir = config.fileOutputDir;
|
|
|
+ const sourceExtension = path.extname(filePath);
|
|
|
+ const outputExtension = config.outputFileExtension;
|
|
|
+ const base = path.basename(filePath, sourceExtension);
|
|
|
+ await fs.mkdir(fileOutputDir, { recursive: true });
|
|
|
|
|
|
- const original = sharp(filePath)
|
|
|
- const metadata = await original.metadata()
|
|
|
- const { width, height } = metadata
|
|
|
+ const original = sharp(filePath);
|
|
|
+ const metadata = await original.metadata();
|
|
|
+ const { width, height } = metadata;
|
|
|
|
|
|
if (!width || !height) {
|
|
|
- throw new Error("Could not determine image dimensions")
|
|
|
+ throw new Error("Could not determine image dimensions");
|
|
|
}
|
|
|
|
|
|
- const aspectRatio = width / height
|
|
|
- const name = config.uniqueFilenames ? base : `${base}-${generateRandomId()}`
|
|
|
+ const aspectRatio = width / height;
|
|
|
+ const name = config.uniqueFilenames ? base : `${base}-${generateRandomId()}`;
|
|
|
const srcSet = await Promise.all(
|
|
|
config.imageSizes.map(async size => {
|
|
|
- const sizeNum = parseInt(size.replace("w", ""), 10)
|
|
|
+ const sizeNum = parseInt(size.replace("w", ""), 10);
|
|
|
const outputFile = path.join(
|
|
|
fileOutputDir,
|
|
|
- `${name}-${sizeNum}${outputExtension}`,
|
|
|
- )
|
|
|
+ `${name}-${sizeNum}${outputExtension}`
|
|
|
+ );
|
|
|
|
|
|
await original
|
|
|
.clone()
|
|
|
.resize(sizeNum)
|
|
|
.webp({ quality: config.quality })
|
|
|
- .toFile(outputFile)
|
|
|
+ .toFile(outputFile);
|
|
|
|
|
|
- return [getCleanPath(outputFile, meta), size]
|
|
|
- }),
|
|
|
- )
|
|
|
+ return [getCleanPath(outputFile, meta), size];
|
|
|
+ })
|
|
|
+ );
|
|
|
|
|
|
- const imageRef = slugifyString(getCleanPath(path.join(filePath), meta))
|
|
|
+ const imageRef = slugifyString(getCleanPath(path.join(filePath), meta));
|
|
|
|
|
|
return {
|
|
|
paths: srcSet.map(src => src[0]),
|
|
|
detail: { srcSet, aspectRatio },
|
|
|
- ref: imageRef,
|
|
|
- }
|
|
|
+ ref: imageRef
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
export async function generateFavicons({ meta, config }) {
|
|
|
- const filePath = config.filePath
|
|
|
- const fileOutputDir = config.fileOutputDir
|
|
|
+ const filePath = config.filePath;
|
|
|
+ const fileOutputDir = config.fileOutputDir;
|
|
|
// Configuration for favicons package
|
|
|
const configuration = {
|
|
|
path: getCleanPath(fileOutputDir, meta), // Path for overriding default icons path
|
|
|
@@ -309,45 +296,45 @@ export async function generateFavicons({ meta, config }) {
|
|
|
appleStartup: true,
|
|
|
favicons: true,
|
|
|
windows: true,
|
|
|
- yandex: true,
|
|
|
- },
|
|
|
- }
|
|
|
+ yandex: true
|
|
|
+ }
|
|
|
+ };
|
|
|
try {
|
|
|
- const response = await favicons(filePath, configuration)
|
|
|
+ const response = await favicons(filePath, configuration);
|
|
|
|
|
|
// Write all generated images to disk
|
|
|
await Promise.all(
|
|
|
response.images.map(async image => {
|
|
|
- const outputPath = path.join(fileOutputDir, image.name)
|
|
|
- await writeFile(outputPath, image.contents)
|
|
|
- }),
|
|
|
- )
|
|
|
+ const outputPath = path.join(fileOutputDir, image.name);
|
|
|
+ await writeFile(outputPath, image.contents);
|
|
|
+ })
|
|
|
+ );
|
|
|
|
|
|
// Write all generated files (manifests, etc.) to disk
|
|
|
await Promise.all(
|
|
|
response.files.map(async file => {
|
|
|
- const outputPath = path.join(fileOutputDir, file.name)
|
|
|
- await writeFile(outputPath, file.contents)
|
|
|
- }),
|
|
|
- )
|
|
|
+ const outputPath = path.join(fileOutputDir, file.name);
|
|
|
+ await writeFile(outputPath, file.contents);
|
|
|
+ })
|
|
|
+ );
|
|
|
|
|
|
// Combine HTML meta tags
|
|
|
- const htmlMeta = response.html.join("\n ")
|
|
|
+ const htmlMeta = response.html.join("\n ");
|
|
|
return {
|
|
|
detail: {
|
|
|
- htmlMeta,
|
|
|
+ htmlMeta
|
|
|
},
|
|
|
paths: [
|
|
|
...response.images.map(img =>
|
|
|
- getCleanPath(path.join(fileOutputDir, img.name), meta),
|
|
|
+ getCleanPath(path.join(fileOutputDir, img.name), meta)
|
|
|
),
|
|
|
...response.files.map(file =>
|
|
|
- getCleanPath(path.join(fileOutputDir, file.name), meta),
|
|
|
- ),
|
|
|
+ getCleanPath(path.join(fileOutputDir, file.name), meta)
|
|
|
+ )
|
|
|
],
|
|
|
- ref: "metatags",
|
|
|
- }
|
|
|
+ ref: "metatags"
|
|
|
+ };
|
|
|
} catch (error) {
|
|
|
- throw new Error(`Failed to generate favicons: ${error.message}`)
|
|
|
+ throw new Error(`Failed to generate favicons: ${error.message}`);
|
|
|
}
|
|
|
}
|