133 lines
4.2 KiB
JavaScript
133 lines
4.2 KiB
JavaScript
const fs = require('fs');
|
|
const pathLib = require('path');
|
|
const inquirer = require('inquirer');
|
|
const { default: traverse } = require('@babel/traverse');
|
|
const { InputDataService } = require('../program/services/InputDataService.js');
|
|
const { AstService } = require('../program/services/AstService.js');
|
|
const { LogService } = require('../program/services/LogService.js');
|
|
const JsdocCommentParser = require('../program/utils/jsdoc-comment-parser.js');
|
|
|
|
/**
|
|
* Extracts name, defaultValue, optional, type, desc from JsdocCommentParser.parse method
|
|
* result
|
|
* @param {string[]} jsdoc
|
|
* @returns {{ name:string, defaultValue:string, optional:boolean, type:string, desc:string }}
|
|
*/
|
|
function getPropsFromParsedJsDoc(jsdoc) {
|
|
const jsdocProps = jsdoc.filter(p => p.tagName === '@property');
|
|
const options = jsdocProps.map(({ tagValue }) => {
|
|
// eslint-disable-next-line no-unused-vars
|
|
const [_, type, nameOptionalDefault, desc] = tagValue.match(/\{(.*)\}\s*([^\s]*)\s*(.*)/);
|
|
let nameDefault = nameOptionalDefault;
|
|
let optional = false;
|
|
if (nameOptionalDefault.startsWith('[') && nameOptionalDefault.endsWith(']')) {
|
|
optional = true;
|
|
nameDefault = nameOptionalDefault.slice(1).slice(0, -1);
|
|
}
|
|
const [name, defaultValue] = nameDefault.split('=');
|
|
return { name, defaultValue, optional, type, desc };
|
|
});
|
|
return options;
|
|
}
|
|
|
|
function getAnalyzerOptions(file) {
|
|
const code = fs.readFileSync(file, 'utf8');
|
|
const ast = AstService.getAst(code, 'babel', { filePath: file });
|
|
|
|
let commentNode;
|
|
traverse(ast, {
|
|
// eslint-disable-next-line no-shadow
|
|
VariableDeclaration(path) {
|
|
if (!path.node.leadingComments) {
|
|
return;
|
|
}
|
|
const decls = path.node.declarations || [];
|
|
decls.forEach(decl => {
|
|
if (decl && decl.id && decl.id.name === 'cfg') {
|
|
[commentNode] = path.node.leadingComments;
|
|
}
|
|
});
|
|
},
|
|
});
|
|
|
|
if (commentNode) {
|
|
const jsdoc = JsdocCommentParser.parse(commentNode);
|
|
return getPropsFromParsedJsDoc(jsdoc);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function gatherAnalyzers(dir, getConfigOptions) {
|
|
return InputDataService.gatherFilesFromDir(dir, { depth: 0 }).map(file => {
|
|
const analyzerObj = { file, name: pathLib.basename(file, '.js') };
|
|
if (getConfigOptions) {
|
|
analyzerObj.options = getAnalyzerOptions(file);
|
|
}
|
|
return analyzerObj;
|
|
});
|
|
}
|
|
|
|
async function promptAnalyzerConfigMenu(
|
|
analyzerName,
|
|
promptOptionalConfig,
|
|
dir = pathLib.resolve(__dirname, '../program/analyzers'),
|
|
) {
|
|
const menuOptions = gatherAnalyzers(dir, true);
|
|
const analyzer = menuOptions.find(o => o.name === analyzerName);
|
|
if (!analyzer) {
|
|
LogService.error(`[promptAnalyzerConfigMenu] analyzer "${analyzerName}" not found.`);
|
|
process.exit(1);
|
|
}
|
|
let configAnswers;
|
|
if (analyzer.options) {
|
|
configAnswers = await inquirer.prompt(
|
|
analyzer.options
|
|
.filter(a => promptOptionalConfig || !a.optional)
|
|
.map(a => ({
|
|
name: a.name,
|
|
message: a.description,
|
|
...(a.defaultValue ? { default: a.defaultValue } : {}),
|
|
})),
|
|
);
|
|
|
|
Object.entries(configAnswers).forEach(([key, value]) => {
|
|
const { type } = analyzer.options.find(o => o.name === key);
|
|
if (type.toLowerCase() === 'boolean') {
|
|
configAnswers[key] = value === 'false' ? false : Boolean(value);
|
|
} else if (type.toLowerCase() === 'number') {
|
|
configAnswers[key] = Number(value);
|
|
} else if (type.toLowerCase() !== 'string') {
|
|
if (value) {
|
|
configAnswers[key] = JSON.parse(value);
|
|
} else {
|
|
// Make sure to not override predefined values with undefined ones
|
|
delete configAnswers[key];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return {
|
|
analyzerConfig: configAnswers,
|
|
};
|
|
}
|
|
|
|
async function promptAnalyzerMenu(dir = pathLib.resolve(__dirname, '../program/analyzers')) {
|
|
const menuOptions = gatherAnalyzers(dir);
|
|
const answers = await inquirer.prompt([
|
|
{
|
|
type: 'list',
|
|
name: 'analyzerName',
|
|
message: 'Which analyzer do you want to run?',
|
|
choices: menuOptions.map(o => o.name),
|
|
},
|
|
]);
|
|
return {
|
|
analyzerName: answers.analyzerName,
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
promptAnalyzerMenu,
|
|
promptAnalyzerConfigMenu,
|
|
};
|