automation/size/utils.js (97 lines of code) (raw):
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import fs from "fs";
import path, { dirname } from "path";
import { fileURLToPath } from "url";
import { exec } from "child_process";
import webpack from "webpack";
import config from "./webpack.config.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export const METRIC_TYPES = [
"boolean",
"counter",
"custom_distribution",
"datetime",
"event",
"labeled",
"memory_distribution",
"quantity",
"string",
"text",
"timespan",
"timing_distribution",
"rate",
"uuid",
"url",
];
export const PLUGINS = [ "encryption" ];
export const PLATFORMS = [ "web" ];
const UNITS = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
/**
* Formats a given number of bytes.
*
* Copied from:
* https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript
*
* @param {number} bytes The number of bytes to format.
* @returns A formatted representation of the bytes given.
*/
export function formatBytes(bytes) {
let l = 0, n = parseInt(bytes, 10) || 0;
while(n >= 1024 && ++l){
n = n/1024;
}
return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + UNITS[l]);
}
/**
* Executes an arbitrary command.
*
* @param {string} cmd The command to execute.
* @returns A promise which executes once the command is complete. Will resolve even in case of errors.
*/
export function executeCmd(cmd) {
console.log("Executing command:", cmd);
return new Promise((resolve, reject) => {
exec(cmd, (error, _, stderr) => {
if (error) {
console.log("Error:", error);
}
if (stderr) {
console.log("Stderr:", stderr);
}
resolve();
});
});
}
/**
* Builds a custom bundle of Glean.js + an optional array of metric types and plugins.
*
* Note: This function will use whatever `@mozilla/glean` package is linked at its runtime.
*
* @param {string} platform The platform to build for, supported platforms are: `web`.
* @param {[string]} metrics An array of metric types. Supported metrics types are listed in `METRIC_TYPES`.
* If not provided the final bundle won't contain any additional metric types.
* @param {[string]} plugins An array of plugins. Supported plugins are listed in `PLUGINS`.
* If not provided the final bundle won't contain any additional plugins.
* @returns A number representing the size in bytes of the generated bundle.
*/
export async function getCustomLibSize(platform, metrics = [], plugins = []) {
console.log(
`Creating a custom build for the "${platform}" platform.`,
`${metrics.length ? `Importing the ${metrics.join(",")} metric types.` : "Without extra metrics types."}`,
`${plugins.length ? `Importing the ${plugins.join(",")} plugins.` : "Without any plugins."}`
);
// Generate custom code importing the given metrics and plugins.
const code =
`import Glean from "@mozilla/glean/${platform}";
${metrics
.map(metric => `import ${metric} from "@mozilla/glean/private/metrics/${metric}";`)
.join("\n")}
${plugins
.map(plugin => `import ${plugin} from "@mozilla/glean/plugins/${plugin}";`)
.join("\n")}
console.log(JSON.stringify(Glean));
${metrics
.map(metric => `console.log(JSON.stringify(${metric}));`)
.join("\n")}
${plugins
.map(plugin => `console.log(JSON.stringify(${plugin}));`)
.join("\n")}
`;
// Create the tmp directory if it doesn't exist.
const tmpDir = path.join(__dirname, "tmp");
if (!fs.existsSync(tmpDir)){
fs.mkdirSync(tmpDir);
}
// Create a file with custom code as content.
const outputFilePath = path.join(tmpDir, "glean.lib.js")
await new Promise((resolve, reject) => {
fs.writeFile(outputFilePath, code, err => {
if (err) reject(err);
resolve();
});
});
// Build the library and return the size of the asset generated.
const customConfig = config(platform, outputFilePath);
return await new Promise((resolve, reject) => {
webpack(
customConfig,
(err, stats) => {
if (err || stats.hasErrors()) reject(err)
const statsJSON = stats.toJson();
resolve(statsJSON.entrypoints.main.assetsSize);
}
);
});
}