UNPKG

pjv

Version:
306 lines (300 loc) 12.8 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /*============================================================================= pjv - package.json visualization tool - an alternative of cat and bat tools providing formatted view of config files: .env: aligned table package.json: especially the "scripts" field - Installation $ yarn global add pjv - Option arguments s - print scripts only in package.json and .env https://docs.npmjs.com/cli/v7/configuring-npm/package-json TODO: When the element of array is an object, formatting is required. [object Object] - test case: ~/.config/yarn/global/node_modules/yaml/ (C) 2022 SpacetimeQ INC =============================================================================*/ const fs_1 = __importDefault(require("fs")); const dotenv_1 = __importDefault(require("dotenv")); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); // for TypeScript use version ^4 const semver_1 = __importDefault(require("semver")); const child_process_1 = require("child_process"); const SEP = chalk_1.default.gray(': '); // separator between key and value const SEP0 = chalk_1.default.gray(':'); const BULLET = '▫️ '; const MIN_PAD_VER = 6; // minimum padding for version strings const SEMVERSIGN = new Map([ ['major', '❌'], ['minor', '✅'], ['patch', '✔️ '], ]); ; ; ; ; const packVers = []; let packageName = ''; const dirBase = path_1.default.basename(process.cwd()); const isObject = (o) => (typeof (o) === 'object') && !Array.isArray(o); // array also is an 'object' let rootPath = './'; const PACKAGE = 'package.json'; const DOTENV = '.env'; const HELP = [ ['v', "version", "package version"], ['e', "env", ".env only"], ['s', "scripts", "scripts (in package.json) only"], ['x', "xcheck", "skip checking npm outdated packages that takes time"], ['h', "help", "help"], ]; const [, , ...args] = process.argv; let vmode = 'all'; if (args[0]) { const arg = args[0]; const { name, version, description } = require('../' + PACKAGE); // own package const option = (arg[0] === '-') // allow up to two '-' for the option prefix ? (arg[1] === '-') ? arg[2] // arg.substring(2) // long : arg[1] // short : ''; if (option) { switch (option.toLowerCase()[0]) { case 'e': vmode = 'dotenv'; break; case 's': vmode = 'scripts'; break; case 'x': vmode = 'xoutdated'; break; case 'v': console.log(version); // console.log(process.env.npm_package_version); process.exit(0); default: console.log(chalk_1.default.yellowBright(name), chalk_1.default.greenBright('v' + version), description, '-', chalk_1.default.yellowBright('node'), chalk_1.default.greenBright(process.version)); console.log(' ', chalk_1.default.gray('by'), chalk_1.default.blue("SPACE") + chalk_1.default.green('TIME') + chalk_1.default.red('Q')); console.log(chalk_1.default.yellow("OPTIONS:")); let pad = 7; // set the longest length in HELP's long option HELP.forEach(arr => { if (arr[1].length > pad) pad = arr[1].length; }); HELP.forEach(arr => console.log(' ', chalk_1.default.green('-' + arr[0]) + ', ' + chalk_1.default.green('--' + arr[1].padEnd(pad)), arr[2])); process.exit(0); } } else { rootPath = arg; // TODO: REGEX check required if (rootPath[rootPath.length - 1] !== '/') rootPath += '/'; } } // arg: a | A - all const SCRIPT_HL = [ 'start', 'serve', 'build', ]; // pad should be the length of the longest key + 1 const maxKeyLen = (obj) => { let len = 0; (Array.isArray(obj) ? obj : Object.keys(obj)).forEach((key) => { if (key.length > len) len = key.length; }); return len; }; // log filename const logFilename = (name) => console.log(chalk_1.default.white.bgGray(` ${name}`.padEnd(80))); const logField = (field) => console.log(chalk_1.default.black.bgCyan(` ◀︎ ${field} ▶︎ `)); const logNumbering = (n) => chalk_1.default.gray(`${(n + 1).toString().padStart(3)}`); const logSubObj = (obj, key) => { console.log(BULLET, chalk_1.default.cyanBright(key)); Object.entries(obj).forEach(([kv, vv], iv) => { if (isObject(vv)) { logSubObj(vv, kv); // recursively call } else { const pk = ` ${kv.padEnd(maxKeyLen(obj))} `; console.log(logNumbering(iv), chalk_1.default.yellow(pk) + SEP + chalk_1.default.green(vv)); } }); }; // iterate object const logKeyValueObj = (obj, key) => { const o = obj[key]; const isScript = key === 'scripts'; const isDep = key.toLowerCase().includes('dependencies'); if (!isScript && vmode === 'scripts') return; if (isDep && vmode !== 'xoutdated') { const dev = key === 'devDependencies'; Object.entries(o).forEach(([name, ver]) => packVers.push({ dep: dirBase, name, dev, ver: ver })); return; // just keep in packVers to print when outdated versions are fetched } logField(key); Object.entries(o).forEach(([k, val], i) => { if (isObject(val)) { logSubObj(val, k); } else { const pk = ` ${k.padEnd(maxKeyLen(o))} `; // padded key console.log(logNumbering(i), isScript ? SCRIPT_HL.includes(k) ? chalk_1.default.black.bgYellow(pk) + SEP + chalk_1.default.yellow(val) : chalk_1.default.yellow(pk) + SEP + chalk_1.default.white(val) : (isDep && k.includes('@')) ? chalk_1.default.redBright(pk) + SEP + chalk_1.default.blue(val) : chalk_1.default.yellow(pk) + SEP + chalk_1.default.cyan(val)); } }); }; const logKeyValue = (obj, key, pad) => { let content = obj[key]; if (key === 'name' && packageName !== dirBase) { content += '🚨' + chalk_1.default.redBright(' ≠../' + dirBase); } console.log(BULLET, chalk_1.default.cyanBright(`${key.padEnd(pad)}`), SEP0, content); }; try { fs_1.default.access(rootPath + DOTENV, fs_1.default.constants.F_OK, (err) => { if (err) { // console.log("No .env"); return; } logFilename('.env'); const env = dotenv_1.default.parse(fs_1.default.readFileSync(rootPath + DOTENV, 'utf8')); const pad = maxKeyLen(env); Object.entries(env).forEach(([key, val], i) => { console.log(logNumbering(i), key.padEnd(pad) + SEP + chalk_1.default.greenBright(val)); }); }); } catch (err) { console.error(err); } if (vmode !== 'dotenv') { try { fs_1.default.access(rootPath + PACKAGE, fs_1.default.constants.F_OK, (err) => { if (err) { // file not found console.log("⛔️ No", rootPath + PACKAGE); process.exit(1); } const json = JSON.parse(fs_1.default.readFileSync(rootPath + PACKAGE, 'utf8')); packageName = json.name; console.log(chalk_1.default.white.bgGray(` ${PACKAGE} `) + chalk_1.default.black.bgWhite(` ${packageName} `) + chalk_1.default.black.bgGreen(` ${json.version || 'no version'} `) + chalk_1.default.green.bgBlack(` ${json.description || ''} `)); // Partition keys array by its content if it is of pure object type or not const [ob, no] = Object.keys(json).reduce(([ob, no], k) => (isObject(json[k]) ? [[...ob, k], no] : [ob, [...no, k]]), [[], []]); if (vmode !== 'scripts') { const pad = maxKeyLen(no); no.forEach(key => logKeyValue(json, key, pad)); } ob.forEach(key => logKeyValueObj(json, key)); }); } catch (err) { console.error(err); } } /** * package versions * @param pack - package versions object * @param pd - padding 0: package nanme, 1: package version, 2: Current, 3: Wanted, 4: Latest */ const logPackVers = (pack, pd) => { if (!pack.length) return; const field = (pack[0].dev ? 'devDependencies' : 'dependencies').padEnd(pd[0] + pd[1] + 2); console.log(chalk_1.default.black.bgCyan(` ◀︎ ${field.padEnd(pd[0] + pd[1] + 2)} ▶︎ `), chalk_1.default.black.bgWhite((pd[2] > MIN_PAD_VER ? 'Current' : 'Cur').padStart(pd[2])), chalk_1.default.black.bgGreen('Wanted'.padStart(pd[3])), chalk_1.default.black.bgMagenta('Latest'.padStart(pd[4]))); pack.forEach((p, i) => { const nk = ` ${p.name.padEnd(pd[0])} `; let sn = ''; // sign if (p.outd) { const df = semver_1.default.diff(p.outd.current || p.outd.wanted, p.outd.latest) || ''; const svsn = df && SEMVERSIGN.get(df); sn = svsn || df; if (p.dep !== dirBase) sn += ' ' + chalk_1.default.blueBright(p.dep); } console.log(logNumbering(i), p.name.includes('@') ? (p.name.includes('@types/') ? chalk_1.default.blueBright.italic(nk) : chalk_1.default.redBright(nk)) + SEP + chalk_1.default.blue(p.ver.padStart(pd[1])) : chalk_1.default.yellow(nk) + SEP + chalk_1.default.cyan(p.ver.padStart(pd[1])), p.outd ? chalk_1.default.white((p.outd.current || '-').padStart(pd[2])) + ' ' + chalk_1.default.greenBright((p.outd.wanted || ' ').padStart(pd[3])) + ' ' + chalk_1.default.magentaBright((p.outd.latest || ' ').padStart(pd[4])) + ' ' + sn : '✨'); }); }; /** * assumption that packVers was already built by reading package.json * depent name from 'npm outdated' is the current folder name */ if (vmode !== 'xoutdated' && vmode !== 'dotenv' && vmode !== 'scripts') { // -------------------------------------------------------------------------------- // takes time to run the external command (0, child_process_1.exec)('npm outdated --json', (error, stdout) => { // -------------------------------------------------------------------------------- if (stdout) { // console.log(stdout); const pd = Array(5).fill(MIN_PAD_VER, 1); // minimum padding pd[0] = 0; // minimum padding for package name const json = JSON.parse(stdout); Object.entries(json).forEach(([key, val]) => { const { current, wanted, latest } = val; if ((current === null || current === void 0 ? void 0 : current.length) > pd[2]) pd[2] = current.length; if ((wanted === null || wanted === void 0 ? void 0 : wanted.length) > pd[3]) pd[3] = wanted.length; if ((latest === null || latest === void 0 ? void 0 : latest.length) > pd[4]) pd[4] = latest.length; if (val.dependent === dirBase) { const fo = packVers.find(x => x.name === key); if (fo) { fo.outd = { current, wanted, latest }; } else { console.error(key, "not found!"); } } else { // dependent is another workspace packVers.push({ dep: val.dependent, name: key, dev: false, ver: '', outd: { current, wanted, latest } }); } }); packVers.forEach(p => { if (p.name.length > pd[0]) pd[0] = p.name.length; if (p.ver.length > pd[1]) pd[1] = p.ver.length; }); // partition array into prod dependencies and devDependencies const [pro, dev] = packVers.reduce(([pro, dev], k) => (k.dev ? [pro, [...dev, k]] : [[...pro, k], dev]), [[], []]); logPackVers(pro, pd); logPackVers(dev, pd); } if (!error) { console.log(packVers.length ? '✨No outdated modules!' : "⚠️ No dependencies."); } }); }