1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. 'use strict';
    
  9. 
    
  10. const minimatch = require('minimatch');
    
  11. const {ESLint} = require('eslint');
    
  12. const listChangedFiles = require('../shared/listChangedFiles');
    
  13. 
    
  14. const allPaths = ['**/*.js'];
    
  15. 
    
  16. let changedFiles = null;
    
  17. 
    
  18. async function runESLintOnFilesWithOptions(filePatterns, onlyChanged, options) {
    
  19.   const eslint = new ESLint(options);
    
  20.   const formatter = await eslint.loadFormatter();
    
  21. 
    
  22.   if (onlyChanged && changedFiles === null) {
    
  23.     // Calculate lazily.
    
  24.     changedFiles = [...listChangedFiles()];
    
  25.   }
    
  26.   const finalFilePatterns = onlyChanged
    
  27.     ? intersect(changedFiles, filePatterns)
    
  28.     : filePatterns;
    
  29.   const results = await eslint.lintFiles(finalFilePatterns);
    
  30. 
    
  31.   if (options != null && options.fix === true) {
    
  32.     await ESLint.outputFixes(results);
    
  33.   }
    
  34. 
    
  35.   // When using `ignorePattern`, eslint will show `File ignored...` warnings for any ignores.
    
  36.   // We don't care because we *expect* some passed files will be ignores if `ignorePattern` is used.
    
  37.   const messages = results.filter(item => {
    
  38.     if (!onlyChanged) {
    
  39.       // Don't suppress the message on a full run.
    
  40.       // We only expect it to happen for "only changed" runs.
    
  41.       return true;
    
  42.     }
    
  43.     const ignoreMessage =
    
  44.       'File ignored because of a matching ignore pattern. Use "--no-ignore" to override.';
    
  45.     return !(item.messages[0] && item.messages[0].message === ignoreMessage);
    
  46.   });
    
  47. 
    
  48.   const errorCount = results.reduce(
    
  49.     (count, result) => count + result.errorCount,
    
  50.     0
    
  51.   );
    
  52.   const warningCount = results.reduce(
    
  53.     (count, result) => count + result.warningCount,
    
  54.     0
    
  55.   );
    
  56.   const ignoredMessageCount = results.length - messages.length;
    
  57.   return {
    
  58.     output: formatter.format(messages),
    
  59.     errorCount: errorCount,
    
  60.     warningCount: warningCount - ignoredMessageCount,
    
  61.   };
    
  62. }
    
  63. 
    
  64. function intersect(files, patterns) {
    
  65.   let intersection = [];
    
  66.   patterns.forEach(pattern => {
    
  67.     intersection = [
    
  68.       ...intersection,
    
  69.       ...minimatch.match(files, pattern, {matchBase: true}),
    
  70.     ];
    
  71.   });
    
  72.   return [...new Set(intersection)];
    
  73. }
    
  74. 
    
  75. async function runESLint({onlyChanged, ...options}) {
    
  76.   if (typeof onlyChanged !== 'boolean') {
    
  77.     throw new Error('Pass options.onlyChanged as a boolean.');
    
  78.   }
    
  79.   const {errorCount, warningCount, output} = await runESLintOnFilesWithOptions(
    
  80.     allPaths,
    
  81.     onlyChanged,
    
  82.     options
    
  83.   );
    
  84.   console.log(output);
    
  85.   return errorCount === 0 && warningCount === 0;
    
  86. }
    
  87. 
    
  88. module.exports = runESLint;