UNPKG

akasharender

Version:

Rendering support for generating static HTML websites or EPUB eBooks

558 lines 82 kB
/** * * 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