Repository: oppia/oppia

Issue:
Refactor the js_ts_linter to run eslint checks on all files at once
Currently, we run the eslint checks separately on each file, this is quite inefficient and blocks us from introducing eslint rules that require the whole project to be compiled (since for these rules we now need to compile the whole project for all the files separately, if we ran the checks on all files together the project would be only compiled once).

Refactor js_ts_linter to run the eslint check on all the files at once.
Refactor the js_ts_linter to run eslint checks on all files at once
Currently, we run the eslint checks separately on each file, this is quite inefficient and blocks us from introducing eslint rules that require the whole project to be compiled (since for these rules we now need to compile the whole project for all the files separately, if we ran the checks on all files together the project would be only compiled once).

Refactor js_ts_linter to run the eslint check on all the files at once.


Discussion:

Resolving patch:
diff --git a/scripts/linters/js_ts_linter.py b/scripts/linters/js_ts_linter.py
--- a/scripts/linters/js_ts_linter.py
+++ b/scripts/linters/js_ts_linter.py
@@ -1121,35 +1121,26 @@ def _lint_js_and_ts_files(self):
                 'and its dependencies.')
 
         files_to_lint = self.all_filepaths
-        num_files_with_errors = 0
         error_messages = []
         full_error_messages = []
         failed = False
         name = 'ESLint'
 
         eslint_cmd_args = [node_path, eslint_path, '--quiet']
-        result_list = []
-        for _, filepath in enumerate(files_to_lint):
-            proc_args = eslint_cmd_args + [filepath]
-            proc = subprocess.Popen(
-                proc_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-
-            encoded_linter_stdout, encoded_linter_stderr = proc.communicate()
-            linter_stdout = encoded_linter_stdout.decode(encoding='utf-8')
-            linter_stderr = encoded_linter_stderr.decode(encoding='utf-8')
-            if linter_stderr:
-                raise Exception(linter_stderr)
-
-            if linter_stdout:
-                num_files_with_errors += 1
-                result_list.append(linter_stdout)
-
-        if num_files_with_errors:
+        proc_args = eslint_cmd_args + files_to_lint
+        proc = subprocess.Popen(
+            proc_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+        encoded_linter_stdout, encoded_linter_stderr = proc.communicate()
+        linter_stdout = encoded_linter_stdout.decode(encoding='utf-8')
+        linter_stderr = encoded_linter_stderr.decode(encoding='utf-8')
+        if linter_stderr:
+            raise Exception(linter_stderr)
+
+        if linter_stdout:
             failed = True
-            for result in result_list:
-                full_error_messages.append(result)
-                error_messages.append(
-                    self._get_trimmed_error_output(result))
+            full_error_messages.append(linter_stdout)
+            error_messages.append(self._get_trimmed_error_output(linter_stdout))
 
         return concurrent_task_utils.TaskResult(
             name, failed, error_messages, full_error_messages)


Test patch:
diff --git a/scripts/linters/js_ts_linter_test.py b/scripts/linters/js_ts_linter_test.py
--- a/scripts/linters/js_ts_linter_test.py
+++ b/scripts/linters/js_ts_linter_test.py
@@ -490,10 +490,19 @@ def test_third_party_linter(self):
         self.validate(lint_task_report, expected_messages, 1)
 
     def test_third_party_linter_with_stderr(self):
-        with self.assertRaisesRegexp(Exception, 'Oops! Something went wrong'):
-            js_ts_linter.ThirdPartyJsTsLintChecksManager(
-                INVALID_SORTED_DEPENDENCIES_FILEPATH
-            ).perform_all_lint_checks()
+        process = subprocess.Popen(['test'], stdout=subprocess.PIPE)
+        def mock_popen(unused_cmd, stdout, stderr):  # pylint: disable=unused-argument
+            return process
+        def mock_communicate(unused_self):
+            return ('Output', 'Invalid')
+        popen_swap = self.swap(subprocess, 'Popen', mock_popen)
+        communicate_swap = self.swap(
+            subprocess.Popen, 'communicate', mock_communicate)
+        with popen_swap, communicate_swap:
+            with self.assertRaisesRegexp(Exception, 'Invalid'):
+                js_ts_linter.ThirdPartyJsTsLintChecksManager(
+                    [INVALID_SORTED_DEPENDENCIES_FILEPATH]
+                ).perform_all_lint_checks()
 
     def test_third_party_linter_with_invalid_eslint_path(self):
         def mock_exists(unused_path):