Skip to content

Instantly share code, notes, and snippets.

@robostheimer
Last active November 1, 2023 14:34
Show Gist options
  • Save robostheimer/9b5b6324e530fde35f2989515ba211a8 to your computer and use it in GitHub Desktop.
Save robostheimer/9b5b6324e530fde35f2989515ba211a8 to your computer and use it in GitHub Desktop.
Node script that converts json files created from `rehearsal graph`(via `rehearsal-js`) running on any directories user supplies (i.e. packages, lib, app, tests, etc.) and creates a spreadsheet using `json2csv`, and `SheetsJS `.
/**
* Node script that converts json files created from `rehearsal graph`(via `rehearsal-js`) running on
* from user supplies (packages, lib, app, tests, etc.) and creates a spreadsheet using `json2csv`, and `SheetsJS `.
* It also exports png visualizations of your graph.
* Simply add a `graph-scripts` directory to the root of your ember app, add this gist. Run `node graph-to-spreadsheet.js`
* with a list of directories to graph individual directories inside of your app.
*
* NOTE, you will need to flag directories with subdirectories that should be graphed, by using `--subdirectories`
* flag (see examples below) to get the graph of all subdirectories inside of a root directory of your app/addon.
*
* run `node graph-scripts/graph-to-spreadsheet.js app,lib--subdirectories,packages--subdirectories,tests,mirage,test-package`
* to get graphs of your ember directories
*
* run `node graph-scripts/graph-to-spreadsheet.js app,lib--subdirectories,packages--subdirectories,tests,mirage,test-package --externals`
* to get graphs of your ember directories that includes packages external to your package (i.e. node_modules packages).
*
* run `node graph-scripts/graph-to-spreadsheet.js app,lib--subdirectories,packages--subdirectories,tests,mirage,test-packages --getDiff <path-to-spreadsheet-to-compare>`
* to compare your current graph to a previous graph
*
* run `node graph-scripts/graph-to-spreadsheet.js packages--subdirectories --withViz --getDiff <path-to-spreadsheet>`
*
* NOTE, for sheet-js to work, you will likely need to add `"sheetjs": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz"` to your package.json
*
*/
const fs = require('fs-extra');
const { exec } = require('child_process');
const rehearsal = (...args) =>
import('@rehearsal/cli/dist/src/index.js').then(({ default: rehearsal }) => rehearsal(...args));
const {
Parser,
transforms: { unwind, flatten }
} = require('json2csv');
const { isEqual } = require('lodash');
const XLSX = require('sheetjs');
const process = require('process');
const renderDot = (...args) =>
import('render-dot/cli/dist/src/index.js').then(({ default: renderDot }) => renderDot(...args));
const workBook = XLSX.utils.book_new();
// need to refactor so ordering of the flags does not matter
const directoriesToGraph = process.argv[2].split(',')
const getDiff = process.argv.includes('--getDiff') ?? false
const indexOfDiffUrl = process.argv.indexOf('--getDiff') +1
const previousWorkBook = getDiff ? XLSX.readFile(process.argv[indexOfDiffUrl]) : XLSX.utils.book_new();
const withViz = process.argv.includes('--withViz') ?? false;
const withExternalModules = process.argv.includes('--externals') ?? false;
console.log(process.argv)
const changedWorkBook = XLSX.utils.book_new();
const getGraphJSON = () => {
fs.readdir('./', (err, directories) => {
directoriesToGraph.forEach((directory, index) => {
if(directory.match('--subdirectories')) {
const directoryWoFlag = directory.replace('--subdirectories', '');
fs.mkdirSync(`graph-scripts/json/${directoryWoFlag}`, { recursive: true }, (err) => {});
fs.mkdirSync(`graph-scripts/dots/${directoryWoFlag}`, { recursive: true }, (err) => {});
fs.mkdirSync(`graph-scripts/pngs/${directoryWoFlag}`, { recursive: true }, (err) => {});
fs.readdir(directoryWoFlag, async (err, directories) => {
directories.forEach((subDirectory) => {
const executionString = withExternalModules
? `yarn rehearsal graph ${directoryWoFlag}/${subDirectory} -o ./graph-scripts/json/${directoryWoFlag}/${subDirectory}.json --externals`
: `yarn rehearsal graph ${directoryWoFlag}/${subDirectory} -o ./graph-scripts/json/${directoryWoFlag}/${subDirectory}.json`;
exec(
executionString,
(error, stdout, stderr) => {
if (err) {
console.log(`json_error: ${error.message}`);
return;
}
if (stderr) {
console.log(`json_stderr: ${stderr}`);
return;
}
console.log(`json_stdout: ${stdout}`);
parseJSONFile(`./graph-scripts/json/${directoryWoFlag}/${subDirectory}.json`);
}
);
if(withViz) {
exec(
`yarn rehearsal graph ${directoryWoFlag}/${subDirectory} -o ./graph-scripts/dots/${directoryWoFlag}/${subDirectory}.dot`,
(error, stdout, stderr) => {
if (err) {
console.log(`dot_error: ${error.message}`);
return;
}
if (stderr) {
console.log(`dot_stderr: ${stderr}`);
return;
}
console.log(`dot_stdout: ${stdout}`);
createPNG(`./graph-scripts/dots/${directoryWoFlag}/${subDirectory}.dot`, directoryWoFlag);
}
);
}
});
});
} else {
fs.mkdirSync(`graph-scripts/json`, { recursive: true }, (err) => {});
fs.mkdirSync(`graph-scripts/dots`, { recursive: true }, (err) => {});
fs.mkdirSync(`graph-scripts/pngs`, { recursive: true }, (err) => {});
const executionString = withExternalModules ? `yarn rehearsal graph ${directory} -o ./graph-scripts/json/${directory}.json --externals` : `yarn rehearsal graph ${directory} -o ./graph-scripts/json/${directory}.json`
exec(executionString, (error, stdout, stderr) => {
if (err) {
console.log(`json_error: ${error.message}`);
return;
}
if (stderr) {
console.log(`json_stderr: ${stderr}`);
return;
}
console.log(`json_stdout: ${stdout}`);
parseJSONFile(`./graph-scripts/json/${directory}.json`);
});
if(withViz) {
exec(`yarn rehearsal graph ${directory} -o ./graph-scripts/dots/${directory}.dot`, (error, stdout, stderr) => {
if (err) {
console.log(`dot_error: ${error.message}`);
return;
}
if (stderr) {
console.log(`dot_stderr: ${stderr}`);
return;
}
console.log(`dot_stdout: ${stdout}`);
createPNG(`./graph-scripts/dots/${directory}.dot`);
});
}
}
});
});
};
async function parseJSONFile(fileName) {
try {
const file = await fs.readFile(fileName);
const json = await JSON.parse(file);
arrayToCSV(json, fileName);
} catch (err) {
console.log(err);
process.exit(1);
}
}
async function createPNG(fileName, directory='') {
await exec(
`render-dot --input ${fileName} --output ./graph-scripts/pngs/${directory} --format png --quality -s ALLOW_MEMORY_GROWTH=1`,
(error, stdout, stderr) => {
if (error) {
console.log(`png_error: ${error.message}`);
return;
}
if (stderr) {
console.log(`png_stderr: ${stderr}`);
return;
}
console.log(`png_stdout: ${stdout}`);
}
);
}
async function arrayToCSV(data, fileName) {
fs.mkdirSync('./graph-scripts/xlsx', { recursive: true }, (err) => {});
const json2csvParser = await new Parser({ transforms: [unwind({ paths: ['files', 'files.edges'] }), flatten('__')] });
const csv = await json2csvParser.parse(data);
const readCSV = await XLSX.read(csv, { type: 'string' });
const workSheet = readCSV.Sheets.Sheet1;
// sheetName cannot exceed 31 characters
const sheetName = fileName
.slice(fileName.lastIndexOf('/') + 1)
.replace(/.json/g, '')
.slice(0, 31);
const date = new Date();
if(getDiff) {
const previousCSV = XLSX.utils.sheet_to_csv(previousWorkBook.Sheets[sheetName]);
if (!isEqual(previousCSV.toLowerCase().trim().replace(/"/g, ''), csv.toLowerCase().trim().replace(/"/g, ''))) {
XLSX.utils.book_append_sheet(changedWorkBook, workSheet, sheetName);
XLSX.writeFile(
changedWorkBook,
`./graph-scripts/xlsx/Diff_Graph_${date.getMonth() +1}_${date.getDate()}_${date.getFullYear()}.xlsx`
);
}
}
XLSX.utils.book_append_sheet(workBook, workSheet, sheetName);
XLSX.writeFile(workBook, `./graph-scripts/xlsx/Graph_${date.getMonth() + 1}_${date.getDate()}_${date.getFullYear()}.xlsx`);
}
async function writeCSV(fileName, data) {
try {
await fs.writeFile(fileName, data, 'utf8');
} catch (err) {
console.log(err);
process.exit(1);
}
}
getGraphJSON();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment