Shuunen/repo-checker

View on GitHub
src/check.ts

Summary

Maintainability
A
55 mins
Test Coverage
/* c8 ignore next */
import { ellipsis, green, red, yellow } from 'shuutils'
import type { ProjectData } from './constants'
import { DependencyCruiserFile, EditorConfigFile, EsLintFile, GitFile, GithubWorkflowFile, LicenseFile, NpmRcFile, NvmrcFile, NycRcFile, PackageJsonFile, ReadmeFile, RenovateFile, RepoCheckerConfigFile, TailwindFile, TravisFile, TsConfigFile } from './files/index.js'
import { log } from './logger'
import { augmentData, getProjectFolders } from './utils'

interface Indicators { failed: readonly string[]; passed: readonly string[]; warnings: readonly string[] }

const checkers = [
  DependencyCruiserFile,
  EditorConfigFile,
  EsLintFile,
  GitFile,
  GithubWorkflowFile,
  LicenseFile,
  NpmRcFile,
  NvmrcFile,
  NycRcFile,
  PackageJsonFile,
  ReadmeFile,
  RenovateFile,
  RepoCheckerConfigFile,
  TailwindFile,
  TravisFile,
  TsConfigFile,
]

function reportLog (color: (string: string) => string, count: number, message: string) {
  const line = `‣ ${count} check${count > 1 ? 's' : ''} ${message}`
  /* c8 ignore next */
  log.info(count === 0 ? line : color(line))
}

interface CheckOptions {
  canFailStop?: boolean
  canFix?: boolean
  canForce?: boolean
  canThrow?: boolean
  data: Readonly<ProjectData>
  folderPath: string
}

function report ({ failed = [], passed = [], warnings = [] }: Readonly<Indicators>) {
  log.info('Report :')
  reportLog(green, passed.length, 'are successful')
  reportLog(yellow, warnings.length, 'triggered warnings')
  reportLog(red, failed.length, 'are problematic')
  /* c8 ignore next */
}

// eslint-disable-next-line max-statements
export async function check ({ canFailStop = false, canFix = false, canForce = false, canThrow = true, data, folderPath }: Readonly<CheckOptions>) {
  const folders = await getProjectFolders(folderPath)
  let passed: string[] = []
  let warnings: string[] = []
  let failed: string[] = []
  log.options.isActive = !data.isQuiet
  /* c8 ignore next */
  if (folders.length === 0) log.warn('no folder to check', folderPath)
  /* eslint-disable no-await-in-loop */
  for (const folder of folders) {
    if (canFailStop && failed.length > 0) {
      log.warn('stop checking other folders because of failures & --fail-stop option')
      break
    }
    log.info('Checking folder :', folder)
    const dataFolder = await augmentData(folder, data, folders.length > 1)
    for (const Checker of checkers) { // eslint-disable-line @typescript-eslint/naming-convention
      const instance = new Checker(folder, dataFolder, canFix, canForce)
      await instance.start()
      await instance.end()
      passed = [...passed, ...instance.passed]
      warnings = [...warnings, ...instance.warnings]
      failed = [...failed, ...instance.failed]
    }
  }
  /* eslint-enable no-await-in-loop */
  report({ failed, passed, warnings })
  const maxLogLength = 100
  if (canThrow && failed.length > 0) throw new Error(`failed at validating at least one rule in one folder : ${ellipsis(failed.join(', '), maxLogLength)}`)
  return { failed, passed, warnings }
}