Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace ejs templates with a simple js file #397

Merged
merged 6 commits into from Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 8 additions & 81 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions package.json
Expand Up @@ -30,15 +30,13 @@
"files": [
"public",
"lib",
"src",
"views"
"src"
],
"dependencies": {
"acorn": "^8.0.4",
"acorn-walk": "^8.0.0",
"chalk": "^4.1.0",
"commander": "^6.2.0",
"ejs": "^3.1.5",
"express": "^4.17.1",
"filesize": "^6.1.0",
"gzip-size": "^6.0.0",
Expand Down
57 changes: 50 additions & 7 deletions views/viewer.ejs → src/template.js
@@ -1,22 +1,65 @@
<!DOCTYPE html>
/* eslint-disable max-len */
const path = require('path');
const fs = require('fs');

const _ = require('lodash');

const projectRoot = path.resolve(__dirname, '..');
const assetsRoot = path.join(projectRoot, 'public');

exports.renderViewer = renderViewer;

/**
* Escapes `<` characters in JSON to safely use it in `<script>` tag.
*/
function escapeJson(json) {
return JSON.stringify(json).replace(/</gu, '\\u003c');
}

function getAssetContent(filename) {
const assetPath = path.join(assetsRoot, filename);

if (!assetPath.startsWith(assetsRoot)) {
throw new Error(`"${filename}" is outside of the assets root`);
}

return fs.readFileSync(assetPath, 'utf8');
}

function html(strings, ...values) {
return strings.map((string, index) => `${string}${values[index] || ''}`).join('');
}

function getScript(filename, mode) {
if (mode === 'static') {
return `<!-- ${_.escape(filename)} -->
<script>${getAssetContent(filename)}</script>`;
} else {
return `<script src="${_.escape(filename)}"></script>`;
}
}

function renderViewer({title, enableWebSocket, chartData, defaultSizes, mode} = {}) {
return html`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title><%= title %></title>
<title>${_.escape(title)}</title>
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />

<script>
window.enableWebSocket = <%- escapeJson(enableWebSocket) %>;
window.enableWebSocket = ${escapeJson(enableWebSocket)};
</script>
<%- include('script', { filename: 'viewer.js' }) %>
${getScript('viewer.js', mode)}
</head>

<body>
<div id="app"></div>
<script>
window.chartData = <%- escapeJson(chartData) %>;
window.defaultSizes = <%- escapeJson(defaultSizes) %>;
window.chartData = ${escapeJson(chartData)};
window.defaultSizes = ${escapeJson(defaultSizes)};
</script>
</body>
</html>
</html>`;
}
86 changes: 24 additions & 62 deletions src/viewer.js
Expand Up @@ -5,15 +5,14 @@ const http = require('http');
const WebSocket = require('ws');
const _ = require('lodash');
const express = require('express');
const ejs = require('ejs');
const {bold} = require('chalk');

const Logger = require('./Logger');
const analyzer = require('./analyzer');
const {open} = require('./utils');
const {renderViewer} = require('./template');

const projectRoot = path.resolve(__dirname, '..');
const assetsRoot = path.join(projectRoot, 'public');

function resolveTitle(reportTitle) {
if (typeof reportTitle === 'function') {
Expand Down Expand Up @@ -50,24 +49,18 @@ async function startServer(bundleStats, opts) {
if (!chartData) return;

const app = express();

// Explicitly using our `ejs` dependency to render templates
// Fixes #17
app.engine('ejs', require('ejs').renderFile);
app.set('view engine', 'ejs');
app.set('views', `${projectRoot}/views`);
app.use(express.static(`${projectRoot}/public`));

app.use('/', (req, res) => {
res.render('viewer', {
app.get('/', (req, res) => {
res.writeHead(200, {'Content-Type': 'text/html'});
const html = renderViewer({
mode: 'server',
title: resolveTitle(reportTitle),
get chartData() { return chartData },
chartData,
defaultSizes,
enableWebSocket: true,
// Helpers
escapeJson
enableWebSocket: true
});
return res.end(html);
});

const server = http.createServer(app);
Expand Down Expand Up @@ -140,42 +133,28 @@ async function generateReport(bundleStats, opts) {
if (!chartData) return;

await new Promise((resolve, reject) => {
ejs.renderFile(
`${projectRoot}/views/viewer.ejs`,
{
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there is no need in promise here anymore. The code is sync

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be a breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the function can be still async but promise wrapper around sync code is not necessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yep, agree. @realityking could you change it please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a commit to do this :)

const reportHtml = renderViewer({
mode: 'static',
title: resolveTitle(reportTitle),
chartData,
defaultSizes,
enableWebSocket: false,
// Helpers
assetContent: getAssetContent,
escapeJson
},
(err, reportHtml) => {
try {
if (err) {
logger.error(err);
reject(err);
return;
}

const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);

fs.mkdirSync(path.dirname(reportFilepath), {recursive: true});
fs.writeFileSync(reportFilepath, reportHtml);

logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);

if (openBrowser) {
open(`file://${reportFilepath}`, logger);
}
resolve();
} catch (e) {
reject(e);
}
enableWebSocket: false
});
const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);

fs.mkdirSync(path.dirname(reportFilepath), {recursive: true});
fs.writeFileSync(reportFilepath, reportHtml);

logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);

if (openBrowser) {
open(`file://${reportFilepath}`, logger);
}
);
resolve();
} catch (e) {
reject(e);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line should not be here I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I wonder how the tests worked with this line present 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They didn't. Travis hangs.

}

Expand All @@ -192,23 +171,6 @@ async function generateJSONReport(bundleStats, opts) {
logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
}

function getAssetContent(filename) {
const assetPath = path.join(assetsRoot, filename);

if (!assetPath.startsWith(assetsRoot)) {
throw new Error(`"${filename}" is outside of the assets root`);
}

return fs.readFileSync(assetPath, 'utf8');
}

/**
* Escapes `<` characters in JSON to safely use it in `<script>` tag.
*/
function escapeJson(json) {
return JSON.stringify(json).replace(/</gu, '\\u003c');
}

function getChartData(analyzerOpts, ...args) {
let chartData;
const {logger} = analyzerOpts;
Expand Down
8 changes: 0 additions & 8 deletions views/script.ejs

This file was deleted.