mAAdhaTTah/brookjs

View on GitHub
packages/brookjs-cli/src/eslint/index.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { CLIEngine } from 'eslint';
import Kefir, { Stream, Observable } from 'kefir';
import { createAsyncAction, ActionType } from 'typesafe-actions';
import { Delta } from 'brookjs-types';
import { sampleByAction } from 'brookjs-flow';
import { service as fs } from '../fs';

export const actions = {
  project: createAsyncAction(
    'LINT_PROJECT_REQUESTED',
    'LINT_PROJECT_SUCCEEDED',
    'LINT_PROJECT_FAILED',
  )<void, void, void>(),
  file: createAsyncAction(
    'LINT_FILE_REQUESTED',
    'LINT_FILE_SUCCEEDED',
    'LINT_FILE_FAILED',
  )<
    void,
    { path: string; report: CLIEngine.LintReport },
    { path: string; error: Error }
  >(),
};

type State = {
  cwd: string;
  paths: string[];
};

type Action = ActionType<typeof actions>;

export const delta: Delta<Action, State> = (action$, state$) => {
  return state$
    .thru(sampleByAction(action$, actions.project.request))
    .flatMap(state => {
      const eslint = ESLintService.create({ cwd: state.cwd, fix: false });

      return Kefir.concat<Action, never>([
        Kefir.merge(
          state.paths.map(path =>
            eslint
              .check(path)
              .map(report => actions.file.success({ path, report }))
              .flatMapErrors(error =>
                Kefir.constant(actions.file.failure({ path, error })),
              ),
          ),
        ),
        Kefir.constant(actions.project.success()),
      ]);
    });
};

export class ESLintService {
  private engine: CLIEngine;

  static create(opts: { cwd: string; fix: boolean }) {
    return new ESLintService(opts);
  }

  private constructor({ cwd, fix }: { cwd: string; fix: boolean }) {
    this.engine = new CLIEngine({
      baseConfig: {
        extends: 'brookjs',
      },
      cwd,
      extensions: ['js', 'jsx', 'mjs', 'ts', 'tsx'],
      fix,
      reportUnusedDisableDirectives: true,
      useEslintrc: false,
    });
  }

  check(
    path: string,
    buffer?: Buffer | string,
  ): Stream<CLIEngine.LintReport, Error> {
    const buffer$: Observable<string, NodeJS.ErrnoException> =
      buffer != null
        ? Kefir.constant(
            typeof buffer === 'string' ? buffer : buffer.toString('utf-8'),
          )
        : fs.readFile(path).map(buffer => buffer.toString('utf-8'));

    return buffer$.map(contents => this.engine.executeOnText(contents, path));
  }
}