bundle-checker
Version:
CLI tool to generate stats on the size of files between two git branches
200 lines (199 loc) • 9.46 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.printStdout = exports.getExistingCommentId = exports.commentOnPr = exports.generateMarkdownReport = exports.normalizeSlugsInFileNames = exports.squashReportByFileExtension = exports.getFormattedRows = exports.groupFilesByExtension = exports.createMarkdownTable = exports.withDeltaSize = void 0;
const rest_1 = __importDefault(require("@octokit/rest"));
const bytes_1 = __importDefault(require("bytes"));
require("console.table");
const path_1 = __importDefault(require("path"));
const ramda_1 = require("ramda");
const SHARED_TABLE_VALUES = {
FILES_BREAKDOWN_TITLE: 'All targeted files',
FILE_EXTENSION: 'file extension',
FILE_NAME: 'name',
TOTALS_TITLE: 'Overview'
};
const COMMENT_WATERMARK = '<p align="right"><i>Generated by </i><a href="https://www.npmjs.com/package/bundle-checker" target="_blank">bundle-checker</a>🔎📦</p>';
function withDeltaSize(a = 0, b = 0) {
const icon = b - a > 0 ? `🔺 +` : `▼ -`;
if (b - a === 0) {
return bytes_1.default(b);
}
else {
return `${bytes_1.default(b)} (${icon}${bytes_1.default(Math.abs(b - a))})`;
}
}
exports.withDeltaSize = withDeltaSize;
function createMarkdownTable([headerRow, ...contentRows]) {
const escape = (markdownString) => {
const replacements = [
[/\*/g, '\\*'],
[/#/g, '\\#'],
[/\//g, '\\/'],
[/\(/g, '\\('],
[/\)/g, '\\)'],
[/\[/g, '\\['],
[/\]/g, '\\]'],
[/\</g, '<'],
[/\>/g, '>'],
[/_/g, '\\_']
];
return replacements.reduce((sequence, current) => markdownString.replace(current[0], current[1]), markdownString);
};
const buildHeader = (headers) => `| ${headers.join(' | ')} |\n` + `| ${headers.map(_ => '---').join(' | ')} |`;
const buildRow = (row) => `| ${row.join(' | ')} |`;
const buildRows = (rows) => rows.map(buildRow).join('\n');
return escape(`${buildHeader(headerRow)}\n` + `${buildRows(contentRows)}`);
}
exports.createMarkdownTable = createMarkdownTable;
exports.groupFilesByExtension = ramda_1.groupBy(path_1.default.extname);
exports.getFormattedRows = (report) => Object.keys(Object.assign(Object.assign({}, report.targetBranchReport), report.currentBranchReport))
.map(fileName => [
fileName,
report.currentBranchReport[fileName] || 0,
report.targetBranchReport[fileName] || 0
])
.sort(sortByDelta)
.map(([fileName, currentBranchSize, targetBranchSize]) => [
fileName,
withDeltaSize(targetBranchSize, currentBranchSize),
bytes_1.default(targetBranchSize)
]);
/*
* Given an IFileSizeReport, returns a new IFileSizeReport where entries are grouped by file extension
*/
exports.squashReportByFileExtension = (report) => {
const keysGroupedByExtension = Object.keys(exports.groupFilesByExtension(Object.keys(report)));
return ramda_1.zipObj(keysGroupedByExtension, keysGroupedByExtension.map(fileExtension => Object.keys(report)
.filter(file => path_1.default.extname(file) === fileExtension)
.reduce((sequence, currentFileName) => sequence + report[currentFileName], 0)));
};
/**
* Attempts to normalize file names by replacing the last slug before file extension (most-likely a webpack hash)
* with an ellipsis, unless it's `.min`.
* @param report
*/
exports.normalizeSlugsInFileNames = (report) => {
const normalizedKeys = Object.keys(report).map((fileName) => {
const { base, dir } = path_1.default.parse(fileName);
const fileNameSlugs = base.split('.');
const normalizedFileName = fileNameSlugs
.map((slug, index) => {
switch (true) {
case index === 0:
case index === fileNameSlugs.length - 1:
case slug.toLowerCase() === 'min':
case slug.toLowerCase() === 'chunk':
return slug;
default:
return '[…]';
}
})
.join('.');
return path_1.default.join(dir, normalizedFileName);
});
return ramda_1.zipObj(normalizedKeys, Object.values(report));
};
exports.generateMarkdownReport = ({ currentBranchName, report, targetBranchName }) => {
const wrapInCollapsible = (content, collapsibleHeader = 'Details of bundled changes') => `<details><summary>${collapsibleHeader}</summary>\n\n${content}\n\n</details>`;
const overviewTable = `### ${SHARED_TABLE_VALUES.TOTALS_TITLE}\n${createMarkdownTable([
[SHARED_TABLE_VALUES.FILE_EXTENSION, currentBranchName, targetBranchName],
...exports.getFormattedRows({
currentBranchReport: exports.squashReportByFileExtension(report.currentBranchReport),
targetBranchReport: exports.squashReportByFileExtension(report.targetBranchReport)
})
])}`;
const filesBreakDownTable = wrapInCollapsible(`### ${SHARED_TABLE_VALUES.FILES_BREAKDOWN_TITLE}\n${createMarkdownTable([
[SHARED_TABLE_VALUES.FILE_NAME, currentBranchName, targetBranchName],
...exports.getFormattedRows(report)
])}`);
return `${overviewTable}\n\n${filesBreakDownTable}`;
};
/**
* Posts a comment on a Pull Request, or updates an existing one if found
*/
function commentOnPr(body) {
return __awaiter(this, void 0, void 0, function* () {
const watermarkedBody = `${body}\n\n${COMMENT_WATERMARK}`;
try {
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';
const PULL_REQUEST_NUMBER = Number(process.env.TRAVIS_PULL_REQUEST || process.env.PULL_REQUEST_NUMBER);
const PULL_REQUEST_SLUG = process.env.TRAVIS_PULL_REQUEST_SLUG || process.env.PULL_REQUEST_SLUG || '';
const [owner, repo] = PULL_REQUEST_SLUG.split('/');
const octokit = new rest_1.default({ auth: GITHUB_TOKEN });
const githubParams = {
number: PULL_REQUEST_NUMBER,
owner,
repo
};
const commentId = yield getExistingCommentId(Object.assign(Object.assign({}, githubParams), { auth: GITHUB_TOKEN }));
if (commentId)
return yield octokit.issues.updateComment(Object.assign(Object.assign({}, githubParams), { body: watermarkedBody, comment_id: commentId }));
return octokit.issues.createComment(Object.assign(Object.assign({}, githubParams), { body: watermarkedBody }));
}
catch (error) {
console.error(error);
}
});
}
exports.commentOnPr = commentOnPr;
function getExistingCommentId(params) {
return __awaiter(this, void 0, void 0, function* () {
const octokit = new rest_1.default({ auth: params.auth });
const allComments = yield octokit.issues.listComments({
number: params.number,
owner: params.owner,
repo: params.repo
});
return (allComments.data
.filter(comment => comment.body.includes(COMMENT_WATERMARK))
.map(comment => comment.id)
.pop() || 0);
});
}
exports.getExistingCommentId = getExistingCommentId;
function printStdout(args) {
return __awaiter(this, void 0, void 0, function* () {
const totalTable = getConsoleTotalTable(args);
const filesBreakdownTable = getFilesBreakDownTable(args);
console.log(SHARED_TABLE_VALUES.TOTALS_TITLE);
console.table(totalTable);
console.log(SHARED_TABLE_VALUES.FILES_BREAKDOWN_TITLE);
console.table(filesBreakdownTable);
});
}
exports.printStdout = printStdout;
const getConsoleTotalTable = ({ report, currentBranchName, targetBranchName }) => {
const table = exports.getFormattedRows({
currentBranchReport: exports.squashReportByFileExtension(report.currentBranchReport),
targetBranchReport: exports.squashReportByFileExtension(report.targetBranchReport)
});
return table.map(([ext, currentSize, targetSize]) => ({
[SHARED_TABLE_VALUES.FILE_EXTENSION]: ext,
[currentBranchName]: currentSize,
[targetBranchName]: targetSize
}));
};
const getFilesBreakDownTable = ({ currentBranchName, targetBranchName, report }) => exports.getFormattedRows(report).map(([fileName, currentSize, targetSize]) => ({
[SHARED_TABLE_VALUES.FILE_NAME]: fileName,
[currentBranchName]: currentSize,
[targetBranchName]: targetSize
}));
const sortByDelta = (a, b) => {
const aDelta = a[1] - a[2];
const bDelta = b[1] - b[2];
if (Math.abs(aDelta) > Math.abs(bDelta))
return -1;
return 1;
};