UNPKG

bundle-checker

Version:

CLI tool to generate stats on the size of files between two git branches

200 lines (199 loc) 9.46 kB
"use strict"; 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, '&lt;'], [/\>/g, '&gt;'], [/_/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; };