Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

eslint: howto lint only touched files

I have recently added eslint, as webpack loader, in a codebase that was never parsed with a linter before.

Obviously the amount of errors triggered are endless: there is any chance to configure eslint to parse only the touched files? I would like the linter to parse every file in which developers make changes and those only.

This is the loader I am using so far (in case can be of interest), very standard configuration:

{test: /\.(jsx|js)$/, loader: "eslint-loader?{cache: true}", exclude: /node_modules/}

Thank you

like image 876
lzzluca Avatar asked Oct 29 '22 17:10

lzzluca


2 Answers

I accomplished it by using a watcher; this is the solution in the details:

dependencies for the Webpack configuration:

var logger = require('reliable-logger');
var watch = require('watch');
var CLIEngine =  require('eslint').CLIEngine

watcher and linter configuration and start; I am pasting it with all the todos, as it is:

var configureLinterAndWatchFiles = function() {
var changedFiles = [];
var formatter;
var report;
var SEPARATOR = "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////";

// TODO I got the feeling that one of those settings is breaking the
//   linter (probably the path resolving?)
var linter =  new CLIEngine({
    // TODO do I need this? Looks like I don't...
    // envs: ["node"],
    // TODO what is the default?
    useEslintrc: true,
    // TODO I find weird that I get no error with this: configFile: "../.eslintrc1111"
    //  make sure that the configuration file is correctly picked up
    configFile: ".eslintrc",
    // TODO useless if your root is src
    // ignorePath: "node_modules"
    // TODO probably both useless... the first I still don't get it,
    //   the second you are enforcing the filtering yourself by checks
    // cache: false,
    // extensions: [".js", ".jsx"]
});

var fileUpdatedFn = function(f) {
    // TODO I would prefer much more to get the list of changed files from
    //   git status (how to?). Here I am building my own
    // resetting the array only for debug purpose
    // changedFiles = [];
    if(/.js$/.test(f) || /.jsx$/.test(f)) {
        changedFiles.push(f);
        logger.info(SEPARATOR);
        report = linter.executeOnFiles(changedFiles);
        logger.info(formatter(report.results));
    }
};

// get the default formatter
formatter = linter.getFormatter();

watch.watchTree('src', function(f, curr, prev) {
    if (typeof f == "object" && prev === null && curr === null) {
    // Finished walking the tree
    } else if (prev === null) {
    // f is a new file
    } else if (curr.nlink === 0) {
    // f was removed
    } else {
        // f was changed
        fileUpdatedFn(f);
    }
});

};

in module.exports, as last line:

module.exports = function(callback, options){
  // ... more code ...
  configureLinterAndWatchFiles();
}

That should be it. As I pointed out in a comment:

I wonder, though, if the cache flag (eslint.org/docs/developer-guide/nodejs-api#cliengine) was the best to be used for the problem. From here (github.com/adametry/gulp-eslint/issues/…): "--cache flag will skip over any files that had no problems in the previous run unless they have been modified": not sure if that is my case but is of interest.

like image 53
lzzluca Avatar answered Nov 13 '22 19:11

lzzluca


Definitively I'm a little late for the party, but I faced the very same issue today & it seems like there is still no common solution for that.

I ended up monkey patching webpack's devServer with this:

  const { exec } = require('child_process');
  
  // ...
  
  devServer: {
    hot: false,
    inline: false,
    publicPath: '/',
    historyApiFallback: true,
    disableHostCheck: true,
    after: (app, server, compiler) => {
      compiler.hooks.watchRun.tap(
        'EsLint-upon-save',
        () => {
          // This should only work in dev environment
          if (process.env.NODE_ENV !== 'development') {
            return;
          }
        
          // Credits to:
          // https://stackoverflow.com/a/43149576/9430588
          const filesChanged = Object.keys(compiler.watchFileSystem.watcher.mtimes);
          
          // Might be empty
          if (!filesChanged.length) {
            return;
          }

          filesChanged.forEach((changedFileAbsolutePath) => {
            const extension = changedFileAbsolutePath.split('.').pop();

            if (extension === 'js' || extension === 'jsx') {
              exec(`npx eslint --fix --fix-type suggestion,layout ${changedFileAbsolutePath}`); 
            }
          });
        }
      );
    }
  },

It's surely quite quick & dirty type of solution, however it seems to work fine with [email protected].

like image 35
Igor Bykov Avatar answered Nov 13 '22 19:11

Igor Bykov