UNPKG

@dlillyatx/dc-cli

Version:
166 lines (165 loc) 6.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.handler = exports.printTree = exports.fillWhitespace = exports.TreeBuilder = exports.firstSecondThird = exports.prepareContentForTree = exports.traverseRecursive = exports.builder = exports.LOG_FILENAME = exports.desc = exports.command = void 0; const log_helpers_1 = require("../../common/log-helpers"); const path_1 = require("path"); const fs_1 = require("fs"); const util_1 = require("util"); const dc_management_sdk_js_1 = require("dc-management-sdk-js"); const content_dependancy_tree_1 = require("../../common/content-item/content-dependancy-tree"); const content_mapping_1 = require("../../common/content-mapping"); exports.command = 'tree <dir>'; exports.desc = 'Print a content dependency tree from content in the given folder.'; exports.LOG_FILENAME = (platform = process.platform) => log_helpers_1.getDefaultLogPath('item', 'tree', platform); exports.builder = (yargs) => { yargs.positional('dir', { type: 'string', describe: 'Path to the content items to build a tree from. Should be in the same format as an export.' }); }; exports.traverseRecursive = async (path, action) => { const dir = await util_1.promisify(fs_1.readdir)(path); dir.sort(); for (let i = 0; i < dir.length; i++) { let contained = dir[i]; contained = path_1.join(path, contained); const stat = await util_1.promisify(fs_1.lstat)(contained); if (stat.isDirectory()) { await exports.traverseRecursive(contained, action); } else { await action(contained); } } }; exports.prepareContentForTree = async (repo) => { const contentItems = []; const schemaNames = new Set(); await exports.traverseRecursive(path_1.resolve(repo.basePath), async (path) => { if (path_1.extname(path) !== '.json') { return; } let contentJSON; try { const contentText = await util_1.promisify(fs_1.readFile)(path, { encoding: 'utf8' }); contentJSON = JSON.parse(contentText); } catch (e) { console.error(`Couldn't read content item at '${path}': ${e.toString()}`); return; } schemaNames.add(contentJSON.body._meta.schema); contentItems.push({ repo: repo.repo, content: new dc_management_sdk_js_1.ContentItem(contentJSON) }); }); return new content_dependancy_tree_1.ContentDependancyTree(contentItems, new content_mapping_1.ContentMapping()); }; exports.firstSecondThird = (index, total) => { return index == total - 1 ? 2 : index === 0 ? 0 : 1; }; const fstPipes = ['├', '├', '└']; const circularPipes = ['╗', '║', '╝']; const circularLine = '═'; class TreeBuilder { constructor(evaluated) { this.evaluated = evaluated; this.lines = []; this.circularLinks = []; } addDependency(item, evalThis, fst, prefix) { const depth = evalThis.length - 1; const pipe = depth < 0 ? '' : fstPipes[fst] + '─ '; const circularMatch = evalThis.find(parent => parent.item == item); if (circularMatch) { this.lines.push(`${prefix}${pipe}*** (${item.owner.content.label})`); this.circularLinks.push([circularMatch.line, this.lines.length - 1]); return false; } else if (this.evaluated.has(item)) { if (depth > -1) { this.lines.push(`${prefix}${pipe}(${item.owner.content.label})`); } return false; } else { this.lines.push(`${prefix}${pipe}${item.owner.content.label}`); } evalThis.push({ item, line: this.lines.length - 1 }); this.evaluated.add(item); const filteredItems = item.dependancies.filter(dep => dep.resolved); filteredItems.forEach((dep, index) => { const subFst = exports.firstSecondThird(index, filteredItems.length); const subPrefix = depth == -1 ? '' : fst === 2 ? ' ' : '│ '; this.addDependency(dep.resolved, [...evalThis], subFst, prefix + subPrefix); }); return true; } } exports.TreeBuilder = TreeBuilder; exports.fillWhitespace = (original, current, char, targetLength) => { let position = original.length; let repeats = targetLength - original.length; while (position < current.length && repeats > 0) { if (current[position] != char && current[position] == ' ') { current = current.slice(0, position) + char + current.slice(position + 1); } position++; repeats--; } if (repeats > 0) { current += char.repeat(repeats); } return current; }; exports.printTree = (item, evaluated) => { const builder = new TreeBuilder(evaluated); const result = builder.addDependency(item, [], 0, ''); if (!result) return false; const circularLinks = builder.circularLinks; const lines = builder.lines.map(line => line + ' '); const modifiedLines = [...lines]; const maxWidth = Math.max(...lines.map(x => x.length)); for (let i = 0; i < circularLinks.length; i++) { const link = circularLinks[i]; let linkDist = maxWidth + 2; for (let j = 0; j < i; j++) { const link2 = circularLinks[j]; if (link[0] <= link2[1] && link[1] >= link2[0]) { linkDist += 2; } } for (let ln = link[0]; ln <= link[1]; ln++) { const end = ln == link[0] || ln == link[1]; const original = lines[ln]; let current = modifiedLines[ln]; current = exports.fillWhitespace(original, current, end ? circularLine : ' ', linkDist); current += circularPipes[exports.firstSecondThird(ln - link[0], link[1] - link[0] + 1)]; modifiedLines[ln] = current; } } modifiedLines.forEach(line => console.log(line)); console.log(''); return true; }; exports.handler = async (argv) => { const dir = argv.dir; const tree = await exports.prepareContentForTree({ basePath: dir, repo: new dc_management_sdk_js_1.ContentRepository() }); const evaluated = new Set(); for (let i = tree.levels.length - 1; i >= 0; i--) { const level = tree.levels[i]; console.log(`=== LEVEL ${i + 1} (${level.items.length}) ===`); level.items.forEach(item => { exports.printTree(item, evaluated); }); } let topLevelPrints = 0; if (tree.circularLinks.length > 0) { console.log(`=== CIRCULAR (${tree.circularLinks.length}) ===`); tree.circularLinks.forEach(item => { if (exports.printTree(item, evaluated)) { topLevelPrints++; } }); } console.log(`Finished. Circular Dependencies printed: ${topLevelPrints}`); };