UNPKG

@speedy-js/depcost

Version:

[![npm version](https://badgen.net/npm/v/@speedy-js/depcost)](https://npm.im/@speedy-js/depcost)

213 lines (189 loc) 4.45 kB
/** * Module dependencies */ const { join, isAbsolute, resolve } = require('path') const hash = require('hash-sum') const bytes = require('bytes') const { removeSync, writeFile, existsSync } = require('fs-extra') const chalk = require('chalk') const execa = require('execa') const log = require('npmlog') const parsePackageName = require('./parse-package-name') const store = require('./store') const { getTmpDirByTS } = require('./tmp') const exec = (commands, opts) => { const [command, ...args] = commands.split(' ') return execa(command, args, opts) } /** * Get require time of a entry. * * @param {string} entry * @param {string} tmpfile */ async function getRequireTime(entry, tmpfile) { if (!tmpfile) { const tmpdir = getTmpDirByTS() tmpfile = join(tmpdir, `index-${hash(entry)}.js`) } await writeFile( tmpfile, ` console.time('__depcost__') try { require('${entry}') } catch(e) { // Do not need handle it. } console.timeEnd('__depcost__') `, 'utf-8', ) log.info(entry, 'calculate require time') try { // e.g. '__depcost__: 49.518ms' const { stdout: execOutput } = await exec(COMMANDS.execNode(tmpfile)) const matched = execOutput.match(/__depcost__\:\s(.*ms)/) if (matched) { return matched[1].trim() } return 'N/A' } catch (e) { console.log(e) return 'N/A' } } /** * Get size of a directory * * @param {string} cwd */ async function getDirectorySize(cwd) { const sizeCommand = COMMANDS.size(cwd) log.info(cwd, sizeCommand) const { stdout: sizeOutput } = await exec(sizeCommand, { cwd }) const sizes = sizeOutput.split('\t') return sizes[0] } /** * Get entry by directory path * * @param {string} dir */ function getEntryByDir(dir) { return join(dir, 'index.js') } /** * Parse and get correct install script. */ function getInstallScript(npmClient, pkg) { if (npmClient.endsWith('npm')) { return `${npmClient} install ${pkg} -D` } if (npmClient === 'yarn') { return `${npmClient} add ${pkg} -D` } throw new Error(`Unknown npm client: ${npmClient}`) } /** * Core commands. */ const COMMANDS = { init: 'npm init -y', installPkg: (pkg, npmClient) => getInstallScript(npmClient, pkg), size: dir => `du -sk ${dir}`, execNode: file => `node ${file}`, } /** * A simple util to make "process.on" only called once. */ const onProcessExit = (() => { const listeners = [] process.on('exit', () => { listeners.forEach(listener => listener()) }) return listener => listeners.push(listener) })() /** * Retrieve cost of a package * * @param options * @return {Promise<{ * pkg: Record<string, unknown>, * name: string, * version: string, * size: string * rawSize: number, * requireTime: string, * }>} */ module.exports = async function (options) { const { pkg, track, npmClient, resolvelocal = false } = options; const parsed = parsePackageName(pkg); const cache = store.get(pkg); let usingLocal = false; /** * handle cache. */ if ( cache && cache.pkg && cache.name && cache.version && cache.size && cache.rawSize && cache.requireTime ) { return cache; } log.info('getSingleDepCost', pkg) /** * Create a temp dir for calculating size. */ let tempDir = getTmpDirByTS() let dir = tempDir; if (track) { console.log(chalk.gray('dir'), dir) } else { onProcessExit(() => { log.info('clean temp directory ...') removeSync(dir) }) } /** * for local directory. */ if (resolvelocal && existsSync(pkg)) { if (isAbsolute(pkg)) { dir = pkg } else { dir = resolve(pkg) } usingLocal = true; console.log(`leverage local directory: ${chalk.gray(dir)}`); } log.info(pkg, COMMANDS.init) await exec(COMMANDS.init, { cwd: dir }) const installPkgCommand = COMMANDS.installPkg(pkg, npmClient) log.info(pkg, installPkgCommand) await exec(installPkgCommand, { cwd: dir }) return Promise.all([ getDirectorySize(dir), getRequireTime(parsed.name, getEntryByDir(dir)) ]).then( ([size, requireTime]) => { const rawSize = Number(size) const ret = { pkg, name: parsed.name, version: parsed.version, size: bytes(rawSize * 1024), rawSize, // unit: kb requireTime, } store.set(pkg, ret) return ret }, ) } module.exports.getRequireTime = getRequireTime