akasharender
Version:
Rendering support for generating static HTML websites or EPUB eBooks
685 lines • 103 kB
JavaScript
/**
*
* Copyright 2014-2025 David Herron
*
* This file is part of AkashaCMS (http://akashacms.com/).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import path from 'node:path';
import { promises as fsp } from 'node:fs';
import util from 'node:util';
import * as data from './data.js';
import mahabhuta, { FilesystemPerfDataStore } from 'mahabhuta';
import fastq from 'fastq';
import { performance } from 'node:perf_hooks';
function createRenderingData(config, docInfo) {
const ret = {
config,
renderFirstContext: {
fspath: docInfo.vpath,
content: docInfo.docContent,
body: docInfo.docBody,
metadata: docInfo.metadata
},
renderer: config.findRendererPath(docInfo.vpath),
docInfo,
vpath: docInfo.vpath,
renderPath: docInfo.renderPath,
mountPoint: docInfo.mountPoint,
renderTo: config.renderTo,
results: {
vpath: docInfo.vpath,
renderPath: docInfo.renderPath,
renderStart: performance.now(),
errors: new Array()
}
};
if (ret.renderer) {
ret.results.renderFormat = ret.renderer.renderFormat(ret.renderFirstContext);
}
return ret;
}
//////////////////////////////////////////////////////////
/**
* The core part of rendering content using a renderer.
* This function looks for the renderer, and if none is
* found it simply returns. It then does a little setup
* to the metadata object, and calls the render function
*
* @param config - AkashaCMS Configuration
* @param rc - RenderingContext for use with Renderers
* @returns
*/
export async function renderContent(config, rc) {
// console.log(`renderContent `, rc);
const renderer = config.findRendererPath(rc.fspath);
if (!renderer) {
return {
rendererName: undefined,
format: undefined,
rendered: rc.body
};
}
// Add necessary items to the metadata
rc.metadata.config = config;
rc.metadata.partial = (fname, metadata) => {
return config.akasha.partial(config, fname, metadata);
};
rc.metadata.partialSync = (fname, metadata) => {
return config.akasha.partialSync(config, fname, metadata);
};
rc.metadata.akasha = config.akasha;
rc.metadata.plugin = config.plugin;
// Render the primary content
let docrendered = await renderer.render(rc);
// console.log(`renderContent rendered=`, docrendered);
return {
rendererName: renderer.name,
format: renderer.renderFormat(rc),
rendered: docrendered
};
}
// This function and the next exist solely to
// simplify renderDocument.
async function writeCSStoOutput(config, docInfo, docRendered, renderStart) {
const renderToFpath = path.join(config.renderTo, docInfo.renderPath);
const renderToDir = path.dirname(renderToFpath);
await fsp.mkdir(renderToDir, {
recursive: true
});
await fsp.writeFile(renderToFpath, docRendered, 'utf8');
const renderEndRendered = new Date();
await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "RENDERED", renderStart);
return `CSS ${docInfo.vpath} ==> ${docInfo.renderPath} (${(renderEndRendered.valueOf() - renderStart.valueOf()) / 1000} seconds)\n${await data.data4file(docInfo.mountPoint, docInfo.vpath)}`;
}
async function copyAssetToOutput(config, docInfo, docRendered, renderStart) {
const renderToFpath = path.join(config.renderTo, docInfo.renderPath);
const renderToDir = path.dirname(renderToFpath);
await fsp.mkdir(renderToDir, {
recursive: true
});
await fsp.copyFile(docInfo.fspath, renderToFpath);
// console.log(`COPIED ${docInfo.path} ==> ${renderToFpath}`);
const renderEndCopied = new Date();
return `COPY ${docInfo.vpath} ==> ${renderToFpath} (${(renderEndCopied.valueOf() - renderStart.valueOf()) / 1000} seconds)`;
}
function copyProperties(dest, src, exceptLayout) {
for (var yprop in src) {
if (exceptLayout && yprop === 'layout')
continue;
dest[yprop] = src[yprop];
}
return dest;
}
async function renderCSSFile(ret) {
try {
ret.results.renderFormat = 'CSS';
ret.results.renderFirstStart = performance.now();
// Render the CSS content
ret.renderedFirst = await ret.renderer.render(ret.renderFirstContext);
ret.results.renderFirstEnd = performance.now();
// Write the rendered CSS to output
const renderDest = path.join(ret.config.renderTo, ret.docInfo.renderPath);
await fsp.mkdir(path.dirname(renderDest), { recursive: true });
await fsp.writeFile(renderDest, ret.renderedFirst, 'utf-8');
ret.results.renderFirstEnd = performance.now();
ret.results.renderEnd = performance.now();
}
catch (error) {
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error instanceof Error ? error : new Error(String(error)));
}
// Calculate elapsed times
if (ret.results.renderFirstStart && ret.results.renderFirstEnd) {
ret.results.renderFirstElapsed = ret.results.renderFirstEnd - ret.results.renderFirstStart;
}
ret.results.renderLayoutElapsed = 0;
ret.results.renderMahaElapsed = 0;
if (ret.results.renderStart && ret.results.renderEnd) {
ret.results.renderTotalElapsed = ret.results.renderEnd - ret.results.renderStart;
}
// console.log(`renderCSSFile ${ret.vpath}`, ret);
return ret;
}
async function copyAssetFile(ret) {
try {
ret.results.renderFormat = 'COPY';
ret.results.renderFirstStart = performance.now();
// Copy the asset file to output directory
const renderDest = path.join(ret.config.renderTo, ret.docInfo.renderPath);
await fsp.mkdir(path.dirname(renderDest), { recursive: true });
await fsp.copyFile(ret.docInfo.fspath, renderDest);
ret.results.renderFirstEnd = performance.now();
ret.results.renderEnd = performance.now();
}
catch (error) {
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error instanceof Error ? error : new Error(String(error)));
}
// Calculate elapsed times
if (ret.results.renderFirstStart && ret.results.renderFirstEnd) {
ret.results.renderFirstElapsed = ret.results.renderFirstEnd - ret.results.renderFirstStart;
}
ret.results.renderLayoutElapsed = 0;
ret.results.renderMahaElapsed = 0;
if (ret.results.renderStart && ret.results.renderEnd) {
ret.results.renderTotalElapsed = ret.results.renderEnd - ret.results.renderStart;
}
// Use this to verify error handling
// ret.results.errors.push(new Error(`Random error`));
// console.log(`copyAssetFile ${ret.vpath}`, ret);
return ret;
}
/**
* Attempt to rewrite renderDocument with cleaner code, and a
* different method for collecting performance/timing data.
*
* The existing renderDocument is messy and hard to understand.
* Goal: make it more straight-forward, easy to understand.
* Goal: store all data in a well designed object
*
* The existing performance measurements are imprecise by using
* the Date object, and by not computing the elapsed time of
* each segment. Instead, it computs the time from the start
* for each segment, which isn't useful. We want to see the
* elapsed time.
*
* For precise time measures this uses the Node.js performance
* hooks to get accurate timestamps.
*
* This code has not been executed as yet.
*
* Tasks:
* * TODO Implement CSS renderFormat
* * TODO Implement the != HTML renderFormat
* * TODO Test and fix bugs
*
* @param config
* @param docInfo
* @returns
*/
export async function renderDocument2(config, docInfo) {
// Create the master object to hold all data
const ret = createRenderingData(config, docInfo);
// Peel off to mode-specific functions
if (ret?.renderer?.renderFormat(ret.renderFirstContext) === 'CSS') {
const cssResult = await renderCSSFile(ret);
return cssResult.results;
}
else if (!ret.renderer
|| (ret.renderer.renderFormat(ret.renderFirstContext) !== 'HTML')) {
const assetResult = await copyAssetFile(ret);
return assetResult.results;
}
// Otherwise it is HTML
// This is where we render the content, then render that
// into the layout (if one exists), then run Mahabhuta.
// These functions are duplicates between the first
// two stages. Save a couple microseconds by instantiating
// the functions once.
const doPartial = (fname, metadata) => {
return config.akasha.partial(config, fname, metadata);
};
const doPartialSync = (fname, metadata) => {
return config.akasha.partialSync(config, fname, metadata);
};
// First Render
ret.results.renderFirstStart = performance.now();
try {
// Add necessary items to the metadata
ret.renderFirstContext.metadata.config = config;
ret.renderFirstContext.metadata.partial = doPartial;
ret.renderFirstContext.metadata.partialSync = doPartialSync;
ret.renderFirstContext.metadata.akasha = config.akasha;
ret.renderFirstContext.metadata.plugin = config.plugin;
// Render the primary content
ret.renderedFirst = await ret.renderer.render(ret.renderFirstContext);
}
catch (error) {
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error instanceof Error ? error : new Error(String(error)));
// Use empty string as fallback if rendering fails
ret.renderedFirst = '';
}
ret.results.renderFirstEnd = performance.now();
// END First Render
// Layout Render
ret.results.renderLayoutStart = performance.now();
if (ret?.docInfo?.metadata?.layout) {
try {
const layouts = config.akasha.filecache.layoutsCache;
// await layouts.isReady();
let found = await layouts.find(ret.docInfo.metadata.layout);
if (!found) {
const error = new Error(`No layout found in ${util.inspect(ret.config.layoutDirs)} for ${ret?.docInfo?.metadata?.layout} in file ${ret.docInfo.vpath}`);
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error);
// Skip layout rendering, use first render result
ret.renderedLayout = ret.renderedFirst;
}
else {
const renderer = config.findRendererPath(ret.docInfo.metadata.layout);
ret.renderLayoutContext = {
fspath: ret.docInfo.metadata.layout,
content: found.docContent,
body: found.docBody,
metadata: {}
};
ret.renderLayoutContext.metadata
= copyProperties(ret.renderLayoutContext.metadata, found.metadata, false);
ret.renderLayoutContext.metadata
= copyProperties(ret.renderLayoutContext.metadata, ret.docInfo.metadata, true);
ret.renderLayoutContext.metadata.content = ret.renderedFirst;
ret.renderLayoutContext.metadata.config = config;
ret.renderLayoutContext.metadata.partial = doPartial;
ret.renderLayoutContext.metadata.partialSync = doPartialSync;
ret.renderLayoutContext.metadata.akasha = config.akasha;
ret.renderLayoutContext.metadata.plugin = config.plugin;
// Render the layout content
ret.renderedLayout = await renderer.render(ret.renderLayoutContext);
}
}
catch (e) {
const error = new Error(`Error rendering ${docInfo.vpath} with ${docInfo?.metadata?.layout} ${e.stack ? e.stack : e}`);
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error);
// Use first render result as fallback
ret.renderedLayout = ret.renderedFirst;
}
}
ret.results.renderLayoutEnd = performance.now();
// END Layout Render
// Mahabhuta
ret.results.renderMahaStart = performance.now();
ret.renderMahaContext = {
fspath: ret.docInfo.metadata.layout,
content: ret.renderedLayout
? ret.renderedLayout : ret.renderedFirst,
body: ret.renderedLayout
? ret.renderedLayout : ret.renderedFirst,
metadata: {}
};
ret.renderMahaContext.metadata
= copyProperties(ret.renderMahaContext.metadata, ret.docInfo.metadata, false);
try {
if (ret.docInfo?.metadata?.config?.mahabhutaConfig) {
mahabhuta.config(ret.docInfo?.metadata?.config?.mahabhutaConfig);
}
ret.renderedMaha = await mahabhuta.processAsync(ret.renderMahaContext.content, ret.renderMahaContext.metadata, ret.config.mahafuncs,
// For performance collection
config.perfDataDir
? new FilesystemPerfDataStore(config.perfDataDir)
: undefined, config.perfDataDir
? ret.docInfo.vpath
: undefined);
}
catch (e2) {
const error = new Error(`Error with Mahabhuta ${ret.docInfo.vpath} with ${ret.docInfo?.metadata?.layout} ${e2.stack ? e2.stack : e2}`);
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error);
// Use layout result or first render as fallback
ret.renderedMaha = ret.renderMahaContext.content;
}
ret.results.renderMahaEnd = performance.now();
// END Mahabhuta
try {
const renderDest = path.join(ret.config.renderTo, ret.docInfo.renderPath);
await fsp.mkdir(path.dirname(renderDest), {
recursive: true
});
await fsp.writeFile(renderDest, ret.renderedMaha, 'utf-8');
}
catch (error) {
ret.results.errors = ret.results.errors || [];
ret.results.errors.push(error instanceof Error ? error : new Error(String(error)));
}
ret.results.renderEnd = performance.now();
// Calculate elapsed times
if (ret.results.renderFirstStart && ret.results.renderFirstEnd) {
ret.results.renderFirstElapsed = ret.results.renderFirstEnd - ret.results.renderFirstStart;
}
if (ret.results.renderLayoutStart && ret.results.renderLayoutEnd) {
ret.results.renderLayoutElapsed = ret.results.renderLayoutEnd - ret.results.renderLayoutStart;
}
if (ret.results.renderMahaStart && ret.results.renderMahaEnd) {
ret.results.renderMahaElapsed = ret.results.renderMahaEnd - ret.results.renderMahaStart;
}
if (ret.results.renderStart && ret.results.renderEnd) {
ret.results.renderTotalElapsed = ret.results.renderEnd - ret.results.renderStart;
}
// console.log(`renderDocument2 ${ret.vpath}`, ret);
return ret.results;
}
/**
* Render a document, accounting for the main content,
* a layout template (if any), and Mahabhuta (if the content
* output is HTML). This also handles rendering other types
* of content such as LESS CSS files.
*
* @param config
* @param docInfo
* @returns
*/
export async function renderDocument(config, docInfo) {
const renderStart = new Date();
// Render the main content
if (typeof docInfo.docContent !== 'string'
|| typeof docInfo.docBody !== 'string') {
// console.warn(`No content to render for `, docInfo);
}
const rc = {
fspath: docInfo.vpath,
content: docInfo.docContent,
body: docInfo.docBody,
metadata: docInfo.metadata
};
// console.log(`renderDocument context= ${rc.fspath}`, rc);
let docFormat; // Knowing the format
let docRendered;
try {
const result = await renderContent(config, rc);
docFormat = result.format;
docRendered = result.rendered;
}
catch (err) {
console.error(`Error rendering ${docInfo.vpath} ${(err.stack ? err.stack : err)}`);
throw new Error(`Error rendering ${docInfo.vpath} ${(err.stack ? err.stack : err)}`);
}
// console.log(`renderDocument ${docFormat} ${docInfo.vpath} rendered=`, docRendered);
await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "FIRST RENDER", renderStart);
// Handle these cases up front so that the remaining
// code can be cleaner and focus on HTML layout rendering.
if (docFormat === 'CSS') {
try {
return writeCSStoOutput(config, docInfo, docRendered, renderStart);
}
catch (err) {
console.error(`in RENDER CSS branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`);
throw new Error(`in RENDER CSS branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`);
}
}
if (docFormat !== 'HTML') {
try {
copyAssetToOutput(config, docInfo, docRendered, renderStart);
}
catch (err) {
console.error(`in copy branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`);
throw new Error(`in copy branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`);
}
}
// Render the main content into a layout template,
// if one is specified
let layoutFormat;
let layoutRendered;
let result;
// console.log(`renderDocument layout ${docInfo?.metadata?.layout} docMetadata ${util.inspect(docInfo.docMetadata)} metadata ${util.inspect(docInfo.metadata)}`);
if (docInfo?.metadata?.layout) {
const layouts = config.akasha.filecache.layoutsCache;
// await layouts.isReady();
let found = await layouts.find(docInfo.metadata.layout);
if (!found) {
throw new Error(`No layout found in ${util.inspect(config.layoutDirs)} for ${docInfo?.metadata?.layout} in file ${docInfo.vpath}`);
}
const rcLayout = {
fspath: docInfo.metadata.layout,
content: found.docContent,
body: found.docBody,
metadata: {}
};
rcLayout.metadata
= copyProperties(rcLayout.metadata, found.metadata, false);
rcLayout.metadata
= copyProperties(rcLayout.metadata, docInfo.metadata, true);
rcLayout.metadata.content = docRendered;
try {
result
= await renderContent(config, rcLayout);
layoutFormat = result.format;
layoutRendered = result.rendered;
}
catch (e) {
let ee = new Error(`Error rendering ${docInfo.vpath} with ${docInfo?.metadata?.layout} ${e.stack ? e.stack : e}`);
console.error(ee);
throw ee;
}
}
else {
layoutFormat = docFormat;
layoutRendered = docRendered;
}
// console.log(`renderDocument ${docInfo.vpath} after layout render format ${layoutFormat} `, result);
const renderSecondRender = new Date();
await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "SECOND RENDER", renderStart);
// Next step is to run Mahabhuta on the rendered content
// Of course, Mahabhuta is not appropriate for everything
// because not everything is HTML
try {
const mahametadata = copyProperties({}, docInfo.metadata, false);
mahametadata.content = docRendered;
if (docInfo?.metadata?.config?.mahabhutaConfig) {
mahabhuta.config(docInfo?.metadata?.config?.mahabhutaConfig);
}
// console.log(`mahametadata`, mahametadata);
layoutRendered = await mahabhuta.processAsync(layoutRendered, mahametadata, config.mahafuncs);
// OLD docrendered = await this.maharun(layoutrendered, docdata, config.mahafuncs);
}
catch (e2) {
let eee = new Error(`Error with Mahabhuta ${docInfo.vpath} with ${docInfo?.metadata?.layout} ${e2.stack ? e2.stack : e2}`);
console.error(eee);
throw eee;
}
await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "MAHABHUTA", renderStart);
const renderDest = path.join(config.renderTo, docInfo.renderPath);
await fsp.mkdir(path.dirname(renderDest), {
recursive: true
});
await fsp.writeFile(renderDest, layoutRendered, 'utf-8');
// console.log(`RENDERED ${renderer.name} ${docInfo.path} ==> ${renderToFpath}`);
const renderEndRendered = new Date();
await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "RENDERED", renderStart);
return `${layoutFormat} ${docInfo.vpath} ==> ${docInfo.renderPath} (${(renderEndRendered.valueOf() - renderStart.valueOf()) / 1000} seconds)\n${await data.data4file(docInfo.mountPoint, docInfo.vpath)}`;
}
/**
* Render all the documents in a site, limiting
* the number of simultaneous rendering tasks
* to the number in config.concurrency.
*
* @param config
* @returns
*/
export async function render(config) {
const documents = config.akasha.filecache.documentsCache;
// await documents.isReady();
// console.log('CALLING config.hookBeforeSiteRendered');
await config.hookBeforeSiteRendered();
// 1. Gather list of files from RenderFileCache
const filez = await documents.paths();
// console.log(`newerrender filez ${filez.length}`);
// 2. Exclude any that we want to ignore
const filez2 = [];
for (let entry of filez) {
let include = true;
// console.log(entry);
let stats;
try {
stats = await fsp.stat(entry.fspath);
}
catch (err) {
stats = undefined;
}
if (!entry)
include = false;
else if (!stats || stats.isDirectory())
include = false;
// This should arise using an ignore clause
// else if (path.basename(entry.vpath) === '.DS_Store') include = false;
// else if (path.basename(entry.vpath) === '.placeholder') include = false;
if (include) {
// The queue is an array of tuples containing the
// config object and the path string
filez2.push({
config: config,
info: await documents.find(entry.vpath)
});
}
}
// console.log(`newerrender filez2 after ignore ${filez2.length}`);
// 3. Make a fastq to process using renderDocument,
// pushing results to the results array
// This sets up the queue processor
// The concurrency setting lets us process documents
// in parallel while limiting total impact.
const queue = fastq.promise(
// This function is invoked for each entry in the
// queue. It handles rendering the queue
// The queue has config objects and path strings
// which is exactly what's required by
// renderDocument
async function renderDocumentInQueue(entry) {
// console.log(`renderDocumentInQueue ${entry.info.vpath}`);
try {
let result = await renderDocument(entry.config, entry.info);
// console.log(`DONE renderDocumentInQueue ${entry.info.vpath}`, result);
return { result };
}
catch (error) {
// console.log(`ERROR renderDocumentInQueue ${entry.info.vpath}`, error.stack);
return { error };
}
}, config.concurrency);
// queue.push returns a Promise that's fulfilled when
// the task finishes.
// Hence waitFor is an array of Promises.
const waitFor = [];
for (let entry of filez2) {
waitFor.push(queue.push(entry));
}
// This automatically waits for all those
// Promises to resolve, while making the results
// array contain results.
const results = [];
for (let result of waitFor) {
results.push(await result);
}
// 4. Invoke hookSiteRendered
try {
// console.log('Invoking hookSiteRendered');
await config.hookSiteRendered();
}
catch (e) {
console.error(e.stack);
throw new Error(`hookSiteRendered failed because ${e}`);
}
// 5. return results
return results;
}
;
/**
* Render all the documents in a site using renderDocument2,
* limiting the number of simultaneous rendering tasks
* to the number in config.concurrency.
*
* Returns structured RenderingResults data instead of text strings.
*
* @param config
* @returns Array of RenderingResults with performance and error data
*/
export async function render2(config) {
const documents = config.akasha.filecache.documentsCache;
// await documents.isReady();
// console.log('CALLING config.hookBeforeSiteRendered');
await config.hookBeforeSiteRendered();
// 1. Gather list of files from RenderFileCache
const filez = await documents.paths();
// console.log(`render2 filez ${filez.length}`);
// 2. Exclude any that we want to ignore
const filez2 = [];
for (let entry of filez) {
let include = true;
// console.log(entry);
let stats;
try {
stats = await fsp.stat(entry.fspath);
}
catch (err) {
stats = undefined;
}
if (!entry)
include = false;
else if (!stats || stats.isDirectory())
include = false;
// This should arise using an ignore clause
// else if (path.basename(entry.vpath) === '.DS_Store') include = false;
// else if (path.basename(entry.vpath) === '.placeholder') include = false;
if (include) {
// The queue is an array of tuples containing the
// config object and the path string
filez2.push({
config: config,
info: await documents.find(entry.vpath)
});
}
}
// console.log(`render2 filez2 after ignore ${filez2.length}`);
// 3. Make a fastq to process using renderDocument2,
// pushing results to the results array
// This sets up the queue processor
// The concurrency setting lets us process documents
// in parallel while limiting total impact.
const queue = fastq.promise(
// This function is invoked for each entry in the
// queue. It handles rendering the queue
// The queue has config objects and path strings
// which is exactly what's required by
// renderDocument2
async function renderDocument2InQueue(entry) {
// console.log(`renderDocument2InQueue ${entry.info.vpath}`);
try {
let result = await renderDocument2(entry.config, entry.info);
// console.log(`DONE renderDocument2InQueue ${entry.info.vpath}`);
return result;
}
catch (error) {
console.log(`ERROR renderDocument2InQueue ${entry.info.vpath}`, error.stack);
return undefined;
}
}, config.concurrency);
// queue.push returns a Promise that's fulfilled when
// the task finishes.
// Hence waitFor is an array of Promises.
const waitFor = [];
for (let entry of filez2) {
waitFor.push(queue.push(entry));
}
// This automatically waits for all those
// Promises to resolve, while making the results
// array contain results.
const results = [];
for (let result of waitFor) {
results.push(await result);
}
// 4. Invoke hookSiteRendered
try {
// console.log('Invoking hookSiteRendered');
await config.hookSiteRendered();
}
catch (e) {
console.error(e.stack);
throw new Error(`hookSiteRendered failed because ${e}`);
}
// 5. return results
return results;
}
;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vbGliL3JlbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFFSCxPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxFQUFFLFFBQVEsSUFBSSxHQUFHLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDMUMsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sU0FBUyxFQUFFLEVBQ2QsdUJBQXVCLEVBQzFCLE1BQU0sV0FBVyxDQUFDO0FBRW5CLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQVUxQixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUE0RDlDLFNBQVMsbUJBQW1CLENBQ3hCLE1BQXFCLEVBQ3JCLE9BQU87SUFFUCxNQUFNLEdBQUcsR0FBa0I7UUFDdkIsTUFBTTtRQUVOLGtCQUFrQixFQUFvQjtZQUNsQyxNQUFNLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDckIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxVQUFVO1lBQzNCLElBQUksRUFBRSxPQUFPLENBQUMsT0FBTztZQUNyQixRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7U0FDN0I7UUFFRCxRQUFRLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUNqQixPQUFPLENBQUMsS0FBSyxDQUM1QjtRQUVELE9BQU87UUFDUCxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7UUFDcEIsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVO1FBQzlCLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVTtRQUM5QixRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVE7UUFFekIsT0FBTyxFQUFvQjtZQUN2QixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVO1lB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