Repository: nf-core/tools

Issue:
Prettier fails linting for default modules.json
### Description of the bug

The `modules.json` was created using `nf-core modules` and `nf-core subworkflows` commands using the latest code on `dev`. However, linting fails with Prettier which wants to put the `installed_by` section on a single line. We should either ignore this file or fix the default output from tools.

```diff
                    "ataqv/ataqv": {
                         "branch": "master",
                         "git_sha": "56421e1a812bc2f9e77dbe9f297e9d9c580cb8a5",
-                        "installed_by": [
-                            "modules"
-                        ]
+                        "installed_by": ["modules"]
                     },
```


Discussion:
I think we are moving to run Prettier automatically after any command modifying Prettier formatted files. Once we get this we can also remove some hacks from the code to comply with formatting that are applied to some file dumping functions.
In this respect it would be nice to get the actual invocations that mess up the files to start figuring out all the places we need to invoke Prettier.
Prettier PR now merged, so just a case of using that function on this file after modification.

Resolving patch:
diff --git a/nf_core/lint_utils.py b/nf_core/lint_utils.py
--- a/nf_core/lint_utils.py
+++ b/nf_core/lint_utils.py
@@ -72,8 +72,9 @@ def run_prettier_on_file(file):
         )
     except subprocess.CalledProcessError as e:
         if ": SyntaxError: " in e.stdout.decode():
-            raise ValueError(f"Can't format {file} because it has a synthax error.\n{e.stdout.decode()}") from e
-        raise ValueError(
-            "There was an error running the prettier pre-commit hook.\n"
-            f"STDOUT: {e.stdout.decode()}\nSTDERR: {e.stderr.decode()}"
-        ) from e
+            log.critical(f"Can't format {file} because it has a syntax error.\n{e.stdout.decode()}")
+        else:
+            log.warning(
+                "There was an error running the prettier pre-commit hook.\n"
+                f"STDOUT: {e.stdout.decode()}\nSTDERR: {e.stderr.decode()}"
+            )
diff --git a/nf_core/modules/modules_json.py b/nf_core/modules/modules_json.py
--- a/nf_core/modules/modules_json.py
+++ b/nf_core/modules/modules_json.py
@@ -13,6 +13,7 @@
 
 import nf_core.utils
 from nf_core.components.components_utils import get_components_to_install
+from nf_core.lint_utils import run_prettier_on_file
 from nf_core.modules.modules_repo import (
     NF_CORE_MODULES_NAME,
     NF_CORE_MODULES_REMOTE,
@@ -39,6 +40,7 @@ def __init__(self, pipeline_dir):
         self.dir = pipeline_dir
         self.modules_dir = Path(self.dir, "modules")
         self.subworkflows_dir = Path(self.dir, "subworkflows")
+        self.modules_json_path = Path(self.dir, "modules.json")
         self.modules_json = None
         self.pipeline_modules = None
         self.pipeline_subworkflows = None
@@ -62,7 +64,7 @@ def create(self):
         pipeline_config = nf_core.utils.fetch_wf_config(self.dir)
         pipeline_name = pipeline_config.get("manifest.name", "")
         pipeline_url = pipeline_config.get("manifest.homePage", "")
-        modules_json = {"name": pipeline_name.strip("'"), "homePage": pipeline_url.strip("'"), "repos": {}}
+        new_modules_json = {"name": pipeline_name.strip("'"), "homePage": pipeline_url.strip("'"), "repos": {}}
 
         if not self.modules_dir.exists():
             raise UserWarning("Can't find a ./modules directory. Is this a DSL2 pipeline?")
@@ -74,29 +76,26 @@ def create(self):
         repo_module_names = self.get_component_names_from_repo(repos, self.modules_dir)
         repo_subworkflow_names = self.get_component_names_from_repo(repos, self.subworkflows_dir)
 
-        # Add module/subworkflow info into modules_json
+        # Add module/subworkflow info
         for repo_url, module_names, install_dir in sorted(repo_module_names):
-            modules_json["repos"][repo_url] = {}
-            modules_json["repos"][repo_url]["modules"] = {}
-            modules_json["repos"][repo_url]["modules"][install_dir] = {}
-            modules_json["repos"][repo_url]["modules"][install_dir] = self.determine_branches_and_shas(
+            new_modules_json["repos"][repo_url] = {}
+            new_modules_json["repos"][repo_url]["modules"] = {}
+            new_modules_json["repos"][repo_url]["modules"][install_dir] = {}
+            new_modules_json["repos"][repo_url]["modules"][install_dir] = self.determine_branches_and_shas(
                 "modules", install_dir, repo_url, module_names
             )
         for repo_url, subworkflow_names, install_dir in sorted(repo_subworkflow_names):
-            if repo_url not in modules_json["repos"]:  # Don't overwrite the repo if it was already added by modules
-                modules_json["repos"][repo_url] = {}
-            modules_json["repos"][repo_url]["subworkflows"] = {}
-            modules_json["repos"][repo_url]["subworkflows"][install_dir] = {}
-            modules_json["repos"][repo_url]["subworkflows"][install_dir] = self.determine_branches_and_shas(
+            if repo_url not in new_modules_json["repos"]:  # Don't overwrite the repo if it was already added by modules
+                new_modules_json["repos"][repo_url] = {}
+            new_modules_json["repos"][repo_url]["subworkflows"] = {}
+            new_modules_json["repos"][repo_url]["subworkflows"][install_dir] = {}
+            new_modules_json["repos"][repo_url]["subworkflows"][install_dir] = self.determine_branches_and_shas(
                 "subworkflows", install_dir, repo_url, subworkflow_names
             )
 
         # write the modules.json file and assign it to the object
-        modules_json_path = Path(self.dir, "modules.json")
-        with open(modules_json_path, "w") as fh:
-            json.dump(modules_json, fh, indent=4)
-            fh.write("\n")
-        self.modules_json = modules_json
+        self.modules_json = new_modules_json
+        self.dump()
 
     def get_component_names_from_repo(self, repos, directory):
         """
@@ -617,9 +616,8 @@ def load(self):
         Raises:
             UserWarning: If the modules.json file is not found
         """
-        modules_json_path = os.path.join(self.dir, "modules.json")
         try:
-            with open(modules_json_path, "r") as fh:
+            with open(self.modules_json_path, "r") as fh:
                 self.modules_json = json.load(fh)
         except FileNotFoundError:
             raise UserWarning("File 'modules.json' is missing")
@@ -1025,10 +1023,10 @@ def dump(self):
         """
         # Sort the modules.json
         self.modules_json["repos"] = nf_core.utils.sort_dictionary(self.modules_json["repos"])
-        modules_json_path = os.path.join(self.dir, "modules.json")
-        with open(modules_json_path, "w") as fh:
+        with open(self.modules_json_path, "w") as fh:
             json.dump(self.modules_json, fh, indent=4)
             fh.write("\n")
+        run_prettier_on_file(self.modules_json_path)
 
     def resolve_missing_installation(self, missing_installation, component_type):
         missing_but_in_mod_json = [


Test patch:
diff --git a/tests/test_lint_utils.py b/tests/test_lint_utils.py
--- a/tests/test_lint_utils.py
+++ b/tests/test_lint_utils.py
@@ -39,7 +39,7 @@ def git_dir_with_json_malformed(temp_git_repo):
     return file
 
 
-@pytest.fixture(name="synthax_error_json")
+@pytest.fixture(name="syntax_error_json")
 def git_dir_with_json_syntax_error(temp_git_repo):
     tmp_git_dir, repo = temp_git_repo
     file = tmp_git_dir / "synthax-error.json"
@@ -59,7 +59,7 @@ def test_run_prettier_on_malformed_file(malformed_json):
     assert malformed_json.read_text() == JSON_FORMATTED
 
 
-def test_run_prettier_on_synthax_error_file(synthax_error_json):
-    with pytest.raises(ValueError) as exc_info:
-        nf_core.lint_utils.run_prettier_on_file(synthax_error_json)
-    assert exc_info.value.args[0].startswith(f"Can't format {synthax_error_json} because it has a synthax error.")
+def test_run_prettier_on_syntax_error_file(syntax_error_json, caplog):
+    nf_core.lint_utils.run_prettier_on_file(syntax_error_json)
+    expected_critical_log = "SyntaxError: Unexpected token (1:10)"
+    assert expected_critical_log in caplog.text