nielse63
2/11/2020 - 4:08 PM

Document styleguide

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;