Skip to content

Commit

Permalink
Refactored and optimized code of export, browser, pool and other modu…
Browse files Browse the repository at this point in the history
…les.
  • Loading branch information
PaulDalek committed May 14, 2024
1 parent df5f150 commit 5334f73
Show file tree
Hide file tree
Showing 11 changed files with 469 additions and 248 deletions.
4 changes: 2 additions & 2 deletions dist/index.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.esm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.esm.js.map

Large diffs are not rendered by default.

206 changes: 198 additions & 8 deletions lib/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,21 @@ See LICENSE file in root for details.
*******************************************************************************/

import fs from 'fs';
import * as url from 'url';
import { readFileSync } from 'fs';
import path from 'path';

import puppeteer from 'puppeteer';

import { getCachePath } from './cache.js';
import { getOptions } from './config.js';
import { setupHighcharts } from './highcharts.js';
import { log, logWithStack } from './logger.js';
import { __dirname } from './utils.js';

import ExportError from './errors/ExportError.js';

// Get the template for the page
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const template = fs.readFileSync(
__dirname + '/../templates/template.html',
'utf8'
);
const template = readFileSync(__dirname + '/templates/template.html', 'utf8');

let browser;

Expand Down Expand Up @@ -164,6 +161,9 @@ export async function newPage() {
// Set the content
await setPageContent(page);

// Set page events
setPageEvents(page);

return page;
}

Expand Down Expand Up @@ -204,6 +204,161 @@ export async function clearPage(page, hardReset = false) {
}
}

/**
* Adds custom JS and CSS resources to a Puppeteer Page based on the specified
* options.
*
* @param {Object} page - The Puppeteer Page object to which resources will be
* added.
* @param {Object} options - All options and configuration.
*
* @returns {Promise<Array<Object>>} - Promise resolving to an array of injected
* resources.
*/
export async function addPageResources(page, options) {
// Injected resources array
const injectedResources = [];

// Use resources
const resources = options.customLogic.resources;
if (resources) {
const injectedJs = [];

// Load custom JS code
if (resources.js) {
injectedJs.push({
content: resources.js
});
}

// Load scripts from all custom files
if (resources.files) {
for (const file of resources.files) {
const isLocal = !file.startsWith('http') ? true : false;

// Add each custom script from resources' files
injectedJs.push(
isLocal
? {
content: readFileSync(file, 'utf8')
}
: {
url: file
}
);
}
}

for (const jsResource of injectedJs) {
try {
injectedResources.push(await page.addScriptTag(jsResource));
} catch (error) {
logWithStack(2, error, `[export] The JS resource cannot be loaded.`);
}
}
injectedJs.length = 0;

// Load CSS
const injectedCss = [];
if (resources.css) {
let cssImports = resources.css.match(/@import\s*([^;]*);/g);
if (cssImports) {
// Handle css section
for (let cssImportPath of cssImports) {
if (cssImportPath) {
cssImportPath = cssImportPath
.replace('url(', '')
.replace('@import', '')
.replace(/"/g, '')
.replace(/'/g, '')
.replace(/;/, '')
.replace(/\)/g, '')
.trim();

// Add each custom css from resources
if (cssImportPath.startsWith('http')) {
injectedCss.push({
url: cssImportPath
});
} else if (options.customLogic.allowFileResources) {
injectedCss.push({
path: path.join(__dirname, cssImportPath)
});
}
}
}
}

// The rest of the CSS section will be content by now
injectedCss.push({
content: resources.css.replace(/@import\s*([^;]*);/g, '') || ' '
});

for (const cssResource of injectedCss) {
try {
injectedResources.push(await page.addStyleTag(cssResource));
} catch (error) {
logWithStack(2, error, `[export] The CSS resource cannot be loaded.`);
}
}
injectedCss.length = 0;
}
}
return injectedResources;
}

/**
* Clears out all state set on the page with addScriptTag/addStyleTag. Removes
* injected resources and resets CSS and script tags on the page. Additionally,
* it destroys previously existing charts.
*
* @param {Object} page - The Puppeteer Page object from which resources will
* be cleared.
* @param {Array<Object>} injectedResources - Array of injected resources
* to be cleared.
*/
export async function clearPageResources(page, injectedResources) {
for (const resource of injectedResources) {
await resource.dispose();
}

// Destroy old charts after export is done and reset all CSS and script tags
await page.evaluate(() => {
// We are not guaranteed that Highcharts is loaded, e,g, when doing SVG
// exports
if (typeof Highcharts !== 'undefined') {
// eslint-disable-next-line no-undef
const oldCharts = Highcharts.charts;

// Check in any already existing charts
if (Array.isArray(oldCharts) && oldCharts.length) {
// Destroy old charts
for (const oldChart of oldCharts) {
oldChart && oldChart.destroy();
// eslint-disable-next-line no-undef
Highcharts.charts.shift();
}
}
}

// eslint-disable-next-line no-undef
const [...scriptsToRemove] = document.getElementsByTagName('script');
// eslint-disable-next-line no-undef
const [, ...stylesToRemove] = document.getElementsByTagName('style');
// eslint-disable-next-line no-undef
const [...linksToRemove] = document.getElementsByTagName('link');

// Remove tags
for (const element of [
...scriptsToRemove,
...stylesToRemove,
...linksToRemove
]) {
element.remove();
}
});
}

/**
* Sets the content for a Puppeteer Page using a predefined template
* and additional scripts. Also, sets the pageerror in order to catch
Expand All @@ -222,10 +377,45 @@ async function setPageContent(page) {
await page.evaluate(setupHighcharts);
}

/**
* Set events for a Puppeteer Page.
*
* @param {Object} page - The Puppeteer Page object to set events to.
*/
function setPageEvents(page) {
// Get debug options
const { debug } = getOptions();

// Set the console listener, if needed
if (debug.enable && debug.listenToConsole) {
page.on('console', (message) => {
console.log(`[debug] ${message.text()}`);
});
}

// Set the pageerror listener
page.on('pageerror', async (error) => {
// TODO: Consider adding a switch here that turns on log(0) logging
// on page errors.
await page.$eval(
'#container',
(element, errorMessage) => {
// eslint-disable-next-line no-undef
if (window._displayErrors) {
element.innerHTML = errorMessage;
}
},
`<h1>Chart input data error: </h1>${error.toString()}`
);
});
}

export default {
get,
create,
close,
newPage,
clearPage
clearPage,
addPageResources,
clearPageResources
};
12 changes: 9 additions & 3 deletions lib/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,17 @@ export const checkAndUpdateCache = async (options) => {
export const getCachePath = () =>
join(__dirname, getOptions().highcharts.cachePath);

export const getCache = () => cache;

export const highcharts = () => cache.sources;

export const version = () => cache.hcVersion;

export default {
checkAndUpdateCache,
getCachePath,
updateVersion,
getCache: () => cache,
highcharts: () => cache.sources,
version: () => cache.hcVersion
getCache,
highcharts,
version
};
Loading

0 comments on commit 5334f73

Please sign in to comment.