Using the existing eslint rules, create a markdown file documenting all the rules that are being used, and those that could be used.
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const eslint = require('eslint');
const table = require('markdown-table');
const _ = require('lodash');
// filepaths
const root = path.resolve(__dirname, '..');
const sampleFile = path.resolve(root, 'src/main/plugins/com.glide.sg-studio/ui.html/scripts/App.js');
const configFile = path.resolve(root, '.eslintrc.js');
const ignoreFile = path.resolve(root, '.eslintignore');
const mdFile = path.resolve(root, 'docs/style-guide.md');
// shared global constants
const WARNING_LEVELS = ['💚 Off', '⚠️ Warning', '❗️ Error'];
// functions
function getLevel(value) {
let level = value;
let output = 'off';
if (Array.isArray(level)) {
level = level[0];
}
if (typeof level === 'number') {
output = WARNING_LEVELS[level];
}
return { level: output };
}
function createRuleDocs(docs, includeRecommended = false) {
const primer = {
recommended: '',
description: '',
url: '',
};
if (!includeRecommended) {
delete primer.recommended;
}
return Object.entries(docs).reduce((object, [key, value]) => {
let displayValue = value;
if (key === 'recommended') {
displayValue = value ? ':white_check_mark:' : '';
}
if (Object.keys(primer).includes(key)) {
object[key] = displayValue;
}
return object;
}, primer);
}
function createRulesObject() {
const cli = new eslint.CLIEngine({
useEslintrc: true,
cwd: root,
configFile,
ignorePath: ignoreFile,
});
const allEslintRules = cli.getRules();
const { rules } = cli.getConfigForFile(sampleFile);
const output = {};
const unusedRules = {};
allEslintRules.forEach((value, rule) => {
const ruleConfig = rules[rule];
const { docs } = value.meta;
if (!ruleConfig) {
unusedRules[rule] = {
rule,
...createRuleDocs(docs, true),
};
return;
}
const category = docs.category ? docs.category : ( // eslint-disable-line no-nested-ternary
rule.includes('/') ? rule.split('/')[0] : ''
);
if (!category) {
console.log({ rule });
return;
}
if (!output[category]) {
output[category] = {};
}
output[category][rule] = {
rule,
...getLevel(ruleConfig),
...createRuleDocs(docs),
};
});
return {
...output,
'Unused Rules': unusedRules,
};
}
function ifIsLowerCase(string) {
const char = string[0];
return char === char.toLowerCase();
}
function generateTitle(string) {
return ifIsLowerCase(string) ? _.capitalize(string) : string;
}
function createMarkdown() {
const output = createRulesObject();
const toc = Object.keys(output).map((title) => {
const text = generateTitle(title);
const link = `#${_.kebabCase(title)}`;
return `- [${text}](${link})`;
});
const md = [
'# Style Guide',
'',
...toc,
'',
];
Object.entries(output).forEach(([category, rules]) => {
const title = generateTitle(category);
md.push(`## ${title}`, '');
const columns = [];
const columnTitles = {
url: 'Link to Docs',
};
const tableArray = [
...Object.values(rules).map((ruleConfig) => Object.entries(ruleConfig).map(([key, value]) => {
const columnTitle = columnTitles[key] || _.capitalize(key);
if (!columns.includes(columnTitle)) {
columns.push(columnTitle);
}
if (key === 'url') {
return `[View docs online](${value})`;
}
if (key === 'rule') {
return `**${value}**`;
}
return value;
})),
];
md.push(table([
columns,
...tableArray,
]), '');
});
return md.join('\n');
}
function documentStyleguide() {
const markdown = createMarkdown();
fs.writeFileSync(mdFile, markdown);
console.log(`${path.basename(mdFile)} has been updated`);
}
if (!module.parent) {
documentStyleguide();
}
module.exports = documentStyleguide;