akasharender
Version:
Rendering support for generating static HTML websites or EPUB eBooks
558 lines • 82 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 from 'mahabhuta';
import fastq from 'fastq';
//////////////////////////////////////////////////////////
/**
* Where renderDocument is meant for a document on disk
* and indexed by a DocumentsFileCache instance, this
* function is meant for documents created from in-memory
* data. For instance, the tagged-content plugin generates
* tag pages listing links to documents based on their tag.
* These pages are instantiate out of data rather than
* existing on-disk.
*
* Required data:
* * Blank page - with frontmatter including a "layout" template reference
* * File-name to use for virtual page, which also determines the rendered output file
* * Metadata derived from the frontmatter and filled with other stuff including the data to render into the page,
*
* @param config
* @param docInfo
*/
export async function renderVirtualDocument(config, docInfo) {
const renderer = config.findRendererPath(docInfo.vpath);
const rc = renderer.parseMetadata({
fspath: docInfo.vpath,
content: docInfo.document,
metadata: {}
});
// 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);
// If there is a layout template, render that
// template passing the rendered primary content
let layoutrendered;
if (rc.metadata.layout) {
const layouts = config.akasha
.filecache.layoutsCache;
const layoutInfo = await layouts.find(rc.metadata.layout);
if (!layoutInfo) {
throw new Error(`No layout found in ${util.inspect(config.layoutDirs)} for ${rc.metadata.layout} in file ${docInfo.vpath}`);
}
// Build the metadata for the layout rendering
let layoutmetadata = {};
for (var yprop in layoutInfo.metadata) {
layoutmetadata[yprop] = layoutInfo.metadata[yprop];
}
for (var yprop in rc.metadata) {
if (yprop !== 'layout') {
layoutmetadata[yprop] = rc.metadata[yprop];
}
}
// Make the first rendering available
// in the metadata as "content" variable
layoutmetadata.content = docrendered;
const renderer = config.findRendererPath(rc.metadata.layout);
if (!renderer) {
throw new Error(`No renderer for ${layoutmetadata.layout} in file ${docInfo.vpath}`);
;
}
const layoutContext = {
fspath: layoutInfo.fspath,
content: layoutInfo.docContent,
body: layoutInfo.docBody,
metadata: layoutmetadata
};
layoutrendered
= await renderer.render(layoutContext);
}
// For HTML rendering, fun Mahabhuta functions
const format = renderer.renderFormat(rc);
const doMahabhuta = (format === 'HTML');
if (doMahabhuta) {
const mahametadata = {};
for (var yprop in rc.metadata) {
mahametadata[yprop] = rc.metadata[yprop];
}
mahametadata.content = docrendered;
if (rc.metadata.config.mahabhutaConfig) {
mahabhuta.config(rc.metadata.config.mahabhutaConfig);
}
layoutrendered = await mahabhuta.processAsync(typeof layoutrendered === 'string'
? layoutrendered
: docrendered, mahametadata, config.mahafuncs);
}
// layoutrendered gets the final rendering
}
//////////////////////////////////////////////////////////
/**
* 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
};
}
/**
* 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
const rc = {
fspath: docInfo.vpath,
content: docInfo.docContent,
body: docInfo.docBody,
metadata: docInfo.metadata
};
// console.log(`renderDocument context= ${rc.fspath}`)
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 ${docInfo.vpath} rendered=`, docRendered);
await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "FIRST RENDER", renderStart);
// Render the main content into a layout template,
// if one is specified
let layoutFormat;
let layoutRendered;
// 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: {}
};
for (var yprop in found.metadata) {
rcLayout.metadata[yprop] = found.metadata[yprop];
}
for (var yprop in docInfo.metadata) {
if (yprop !== 'layout') {
rcLayout.metadata[yprop] = docInfo.metadata[yprop];
}
}
rcLayout.metadata.content = docRendered;
try {
const 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} `);
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
const doMahabhuta = (layoutFormat === 'HTML');
if (doMahabhuta) {
try {
const mahametadata = {};
for (var yprop in docInfo.metadata) {
mahametadata[yprop] = docInfo.metadata[yprop];
}
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)}`;
}
else if (layoutFormat === 'CSS') {
try {
const renderToFpath = path.join(config.renderTo, docInfo.renderPath);
const renderToDir = path.dirname(renderToFpath);
await fsp.mkdir(renderToDir, {
recursive: true
});
await fsp.writeFile(renderToFpath, layoutRendered, 'utf8');
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)}`;
}
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}`);
}
}
else {
try {
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)`;
}
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}`);
}
}
}
// export async function renderDocument2(config, docInfo) {
// const renderStart = new Date();
// const renderBaseMetadata = docInfo.baseMetadata;
// // console.log(`renderDocument `, docInfo);
// const renderer = config.findRendererPath(docInfo.vpath);
// if (renderer) {
// // console.log(`ABOUT TO RENDER ${renderer.name} ${docInfo.vpath} ==> ${renderToFpath}`);
// try {
// // Set up required metadata values
// docInfo.metadata.config = config;
// docInfo.metadata.partial = (fname, metadata) => {
// return config.akasha.partial(config, fname, metadata);
// }
// docInfo.metadata.partialSync = (fname, metadata) => {
// return config.akasha.partialSync(config, fname, metadata);
// }
// docInfo.metadata.akasha = config.akasha;
// docInfo.metadata.plugin = config.plugin;
// // Render the document - output goes to "docrendered"
// let docrendered;
// try {
// const context = {
// fspath: docInfo.fspath,
// content: docInfo.docContent,
// body: docInfo.docBody,
// metadata: docInfo.metadata
// };
// // console.log({
// // title: 'before DOCUMENT renderer.render',
// // fspath: docInfo.fspath,
// // content: docInfo.docContent,
// // body: docInfo.docBody,
// // metadata: docInfo.metadata,
// // info: docInfo.info
// // });
// if (typeof context.content !== 'string'
// || typeof context.body !== 'string') {
// // console.warn(`render should fail for ${util.inspect(context)} `, docInfo);
// }
// docrendered = await renderer.render(context);
// } 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(docrendered);
// await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo,
// "FIRST RENDER", renderStart);
// // There may be a layout, which will be in metadata.layout
// // If so, get the layout file
// // Construct a new metadata -- and it's "content" is
// // set from docrendered
// // Then render the document into "layoutrendered"
// // Otherwise cpy docrendered to layoutrendered
// let layoutrendered;
// 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}`);
// }
// let layoutmetadata: any = {};
// for (var yprop in found.metadata) {
// layoutmetadata[yprop] = found.metadata[yprop];
// }
// for (var yprop in docInfo.metadata) {
// if (yprop !== 'layout') {
// layoutmetadata[yprop] = docInfo.metadata[yprop];
// }
// }
// layoutmetadata.content = docrendered;
// const renderer = config.findRendererPath(docInfo.metadata.layout);
// if (!renderer) {
// throw new Error(`No renderer for ${layoutmetadata.layout} in file ${docInfo.vpath}`);;
// }
// const context = {
// fspath: found.fspath,
// content: found.docContent,
// body: found.docBody,
// metadata: layoutmetadata
// };
// if (typeof context.content !== 'string'
// || typeof context.body !== 'string') {
// throw new Error(`renderDocument LAYOUT RENDERING for ${docInfo.vpath} with layout ${docInfo.metadata.layout} has no context.content or context.body to which to render the content ${util.inspect(context)} ${util.inspect(found)}`);
// }
// // console.log(`renderDocument `, found);
// // console.log(`renderDocument `, layoutmetadata);
// try {
// layoutrendered
// = await renderer.render(context);
// } 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 {
// layoutrendered = docrendered;
// }
// 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
// const format = renderer.renderFormat({ fspath: docInfo.fspath });
// const doMahabhuta = (format === 'HTML') ? true :false;
// if (doMahabhuta) {
// try {
// const mahametadata: any = {};
// for (var yprop in docInfo.metadata) {
// mahametadata[yprop] = docInfo.metadata[yprop];
// }
// 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 `${renderer.name} ${docInfo.vpath} ==> ${docInfo.renderPath} (${(renderEndRendered.valueOf() - renderStart.valueOf()) / 1000} seconds)\n${await data.data4file(docInfo.mountPoint, docInfo.vpath)}`;
// } catch (err) {
// console.error(`in renderer branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`);
// throw new Error(`in renderer branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`);
// }
// } else {
// // console.log(`COPYING ${docInfo.path} ==> ${renderToFpath}`);
// try {
// 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)`;
// } 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 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;
}
;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vbGliL3JlbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFFSCxPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxFQUFFLFFBQVEsSUFBSSxHQUFHLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDMUMsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sU0FBUyxNQUFNLFdBQVcsQ0FBQztBQUVsQyxPQUFPLEtBQUssTUFBTSxPQUFPLENBQUM7QUFRMUIsMERBQTBEO0FBRTFEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxxQkFBcUIsQ0FDdkMsTUFBcUIsRUFDckIsT0FNQztJQUlELE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FDaEMsT0FBTyxDQUFDLEtBQUssQ0FDcEIsQ0FBQztJQUVGLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUM7UUFDOUIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxLQUFLO1FBQ3JCLE9BQU8sRUFBRSxPQUFPLENBQUMsUUFBUTtRQUN6QixRQUFRLEVBQUUsRUFBRTtLQUNmLENBQUMsQ0FBQztJQUVILHNDQUFzQztJQUN0QyxFQUFFLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDNUIsRUFBRSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLEVBQUU7UUFDdEMsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzFELENBQUMsQ0FBQztJQUNGLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxHQUFHLENBQUMsS0FB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Q29weXJpZ2h0IDIwMTQtMjAyNSBEYXZpZCBIZXJyb25cbiAqXG4gKiBUaGlzIGZpbGUgaXMgcGFydCBvZiBBa2FzaGFDTVMgKGh0dHA6Ly9ha2FzaGFjbXMuY29tLykuXG4gKlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiAgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqICBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiAgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiAgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBwcm9taXNlcyBhcyBmc3AgfSBmcm9tICdub2RlOmZzJztcbmltcG9ydCB1dGlsIGZyb20gJ25vZGU6dXRpbCc7XG5pbXBvcnQgKiBhcyBkYXRhIGZyb20gJy4vZGF0YS5qcyc7XG5pbXBvcnQgbWFoYWJodXRhIGZyb20gJ21haGFiaHV0YSc7XG5cbmltcG9ydCBmYXN0cSBmcm9tICdmYXN0cSc7XG5pbXBvcnQgdHlwZSB7IHF1ZXVlQXNQcm9taXNlZCB9IGZyb20gXCJmYXN0cVwiO1xuaW1wb3J0IHsgQ29uZmlndXJhdGlvbiB9IGZyb20gJy4vaW5kZXguanMnO1xuaW1wb3J0IHsgUmVuZGVyaW5nQ29udGV4dCB9IGZyb20gJ0Bha2FzaGFjbXMvcmVuZGVyZXJzJztcbmltcG9ydCB7XG4gICAgRG9jdW1lbnRzRmlsZUNhY2hlLCBEb2N1bWVudFxufSBmcm9tICcuL2NhY2hlL2ZpbGUtY2FjaGUtc3FsaXRlLmpzJztcblxuLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4vKipcbiAqIFdoZXJlIHJlbmRlckRvY3VtZW50IGlzIG1lYW50IGZvciBhIGRvY3VtZW50IG9uIGRpc2tcbiAqIGFuZCBpbmRleGVkIGJ5IGEgRG9jdW1lbnRzRmlsZUNhY2hlIGluc3RhbmNlLCB0aGlzXG4gKiBmdW5jdGlvbiBpcyBtZWFudCBmb3IgZG9jdW1lbnRzIGNyZWF0ZWQgZnJvbSBpbi1tZW1vcnlcbiAqIGRhdGEuICBGb3IgaW5zdGFuY2UsIHRoZSB0YWdnZWQtY29udGVudCBwbHVnaW4gZ2VuZXJhdGVzXG4gKiB0YWcgcGFnZXMgbGlzdGluZyBsaW5rcyB0byBkb2N1bWVudHMgYmFzZWQgb24gdGhlaXIgdGFnLlxuICogVGhlc2UgcGFnZXMgYXJlIGluc3RhbnRpYXRlIG91dCBvZiBkYXRhIHJhdGhlciB0aGFuXG4gKiBleGlzdGluZyBvbi1kaXNrLlxuICpcbiAqIFJlcXVpcmVkIGRhdGE6XG4gKiAgICAgKiBCbGFuayBwYWdlIC0gd2l0aCBmcm9udG1hdHRlciBpbmNsdWRpbmcgYSBcImxheW91dFwiIHRlbXBsYXRlIHJlZmVyZW5jZVxuICogICAgICogRmlsZS1uYW1lIHRvIHVzZSBmb3IgdmlydHVhbCBwYWdlLCB3aGljaCBhbHNvIGRldGVybWluZXMgdGhlIHJlbmRlcmVkIG91dHB1dCBmaWxlXG4gKiAgICAgKiBNZXRhZGF0YSBkZXJpdmVkIGZyb20gdGhlIGZyb250bWF0dGVyIGFuZCBmaWxsZWQgd2l0aCBvdGhlciBzdHVmZiBpbmNsdWRpbmcgdGhlIGRhdGEgdG8gcmVuZGVyIGludG8gdGhlIHBhZ2UsICBcbiAqXG4gKiBAcGFyYW0gY29uZmlnIFxuICogQHBhcmFtIGRvY0luZm8gXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW5kZXJWaXJ0dWFsRG9jdW1lbnQoXG4gICAgY29uZmlnOiBDb25maWd1cmF0aW9uLFxuICAgIGRvY0luZm86IHtcbiAgICAgICAgLy8gVGhlIHZpcnR1YWwgcGF0aG5hbWVcbiAgICAgICAgdnBhdGg6IHN0cmluZztcbiAgICAgICAgLy8gVGhlIGRvY3VtZW50IHRvIHJlbmRlciBhcyBpZiBpdCdzXG4gICAgICAgIC8vIGF0IHRoYXQgcGF0aFxuICAgICAgICBkb2N1bWVudDogc3RyaW5nO1xuICAgIH1cbikge1xuXG5cbiAgICBjb25zdCByZW5kZXJlciA9IGNvbmZpZy5maW5kUmVuZGVyZXJQYXRoKFxuICAgICAgICAgICAgZG9jSW5mby52cGF0aFxuICAgICk7XG5cbiAgICBjb25zdCByYyA9IHJlbmRlcmVyLnBhcnNlTWV0YWRhdGEoe1xuICAgICAgICBmc3BhdGg6IGRvY0luZm8udnBhdGgsXG4gICAgICAgIGNvbnRlbnQ6IGRvY0luZm8uZG9jdW1lbnQsXG4gICAgICAgIG1ldGFkYXRhOiB7fVxuICAgIH0pO1xuXG4gICAgLy8gQWRkIG5lY2Vzc2FyeSBpdGVtcyB0byB0aGUgbWV0YWRhdGFcbiAgICByYy5tZXRhZGF0YS5jb25maWcgPSBjb25maWc7XG4gICAgcmMubWV0YWRhdGEucGFydGlhbCA9IChmbmFtZSwgbWV0YWRhdGEpID0+IHtcbiAgICAgICAgcmV0dXJuIGNvbmZpZy5ha2FzaGEucGFydGlhbChjb25maWcsIGZuYW1lLCBtZXRhZGF0YSk7XG4gICAgfTtcbiAgICByYy5tZXRhZGF0YS5wYXJ0aWFsU3luYyA9IChmbmFtZSwgbWV0YWRhdGEpID0+IHtcbiAgICAgICAgcmV0dXJuIGNvbmZpZy5ha2FzaGEucGFydGlhbFN5bmMoY29uZmlnLCBmbmFtZSwgbWV0YWRhdGEpO1xuICAgIH07XG4gICAgcmMubWV0YWRhdGEuYWthc2hhID0gY29uZmlnLmFrYXNoYTtcbiAgICByYy5tZXRhZGF0YS5wbHVnaW4gPSBjb25maWcucGx1Z2luO1xuXG4gICAgLy8gUmVuZGVyIHRoZSBwcmltYXJ5IGNvbnRlbnRcbiAgICBsZXQgZG9jcmVuZGVyZWQgPSBhd2FpdCByZW5kZXJlci5yZW5kZXIocmMpO1xuXG4gICAgLy8gSWYgdGhlcmUgaXMgYSBsYXlvdXQgdGVtcGxhdGUsIHJlbmRlciB0aGF0XG4gICAgLy8gdGVtcGxhdGUgcGFzc2luZyB0aGUgcmVuZGVyZWQgcHJpbWFyeSBjb250ZW50XG4gICAgbGV0IGxheW91dHJlbmRlcmVkO1xuICAgIGlmIChyYy5tZXRhZGF0YS5sYXlvdXQpIHtcbiAgICAgICAgY29uc3QgbGF5b3V0cyA9IGNvbmZpZy5ha2FzaGFcbiAgICAgICAgICAgICAgICAuZmlsZWNhY2hlLmxheW91dHNDYWNoZTtcbiAgICAgICAgY29uc3QgbGF5b3V0SW5mbyA9IGF3YWl0IGxheW91dHMuZmluZChyYy5tZXRhZGF0YS5sYXlvdXQpO1xuICAgICAgICBpZiAoIWxheW91dEluZm8pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gbGF5b3V0IGZvdW5kIGluICR7dXRpbC5pbnNwZWN0KGNvbmZpZy5sYXlvdXREaXJzKX0gZm9yICR7cmMubWV0YWRhdGEubGF5b3V0fSBpbiBmaWxlICR7ZG9jSW5mby52cGF0aH1gKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEJ1aWxkIHRoZSBtZXRhZGF0YSBmb3IgdGhlIGxheW91dCByZW5kZXJpbmdcbiAgICAgICAgbGV0IGxheW91dG1ldGFkYXRhOiBhbnkgPSB7fTtcbiAgICAgICAgZm9yICh2YXIgeXByb3AgaW4gbGF5b3V0SW5mby5tZXRhZGF0YSkge1xuICAgICAgICAgICAgbGF5b3V0bWV0YWRhdGFbeXByb3BdID0gbGF5b3V0SW5mby5tZXRhZGF0YVt5cHJvcF07XG4gICAgICAgIH1cbiAgICAgICAgZm9yICh2YXIgeXByb3AgaW4gcmMubWV0YWRhdGEpIHtcbiAgICAgICAgICAgIGlmICh5cHJvcCAhPT0gJ2xheW91dCcpIHtcbiAgICAgICAgICAgICAgICBsYXlvdXRtZXRhZGF0YVt5cHJvcF0gPSByYy5tZXRhZGF0YVt5cHJvcF07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBNYWtlIHRoZSBmaXJzdCByZW5kZXJpbmcgYXZhaWxhYmxlXG4gICAgICAgIC8vIGluIHRoZSBtZXRhZGF0YSBhcyBcImNvbnRlbnRcIiB2YXJpYWJsZVxuICAgICAgICBsYXlvdXRtZXRhZGF0YS5jb250ZW50ID0gZG9jcmVuZGVyZWQ7XG