import Command, { Context } from 'common-bin'; import { join } from 'path'; import { writeFileSync } from 'fs'; import resolveBin from 'resolve-bin'; import options from './options'; import getUserConfig, { CONFIG_FILES } from './getUserConfig'; import { Arguments, PlainObject } from './types'; import registerBabel from './registerBabel'; import { getFiles, parseSubOptions, getEsLintExtensions, endsWithArray } from './utils'; const debug = require('debug')('lotus-lint'); class MainCommand extends Command { readonly eslint: string; readonly stylelint: string; readonly prettier: string; constructor(rawArgv) { super(rawArgv); // @ts-ignore this.options = options; this.eslint = resolveBin.sync('eslint'); this.prettier = resolveBin.sync('prettier'); this.stylelint = resolveBin.sync('stylelint'); this.usage = ` Usage: lotus-lint [options] file.js [file.js] [dir] lotus-lint --prettier --stylelint src/ lotus-lint --staged --prettier --stylelint lotus-lint --eslint.debug --tslint.force -s.formatter=json -p.no-semi src/ test/ `; } *run(context: Context) { const { staged } = context.argv; if (!staged) { yield this.lint(context.argv); } else { yield this.lintStaged(context.argv); } yield this.lint(context.argv); } *lint( args: Arguments ) { const { _, eslint, stylelint, prettier, fix, quiet, cwd } = args; if (_.length === 0) { console.log('please specify a path to lint'); return; } registerBabel({ cwd, only: CONFIG_FILES }); const userConfig = getUserConfig({ cwd }); const commonOpts = [...(fix ? ['--fix'] : []), ...(quiet ? ['--quiet'] : [])]; const allFiles = getFiles(_, cwd); try { const jobs = []; if (eslint) { const esLintOptions = parseSubOptions(eslint); const esLintExtensions = getEsLintExtensions(esLintOptions); const files = allFiles.filter(item => endsWithArray(item, esLintExtensions)); if (files.length > 0) { jobs.push( this.helper.forkNode( this.eslint, [...commonOpts, ...parseSubOptions(eslint), ...files], { cwd, }, ), ); } } if (stylelint) { const files = allFiles.filter(item => endsWithArray(item, ['.css', '.less', '.scss', '.sass']), ); if (files.length > 0) { jobs.push( this.helper.forkNode( this.stylelint, [...commonOpts, ...parseSubOptions(stylelint), ...files], { cwd, }, ), ); } } if (prettier) { const files = allFiles.filter(item => endsWithArray(item, ['.js', '.jsx', '.ts', '.tsx', '.css', '.less', '.scss', '.sass']), ); if (files.length > 0) { jobs.push( this.helper.forkNode( this.prettier, [ ...(process.env.FROM_TEST === 'true' ? [] : ['--write']), ...parseSubOptions(prettier), ...files, ], { cwd }, ), ); } } yield Promise.all(jobs); } catch (error) { debug(error); process.exit(error.code); } } *lintStaged( args: Arguments ) { const { eslint, stylelint, prettier, fix, quiet, cwd } = args; const lintStaged = resolveBin('lint-staged'); const commonOpts = `${fix ? '--fix' : ''} ${quiet ? '--quiet' : ''}`; const eslintOptions = parseSubOptions(eslint); const eslintExtensions = getEsLintExtensions(eslintOptions); // generate dynamic configuration const lintstagedrc = { ...(prettier && { '*.{js,jsx,ts,tsx,less,scss,sass,css}': [ `${this.prettier} --write ${parseSubOptions(prettier).join(' ')}`, 'git add', ], }), ...(eslint && { [`*{${eslintExtensions.join(',')}}`]: [ `${this.eslint} ${commonOpts} ${parseSubOptions(eslint).join(' ')}`, 'git add', ], }), ...(stylelint && { '*.{less,scss,sass,css}': [ `${this.stylelint} ${commonOpts} ${parseSubOptions(stylelint).join(' ')}`, 'git add', ], }), }; const rcPath = join(__dirname, '.lintstagedrc.json'); writeFileSync(rcPath, JSON.stringify(lintstagedrc)); try { yield this.helper.forkNode(lintStaged, ['-c', rcPath], { cwd }); } catch (error) { debug(error); process.exit(error.code); } } } module.exports = MainCommand;