Procházet zdrojové kódy

Added support for includes, early content rendering

Craig Fletcher před 5 měsíci
rodič
revize
8916410d6c
3 změnil soubory, kde provedl 144 přidání a 21 odebrání
  1. 33 18
      src/defaults.js
  2. 28 3
      src/lib.js
  3. 83 0
      src/processors.js

+ 33 - 18
src/defaults.js

@@ -4,10 +4,23 @@ import {
   generateFavicons,
   imageToWebP,
   optimiseSvg,
-  renderMarkdownWithTemplate,
+  renderTemplate,
+  renderMarkdownToHtml,
 } from "./processors.js"
 
 export const tasks = [
+  {
+    name: "images",
+    inputFiles: [{ pattern: "images/content/*.jpg" }],
+    stripPaths: ["images/content/"],
+    outputDir: "images/",
+    outputFileExtension: ".webp",
+    imageSizes: [
+      "640w", "768w", "1024w", "1366w", "1600w", "1920w", "2560w",
+    ],
+    quality: 80,
+    processor: imageToWebP,
+  },
   [
     {
       name: "styles",
@@ -25,18 +38,6 @@ export const tasks = [
       outputFileExtension: ".svg",
       processor: optimiseSvg,
     },
-    {
-      name: "images",
-      inputFiles: [{ pattern: "images/content/*.jpg" }],
-      stripPaths: ["images/content/"],
-      outputDir: "images/",
-      outputFileExtension: ".webp",
-      imageSizes: [
-        "640w", "768w", "1024w", "1366w", "1600w", "1920w", "2560w",
-      ],
-      quality: 80,
-      processor: imageToWebP,
-    },
     {
       name: "static files",
       inputFiles: [{ pattern: "static/*" }],
@@ -50,14 +51,28 @@ export const tasks = [
       outputDir: "static/meta/",
       processor: generateFavicons,
     },
+    {
+      name: "markdown",
+      inputFiles: [{ pattern: "markdown/*.md" }],
+      stripPaths: ["markdown/"],
+      outputFileExtension: ".html",
+      processor: renderMarkdownToHtml,
+    },
   ],
   {
-    name: "pages",
-    inputFiles: [{ pattern: "markdown/*.md" }],
-    stripPaths: ["markdown/"],
+    name: "includes",
+    inputFiles: [{ pattern: "includes/*.hbs" }],
+    stripPaths: ["includes/"],
     outputFileExtension: ".html",
-    processor: renderMarkdownWithTemplate,
-    defaultTemplate: "default",
+    processor: renderTemplate,
+  },
+  {
+    name: "stateSelectorTest",
+    stateSelectors: ["resources.markdown"],
+    skipCache: true,
+    logLevel: "debug",
+    processor: renderTemplate,
+    writeOut: true,
     templateDirs: ["templates/", "~/.rhedyn/templates/"],
   },
 ]

+ 28 - 3
src/lib.js

@@ -9,6 +9,7 @@ import {
   removeBasePaths,
   removeCwd,
   replaceFileExtension,
+  getValueAtPath,
 } from "./util.js"
 import path from "path"
 import process from "node:process"
@@ -58,7 +59,7 @@ async function runTask({ meta, config, jobId }) {
     : { proxy: stateObject }
 
   const {
-    detail,
+    detail = {},
     paths = [],
     deps: processorDeps,
     ref,
@@ -76,6 +77,7 @@ async function runTask({ meta, config, jobId }) {
     const processorStateDeps = processorDeps?.state || []
     const configPathDeps = config.deps?.paths || []
     const configStateDeps = config.deps?.state || []
+    const stateSelectors = config.stateSelectors || []
     await updateCache(
       meta.opts.cacheDir,
       jobId,
@@ -83,7 +85,7 @@ async function runTask({ meta, config, jobId }) {
         ...configPathDeps, ...processorPathDeps, config?.filePath,
       ].filter(item => !!item)),
       [
-        ...configStateDeps, ...processorStateDeps, ...(state?.accessed || []),
+        ...configStateDeps, ...stateSelectors, ...processorStateDeps, ...(state?.accessed || []),
       ].filter(item => !!item),
       taskResult,
       cache.updates,
@@ -116,7 +118,26 @@ async function expandFileTask(patternsToInclude, config, meta) {
       }
       return runTask({ meta, config: jobConfig, jobId: `${config.name} @ ${filePath}` })
     }),
-  )}
+  )
+}
+
+async function expandStateTask(stateToExpand, config, meta) {
+  const stateToProcess = stateToExpand.map(property => {
+    const values = getValueAtPath(meta, property)
+    const expandedValues = Array.isArray(values) ? values : Object.values(values)
+    return expandedValues.map((value, index) => ({ property, index, value }))
+  }).flat()
+
+  return await Promise.all(
+    stateToProcess.map(async (stateJob) => {
+      const jobConfig = {
+        ...config,
+        ...stateJob.value.detail,
+      }
+      return runTask({ meta, config: jobConfig, jobId: `${config.name} @ ${stateJob.property}:${stateJob.index}` })
+    }),
+  )
+}
 
 export async function expandAndRunTask(meta, config) {
   const includes = meta.opts?.include?.[config.name] || []
@@ -126,6 +147,10 @@ export async function expandAndRunTask(meta, config) {
     return expandFileTask(patternsToInclude, config, meta)
   }
 
+  if (config.stateSelectors) {
+    return expandStateTask(config.stateSelectors, config, meta)
+  }
+
   const jobId = config.jobId || config.name
   const taskResult = await runTask({ meta, config, jobId })
   return [taskResult]

+ 83 - 0
src/processors.js

@@ -55,7 +55,90 @@ function createMarkdownRenderer(meta) {
       },
     })
 }
+async function findTemplatePath(templateDirs, templateName) {
+  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")
+    templateCache.set(templatePath, {
+      path: templatePath,
+      renderer: handlebars.compile(templateContent),
+    })
+  }
+  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)
+
+  const template = await getTemplate(templatePath)
+  const html = template.renderer({
+    ...meta,
+    href,
+    ...config,
+  })
+  if (config.writeOut) {
+    const minifiedHtml = await minify(html, {
+      collapseWhitespace: true,
+      removeComments: true,
+      removeRedundantAttributes: true,
+      removeEmptyAttributes: true,
+      minifyCSS: true,
+      minifyJS: true,
+    })
 
+    await writeFile(fileOutputPath, minifiedHtml)
+    return {
+      detail: { html },
+      deps: {
+        paths: [template.path],
+      },
+      paths: [fileOutputPath],
+      ref: slugifyString(href),
+    }
+  }
+
+  return {
+    detail: { html },
+    deps: {
+      paths: [template.path],
+    },
+    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)
+
+  const renderer = createMarkdownRenderer(meta)
+  const html = renderer(markdown)
+
+  return {
+    detail: { ...data, href, content: html, fileOutputPath },
+    ref: slugifyString(filePath),
+  }
+}
 export async function renderMarkdownWithTemplate({
   config,
   meta,