lint.js 4.15 KB
Newer Older
liang ce committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
const fs = require('fs')
const globby = require('globby')

const renamedArrayArgs = {
  ext: 'extensions',
  env: 'envs',
  global: 'globals',
  rulesdir: 'rulePaths',
  plugin: 'plugins',
  'ignore-pattern': 'ignorePattern'
}

const renamedArgs = {
  'inline-config': 'allowInlineConfig',
  rule: 'rules',
  eslintrc: 'useEslintrc',
  c: 'configFile',
  config: 'configFile'
}

module.exports = function lint (args = {}, api) {
  const path = require('path')
  const cwd = api.resolve('.')
  const { log, done, exit, chalk, loadModule } = require('@vue/cli-shared-utils')
  const { CLIEngine } = loadModule('eslint', cwd, true) || require('eslint')
  const extensions = require('./eslintOptions').extensions(api)

  const argsConfig = normalizeConfig(args)
  const config = Object.assign({
    extensions,
    fix: true,
    cwd
  }, argsConfig)

  const noFixWarnings = (argsConfig.fixWarnings === false)
  const noFixWarningsPredicate = (lintResult) => lintResult.severity === 2
  config.fix = config.fix && (noFixWarnings ? noFixWarningsPredicate : true)

  if (!fs.existsSync(api.resolve('.eslintignore')) && !config.ignorePattern) {
    // .eslintrc.js files (ignored by default)
    // However, we need to lint & fix them so as to make the default generated project's
    // code style consistent with user's selected eslint config.
    // Though, if users provided their own `.eslintignore` file, we don't want to
    // add our own customized ignore pattern here (in eslint, ignorePattern is
    // an addition to eslintignore, i.e. it can't be overriden by user),
    // following the principle of least astonishment.
    config.ignorePattern = [
      '!.*.js',
      '!{src,tests}/**/.*.js'
    ]
  }

  const engine = new CLIEngine(config)

  const defaultFilesToLint = [
    'src',
    'tests',
    // root config files
    '*.js',
    '.*.js'
  ]
    .filter(pattern =>
      globby
        .sync(path.join(cwd, pattern))
        .some(p => !engine.isPathIgnored(p))
    )

  const files = args._ && args._.length
    ? args._
    : defaultFilesToLint

  // mock process.cwd before executing
  // See:
  // https://github.com/vuejs/vue-cli/issues/2554
  // https://github.com/benmosher/eslint-plugin-import/issues/602
  // https://github.com/eslint/eslint/issues/11218
  const processCwd = process.cwd
  if (!api.invoking) {
    process.cwd = () => cwd
  }
  const report = engine.executeOnFiles(files)
  process.cwd = processCwd

  const formatter = engine.getFormatter(args.format || 'codeframe')

  if (config.fix) {
    CLIEngine.outputFixes(report)
  }

  const maxErrors = argsConfig.maxErrors || 0
  const maxWarnings = typeof argsConfig.maxWarnings === 'number' ? argsConfig.maxWarnings : Infinity
  const isErrorsExceeded = report.errorCount > maxErrors
  const isWarningsExceeded = report.warningCount > maxWarnings

  if (!isErrorsExceeded && !isWarningsExceeded) {
    if (!args.silent) {
      const hasFixed = report.results.some(f => f.output)
      if (hasFixed) {
        log(`The following files have been auto-fixed:`)
        log()
        report.results.forEach(f => {
          if (f.output) {
            log(`  ${chalk.blue(path.relative(cwd, f.filePath))}`)
          }
        })
        log()
      }
      if (report.warningCount || report.errorCount) {
        console.log(formatter(report.results))
      } else {
        done(hasFixed ? `All lint errors auto-fixed.` : `No lint errors found!`)
      }
    }
  } else {
    console.log(formatter(report.results))
    if (isErrorsExceeded && typeof argsConfig.maxErrors === 'number') {
      log(`Eslint found too many errors (maximum: ${argsConfig.maxErrors}).`)
    }
    if (isWarningsExceeded) {
      log(`Eslint found too many warnings (maximum: ${argsConfig.maxWarnings}).`)
    }
    exit(1)
  }
}

function normalizeConfig (args) {
  const config = {}
  for (const key in args) {
    if (renamedArrayArgs[key]) {
      config[renamedArrayArgs[key]] = args[key].split(',')
    } else if (renamedArgs[key]) {
      config[renamedArgs[key]] = args[key]
    } else if (key !== '_') {
      config[camelize(key)] = args[key]
    }
  }
  return config
}

function camelize (str) {
  return str.replace(/-(\w)/g, (_, c) => c ? c.toUpperCase() : '')
}