Repository: kyu08/fzf-make (language: rust)

Issue:
fix(pnpm): collect all scripts not only 2 down directory
exec `pnpm -r exec pwd` in workspace root


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml
index 4cc6ebb8..0dd90b8e 100644
--- a/.github/workflows/fmt.yml
+++ b/.github/workflows/fmt.yml
@@ -8,6 +8,7 @@ on:
       - 'src/**.rs'
       - Cargo.toml
       - Cargo.lock
+      - rustfmt.toml
 
 env:
   CARGO_TERM_COLOR: always
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 9010e302..80e66949 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -8,6 +8,7 @@ on:
       - 'src/**.rs'
       - Cargo.toml
       - Cargo.lock
+      - rustfmt.toml
 
 env:
   CARGO_TERM_COLOR: always
diff --git a/Cargo.toml b/Cargo.toml
index cbf4659f..dc7e8275 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,5 +33,6 @@ syntect-tui = "3.0.5"
 syntect = "5.2.0"
 tempfile = "3.14.0"
 tree-sitter = "=0.24.4"
+# FIXME: https://github.com/kyu08/fzf-make/issues/410
 # The latest tag is 0.1.0 and it is old now. So, I am using the latest commit hash.
 tree-sitter-just = { git = "https://github.com/IndianBoy42/tree-sitter-just.git", rev = "f6d2930" }
diff --git a/justfile b/justfile
deleted file mode 100644
index 0e342d52..00000000
--- a/justfile
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env -S just --justfile
-
-test:
-  cargo test --all
-
-[group: 'misc']
-run:
-  echo run
-
-[group: 'misc']
-build:
-  echo build
-
-[group: 'misc']
-fmt : # https://example.com
-  echo fmt
-
-[group: 'misc']
-[private ]
-fmt-private:
-  echo fmt
-
-# everyone's favorite animate paper clip
-[group: 'check']
-clippy:
-  echo clippy
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 00000000..fd65c78d
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1,3 @@
+edition = "2021"
+max_width = 120
+fn_call_width = 120
diff --git a/src/file/toml.rs b/src/file/toml.rs
index 57623531..fb60503c 100644
--- a/src/file/toml.rs
+++ b/src/file/toml.rs
@@ -133,17 +133,11 @@ pub fn history_file_path() -> Option<(PathBuf, String)> {
         Ok(_) => {
             // When testing
             let cwd = env::current_dir().unwrap();
-            Some((
-                cwd.join(PathBuf::from("test_data/history")),
-                HISTORY_FILE_NAME.to_string(),
-            ))
+            Some((cwd.join(PathBuf::from("test_data/history")), HISTORY_FILE_NAME.to_string()))
+        }
+        _ => {
+            home_dir().map(|home_dir| (home_dir.join(PathBuf::from(".config/fzf-make")), HISTORY_FILE_NAME.to_string()))
         }
-        _ => home_dir().map(|home_dir| {
-            (
-                home_dir.join(PathBuf::from(".config/fzf-make")),
-                HISTORY_FILE_NAME.to_string(),
-            )
-        }),
     }
 }
 
@@ -161,11 +155,7 @@ pub fn create_or_update_history_file(
         fs::create_dir_all(history_directory_path.clone())?;
     }
     let mut history_file = File::create(history_directory_path.join(history_file_name))?;
-    history_file.write_all(
-        toml::to_string(&Histories::from(new_history))
-            .unwrap()
-            .as_bytes(),
-    )?;
+    history_file.write_all(toml::to_string(&Histories::from(new_history)).unwrap().as_bytes())?;
     history_file.flush()?;
 
     Ok(())
@@ -264,18 +254,15 @@ args = "app1 build"
                 content: r#"
                 "#
                 .to_string(),
-                expect: Err(anyhow::anyhow!("TOML parse error at line 1, column 1\n  |\n1 | \n  | ^\nmissing field `histories`\n")),
+                expect: Err(anyhow::anyhow!(
+                    "TOML parse error at line 1, column 1\n  |\n1 | \n  | ^\nmissing field `histories`\n"
+                )),
             },
         ];
 
         for case in cases {
             match case.expect {
-                Ok(v) => assert_eq!(
-                    v,
-                    parse_history(case.content).unwrap(),
-                    "\nFailed: 🚨{:?}🚨\n",
-                    case.title,
-                ),
+                Ok(v) => assert_eq!(v, parse_history(case.content).unwrap(), "\nFailed: 🚨{:?}🚨\n", case.title,),
                 Err(e) => assert_eq!(
                     e.to_string(),
                     parse_history(case.content).unwrap_err().to_string(),
diff --git a/src/file/toml_old.rs b/src/file/toml_old.rs
index 8a124e30..b92e4a95 100644
--- a/src/file/toml_old.rs
+++ b/src/file/toml_old.rs
@@ -15,10 +15,7 @@ impl Histories {
         for h in self.history.clone() {
             let mut commands: Vec<fzf_make_toml::HistoryCommand> = vec![];
             for c in h.executed_targets {
-                commands.push(fzf_make_toml::HistoryCommand::new(
-                    model::runner_type::RunnerType::Make,
-                    c,
-                ));
+                commands.push(fzf_make_toml::HistoryCommand::new(model::runner_type::RunnerType::Make, c));
             }
             // NOTE: In old format, the path includes the file name but new format does not.
             let mut makefile_path = PathBuf::from(h.path);
@@ -69,25 +66,14 @@ mod test {
             after: fzf_make_toml::Histories::new(vec![fzf_make_toml::History::new(
                 PathBuf::from("path"),
                 vec![
-                    fzf_make_toml::HistoryCommand::new(
-                        runner_type::RunnerType::Make,
-                        "command1".to_string(),
-                    ),
-                    fzf_make_toml::HistoryCommand::new(
-                        runner_type::RunnerType::Make,
-                        "command2".to_string(),
-                    ),
+                    fzf_make_toml::HistoryCommand::new(runner_type::RunnerType::Make, "command1".to_string()),
+                    fzf_make_toml::HistoryCommand::new(runner_type::RunnerType::Make, "command2".to_string()),
                 ],
             )]),
         }];
 
         for case in cases {
-            assert_eq!(
-                case.after,
-                case.before.into_histories(),
-                "\nFailed: 🚨{:?}🚨\n",
-                case.title,
-            )
+            assert_eq!(case.after, case.before.into_histories(), "\nFailed: 🚨{:?}🚨\n", case.title,)
         }
     }
 
@@ -115,14 +101,8 @@ executed-targets = ["run", "echo1"]
                     fzf_make_toml::History::new(
                         PathBuf::from("/Users/user/code/fzf-make"),
                         vec![
-                            fzf_make_toml::HistoryCommand::new(
-                                runner_type::RunnerType::Make,
-                                "test".to_string(),
-                            ),
-                            fzf_make_toml::HistoryCommand::new(
-                                runner_type::RunnerType::Make,
-                                "check".to_string(),
-                            ),
+                            fzf_make_toml::HistoryCommand::new(runner_type::RunnerType::Make, "test".to_string()),
+                            fzf_make_toml::HistoryCommand::new(runner_type::RunnerType::Make, "check".to_string()),
                             fzf_make_toml::HistoryCommand::new(
                                 runner_type::RunnerType::Make,
                                 "spell-check".to_string(),
@@ -132,14 +112,8 @@ executed-targets = ["run", "echo1"]
                     fzf_make_toml::History::new(
                         PathBuf::from("/Users/user/code/golang/go-playground"),
                         vec![
-                            fzf_make_toml::HistoryCommand::new(
-                                runner_type::RunnerType::Make,
-                                "run".to_string(),
-                            ),
-                            fzf_make_toml::HistoryCommand::new(
-                                runner_type::RunnerType::Make,
-                                "echo1".to_string(),
-                            ),
+                            fzf_make_toml::HistoryCommand::new(runner_type::RunnerType::Make, "run".to_string()),
+                            fzf_make_toml::HistoryCommand::new(runner_type::RunnerType::Make, "echo1".to_string()),
                         ],
                     ),
                 ])),
@@ -149,18 +123,15 @@ executed-targets = ["run", "echo1"]
                 content: r#"
                 "#
                 .to_string(),
-                expect: Err(anyhow::anyhow!("TOML parse error at line 1, column 1\n  |\n1 | \n  | ^\nmissing field `history`\n")),
+                expect: Err(anyhow::anyhow!(
+                    "TOML parse error at line 1, column 1\n  |\n1 | \n  | ^\nmissing field `history`\n"
+                )),
             },
         ];
 
         for case in cases {
             match case.expect {
-                Ok(e) => assert_eq!(
-                    e,
-                    parse_history(case.content).unwrap(),
-                    "\nFailed: 🚨{:?}🚨\n",
-                    case.title,
-                ),
+                Ok(e) => assert_eq!(e, parse_history(case.content).unwrap(), "\nFailed: 🚨{:?}🚨\n", case.title,),
                 Err(err) => assert_eq!(
                     err.to_string(),
                     parse_history(case.content).unwrap_err().to_string(),
diff --git a/src/model/command.rs b/src/model/command.rs
index 33b38999..f52125c9 100644
--- a/src/model/command.rs
+++ b/src/model/command.rs
@@ -10,12 +10,7 @@ pub struct Command {
 }
 
 impl Command {
-    pub fn new(
-        runner_type: runner_type::RunnerType,
-        args: String,
-        file_path: PathBuf,
-        line_number: u32,
-    ) -> Self {
+    pub fn new(runner_type: runner_type::RunnerType, args: String, file_path: PathBuf, line_number: u32) -> Self {
         Self {
             runner_type,
             args,
diff --git a/src/model/file_util.rs b/src/model/file_util.rs
index a1d5f42a..d4f84907 100644
--- a/src/model/file_util.rs
+++ b/src/model/file_util.rs
@@ -4,3 +4,16 @@ use std::{fs::read_to_string, path::PathBuf};
 pub fn path_to_content(path: PathBuf) -> Result<String> {
     read_to_string(path.as_path()).map_err(|e| anyhow!(e))
 }
+
+pub fn find_file_in_ancestors(current_dir: PathBuf, file_names: Vec<&str>) -> Option<PathBuf> {
+    for path in current_dir.ancestors() {
+        for entry in PathBuf::from(path).read_dir().unwrap() {
+            let entry = entry.unwrap();
+            let file_name = entry.file_name().to_string_lossy().to_lowercase();
+            if file_names.contains(&file_name.as_str()) {
+                return Some(entry.path());
+            }
+        }
+    }
+    None
+}
diff --git a/src/model/histories.rs b/src/model/histories.rs
index 63251ed7..8cdbec75 100644
--- a/src/model/histories.rs
+++ b/src/model/histories.rs
@@ -24,10 +24,7 @@ impl Histories {
 
         // Update the whole histories.
         let mut new_histories = self.histories.clone();
-        match new_histories
-            .iter()
-            .position(|h| h.path == new_history.path)
-        {
+        match new_histories.iter().position(|h| h.path == new_history.path) {
             Some(index) => {
                 new_histories[index] = new_history;
             }
@@ -199,8 +196,7 @@ mod test {
         for case in cases {
             assert_eq!(
                 case.after,
-                case.before
-                    .append(path_to_append.clone(), case.command_to_append),
+                case.before.append(path_to_append.clone(), case.command_to_append),
                 "\nFailed: 🚨{:?}🚨\n",
                 case.title,
             )
@@ -421,12 +417,7 @@ mod test {
         ];
 
         for case in cases {
-            assert_eq!(
-                case.after,
-                case.before.append(case.command_to_append),
-                "\nFailed: 🚨{:?}🚨\n",
-                case.title,
-            )
+            assert_eq!(case.after, case.before.append(case.command_to_append), "\nFailed: 🚨{:?}🚨\n", case.title,)
         }
     }
 }
diff --git a/src/model/js_package_manager/js_package_manager_main.rs b/src/model/js_package_manager/js_package_manager_main.rs
index ab71dd93..18d9cd16 100644
--- a/src/model/js_package_manager/js_package_manager_main.rs
+++ b/src/model/js_package_manager/js_package_manager_main.rs
@@ -1,12 +1,11 @@
 use super::{pnpm, yarn};
-use crate::{file::path_to_content, model::command};
+use crate::model::command;
 use anyhow::Result;
 use codespan::Files;
 use json_spanned_value::{self as jsv, spanned};
 use std::{fs, path::PathBuf};
 
 pub(super) const METADATA_FILE_NAME: &str = "package.json";
-pub(super) const IGNORE_DIR_NAMES: [&str; 5] = ["node_modules", ".git", ".cache", ".next", ".yarn"];
 const METADATA_PACKAGE_NAME_KEY: &str = "name";
 const METADATA_COMMAND_KEY: &str = "scripts";
 
@@ -47,22 +46,8 @@ impl JsPackageManager {
     }
 
     fn new(current_dir: PathBuf, file_names: Vec<String>) -> Option<Self> {
-        let metadata_file_path = &PathBuf::from(METADATA_FILE_NAME);
-
-        // TODO: refactor same as yarn
-        // Search for lockfile to identify the package manager.
-        for file_name in file_names.clone() {
-            if pnpm::Pnpm::use_pnpm(&file_name) {
-                if let Ok(c) = path_to_content::path_to_content(metadata_file_path) {
-                    if let Some(result) = JsPackageManager::parse_package_json(&c) {
-                        return Some(JsPackageManager::JsPnpm(pnpm::Pnpm::new(
-                            current_dir.clone(),
-                            result.1,
-                        )));
-                    }
-                }
-                break;
-            }
+        if let Some(r) = pnpm::Pnpm::new(current_dir.clone(), file_names.clone()) {
+            return Some(JsPackageManager::JsPnpm(r));
         }
 
         if let Some(r) = yarn::Yarn::new(current_dir, file_names) {
@@ -96,8 +81,7 @@ impl JsPackageManager {
             if let Some(object) = v.as_object() {
                 for (k, v) in object {
                     let args = k.to_string();
-                    let line_number =
-                        files.line_index(file, k.start() as u32).number().to_usize() as u32;
+                    let line_number = files.line_index(file, k.start() as u32).number().to_usize() as u32;
                     if let Some(v) = v.as_string() {
                         result.push((args, v.to_string(), line_number));
                     }
@@ -112,9 +96,7 @@ impl JsPackageManager {
 
 pub fn get_js_package_manager_runner(current_dir: PathBuf) -> Option<JsPackageManager> {
     let entries = fs::read_dir(current_dir.clone()).unwrap();
-    let file_names = entries
-        .map(|e| e.unwrap().file_name().into_string().unwrap())
-        .collect();
+    let file_names = entries.map(|e| e.unwrap().file_name().into_string().unwrap()).collect();
 
     JsPackageManager::new(current_dir, file_names)
 }
diff --git a/src/model/js_package_manager/pnpm.rs b/src/model/js_package_manager/pnpm.rs
index 5fcccfb9..679ddc5b 100644
--- a/src/model/js_package_manager/pnpm.rs
+++ b/src/model/js_package_manager/pnpm.rs
@@ -1,10 +1,10 @@
 use super::js_package_manager_main as js;
 use crate::{
     file::path_to_content,
-    model::{command, runner_type},
+    model::{command, file_util, runner_type},
 };
 use anyhow::{anyhow, Result};
-use std::{fs, path::PathBuf, process};
+use std::{path::PathBuf, process};
 
 const PNPM_LOCKFILE_NAME: &str = "pnpm-lock.yaml";
 
@@ -46,134 +46,129 @@ impl Pnpm {
         }
     }
 
-    pub fn new(current_dir: PathBuf, result: Vec<(String, String, u32)>) -> Pnpm {
-        let commands = Pnpm::scripts_to_commands(current_dir.clone(), result);
-
-        Pnpm {
-            path: current_dir,
-            commands,
+    pub fn new(current_dir: PathBuf, cwd_file_names: Vec<String>) -> Option<Pnpm> {
+        let package_json_exist = Iterator::find(&mut cwd_file_names.iter(), |&f| f == js::METADATA_FILE_NAME);
+        let lockfile_exist_in_current_dir = Iterator::find(&mut cwd_file_names.iter(), |&f| f == PNPM_LOCKFILE_NAME);
+        let lockfile_exist_in_ancestors =
+            file_util::find_file_in_ancestors(current_dir.clone(), vec![PNPM_LOCKFILE_NAME]);
+
+        match (package_json_exist, lockfile_exist_in_current_dir, lockfile_exist_in_ancestors) {
+            (None, _, _) => None,
+            (Some(_), Some(_), _) => Pnpm::collect_workspace_scripts(current_dir.clone()).map(|commands| Pnpm {
+                path: current_dir,
+                commands,
+            }),
+            (Some(_), None, Some(_)) => {
+                Self::collect_scripts_in_package_json(current_dir.clone()).map(|commands| Pnpm {
+                    path: current_dir,
+                    commands,
+                })
+            }
+            // Not a workspace children && not a workspace root
+            // In this case, package manager can not be determined.
+            (Some(_), None, None) => None,
         }
     }
 
-    pub fn use_pnpm(file_name: &str) -> bool {
-        file_name == PNPM_LOCKFILE_NAME
-    }
-
-    pub fn to_commands(&self) -> Vec<command::Command> {
-        self.commands.clone()
-    }
+    // scripts_to_commands collects all scripts by following steps:
+    // 1. Collect scripts defined in package.json in the current directory(which fzf-make is launched)
+    // 2. Collect the paths of all `package.json` in the workspace.
+    // 3. Collect all scripts defined in given `package.json` paths.
+    fn collect_workspace_scripts(current_dir: PathBuf) -> Option<Vec<command::Command>> {
+        // Collect scripts defined in package.json in the current directory(which fzf-make is launched)
+        let mut result = match Self::collect_scripts_in_package_json(current_dir.clone()) {
+            Some(result) => result,
+            None => return None,
+        };
 
-    fn get_command(&self, command: command::Command) -> Option<&command::Command> {
-        self.commands.iter().find(|c| **c == command)
-    }
+        // Collect the paths of all `package.json` in the workspace.
+        let workspace_package_json_paths = match Self::get_workspace_packages() {
+            Ok(result) => result,
+            Err(_) => return None,
+        };
 
-    fn scripts_to_commands(
-        current_dir: PathBuf,
-        parsed_scripts_part_of_package_json: Vec<(String, String, u32)>,
-    ) -> Vec<command::Command> {
-        let mut result = vec![];
+        // Collect all scripts defined in given `package.json` paths.
+        for path in workspace_package_json_paths {
+            if let Ok(c) = path_to_content::path_to_content(&path) {
+                if let Some((name, parsing_result)) = js::JsPackageManager::parse_package_json(&c) {
+                    for (key, value, line_number) in parsing_result {
+                        if Self::use_filtering(value) {
+                            continue;
+                        }
+                        result.push(command::Command::new(
+                            runner_type::RunnerType::JsPackageManager(runner_type::JsPackageManager::Pnpm),
+                            // pnpm executes workspace script following format: `pnpm --filter {package_name} {script_name}`
+                            // e.g. `pnpm --filter app4 build`
+                            format!("--filter {} {}", name.clone(), key.as_str()),
+                            path.clone(),
+                            line_number,
+                        ));
+                    }
+                }
+            };
+        }
 
-        for (key, value, line_number) in parsed_scripts_part_of_package_json {
-            if Pnpm::use_filtering(value.clone()) {
-                continue;
-            }
+        Some(result)
+    }
 
-            // scripts defined in package.json in the current directory(which fzf-make is launched)
-            result.push(command::Command::new(
-                runner_type::RunnerType::JsPackageManager(runner_type::JsPackageManager::Pnpm),
-                key,
-                current_dir.clone().join(js::METADATA_FILE_NAME),
-                line_number,
-            ));
-        }
+    fn collect_scripts_in_package_json(current_dir: PathBuf) -> Option<Vec<command::Command>> {
+        let parsed_scripts_part_of_package_json =
+            match path_to_content::path_to_content(&current_dir.join(js::METADATA_FILE_NAME)) {
+                Ok(c) => match js::JsPackageManager::parse_package_json(&c) {
+                    Some(result) => result.1,
+                    None => return None,
+                },
+                Err(_) => return None,
+            };
+
+        Some(
+            parsed_scripts_part_of_package_json
+                .iter()
+                .filter(|(_, value, _)| !Self::use_filtering(value.to_string()))
+                .map(|(key, _value, line_number)| {
+                    command::Command::new(
+                        runner_type::RunnerType::JsPackageManager(runner_type::JsPackageManager::Pnpm),
+                        key.to_string(),
+                        current_dir.clone().join(js::METADATA_FILE_NAME),
+                        *line_number,
+                    )
+                })
+                .collect(),
+        )
+    }
 
-        /*
-        ## Following is the implementation for collecting all scripts in the workspace.
-
-        - If `packages` in pnpm-workspace.yaml is specified, the target to search is only under the directory defined at `packages`. If not specified, all package.json's are the target.
-        - Nested packages do not need to be considered. `./packages/app1/package.json` needs to be considered, but `./packages/app1/app2/package.json` does not need to be considered.
-        - If the directory structure is as follows, the examples will be shown in `entries_cwd.for_each(...)`.
-            ${CWD}
-            ├── package.json
-            ├── node_modules/
-            ├── packages
-            │   ├── app1
-            │   │   ├── package.json
-            │   │   └── node_modules
-            │   ├── app2
-            │   │   ├── package.json
-            │   │   └── node_modules
-            │   └── app3
-            │       ├── package.json
-            │       └── node_modules
-            ├── pnpm-lock.yaml
-            └── pnpm-workspace.yaml
+    // get_workspaces_list parses the result of `pnpm -r exec pwd` and return path of `package.json` of each package.
+    fn get_workspace_packages() -> Result<Vec<PathBuf>> {
+        let output = process::Command::new("pnpm")
+            .arg("-r")
+            .arg("exec")
+            .arg("pwd")
+            .output()?;
+        /* Example output:
+            /Users/kyu08/code/fzf-make/test_data/pnpm_monorepo/packages/app1
+            /Users/kyu08/code/fzf-make/test_data/pnpm_monorepo/packages/app2
+            /Users/kyu08/code/fzf-make/test_data/pnpm_monorepo/packages/app3
+            /Users/kyu08/code/fzf-make/test_data/pnpm_monorepo/packages/sub_packages/sub_app
         */
 
-        // TODO: consider `packages` in pnpm-workspace.yaml.
-        // TODO: Add UT. (Use temp dir or fzf-make/test_data. If use temp dir, the test will be
-        // robust, but troublesome for now...😇)
-        let skip = |entry: &fs::DirEntry| {
-            entry.path().is_file()
-                || js::IGNORE_DIR_NAMES
-                    .iter()
-                    .any(|name| entry.file_name() == *name)
-        };
-        // In above example, entries_cwd: package.json, node_modules, packages/, pnpm-lock.yaml, pnpm-workspace.yaml
-        let entries_cwd = fs::read_dir(current_dir.clone()).unwrap();
-        entries_cwd.for_each(|entry_cwd| {
-            if let Ok(entry_in_cwd) = entry_cwd {
-                if skip(&entry_in_cwd) {
-                    return;
-                }
-                // In above example, entries_of_packages: app1, app2, app3.
-                let entries_of_packages = fs::read_dir(entry_in_cwd.path()).unwrap();
-                entries_of_packages.for_each(|entry_package| {
-                    if let Ok(entry_package) = entry_package {
-                        if skip(&entry_package) {
-                            return;
-                        }
+        let output = String::from_utf8(output.stdout)?;
+        // split by newline to remove unnecessary lines.
+        let lines = output.split("\n").filter(|l| !l.is_empty()).collect::<Vec<&str>>();
 
-                        // In above example, entries_of_each_package: package.json, node_modules.
-                        let entries_of_each_package = fs::read_dir(entry_package.path()).unwrap();
-                        entries_of_each_package.for_each(|entry_of_each_package| {
-                            if let Ok(entry_of_each_package) = entry_of_each_package {
-                                if entry_of_each_package.file_name() != js::METADATA_FILE_NAME {
-                                    return;
-                                }
-                                if let Ok(c) =
-                                    path_to_content::path_to_content(&entry_of_each_package.path())
-                                {
-                                    if let Some((name, parsing_result)) =
-                                        js::JsPackageManager::parse_package_json(&c)
-                                    {
-                                        for (key, _, line_number) in parsing_result {
-                                            result.push(command::Command::new(
-                                                runner_type::RunnerType::JsPackageManager(
-                                                    runner_type::JsPackageManager::Pnpm,
-                                                ),
-                                                format!(
-                                                    "--filter {} {}",
-                                                    name.clone(),
-                                                    key.as_str()
-                                                ),
-                                                entry_of_each_package.path(),
-                                                line_number,
-                                            ));
-                                        }
-                                    }
-                                };
-                            }
-                        });
-                    }
-                });
-            }
-        });
+        Ok(lines
+            .iter()
+            .map(|line| PathBuf::from(line).join(js::METADATA_FILE_NAME))
+            .collect())
+    }
+
+    pub fn to_commands(&self) -> Vec<command::Command> {
+        self.commands.clone()
+    }
 
-        result
+    fn get_command(&self, command: command::Command) -> Option<&command::Command> {
+        self.commands.iter().find(|c| **c == command)
     }
 
-    // is filtering used
     // ref: https://pnpm.io/filtering
     fn use_filtering(value: String) -> bool {
         let args = value.split_whitespace().collect::<Vec<&str>>();
@@ -196,35 +191,18 @@ mod test {
     #[test]
     fn test_is_filtering() {
         assert_eq!(true, Pnpm::use_filtering("pnpm -F app1".to_string()));
+        assert_eq!(true, Pnpm::use_filtering("pnpm -F \"app1\"".to_string()));
         assert_eq!(true, Pnpm::use_filtering("pnpm --filter app2".to_string()));
-        assert_eq!(
-            true,
-            Pnpm::use_filtering("pnpm -r --filter app3".to_string())
-        );
-        assert_eq!(
-            true,
-            Pnpm::use_filtering("pnpm -C packages/app3".to_string())
-        );
-        assert_eq!(
-            true,
-            Pnpm::use_filtering("pnpm --dir packages/app3".to_string())
-        );
+        assert_eq!(true, Pnpm::use_filtering("pnpm -r --filter app3".to_string()));
+        assert_eq!(true, Pnpm::use_filtering("pnpm -C packages/app3".to_string()));
+        assert_eq!(true, Pnpm::use_filtering("pnpm --dir packages/app3".to_string()));
         assert_eq!(true, Pnpm::use_filtering("pnpm -F".to_string()));
         assert_eq!(true, Pnpm::use_filtering("pnpm --filter".to_string()));
-        assert_eq!(
-            false,
-            Pnpm::use_filtering("pnpm -C packages/app1 run test".to_string())
-        );
-        assert_eq!(
-            false,
-            Pnpm::use_filtering("pnpm --filter app1 run test".to_string())
-        );
+        assert_eq!(false, Pnpm::use_filtering("pnpm -C packages/app1 run test".to_string()));
+        assert_eq!(false, Pnpm::use_filtering("pnpm --filter app1 run test".to_string()));
         assert_eq!(false, Pnpm::use_filtering("yarn run".to_string()));
         assert_eq!(false, Pnpm::use_filtering("pnpm run".to_string()));
         assert_eq!(false, Pnpm::use_filtering("pnpm -r hoge".to_string()));
-        assert_eq!(
-            false,
-            Pnpm::use_filtering("yarn -r --filter app3".to_string())
-        );
+        assert_eq!(false, Pnpm::use_filtering("yarn -r --filter app3".to_string()));
     }
 }
diff --git a/src/model/js_package_manager/yarn.rs b/src/model/js_package_manager/yarn.rs
index 37e102a6..59dd78c1 100644
--- a/src/model/js_package_manager/yarn.rs
+++ b/src/model/js_package_manager/yarn.rs
@@ -52,6 +52,7 @@ impl Yarn {
     pub fn new(current_dir: PathBuf, cwd_file_names: Vec<String>) -> Option<Yarn> {
         Iterator::find(&mut cwd_file_names.iter(), |&f| f == js::METADATA_FILE_NAME)?;
         if Iterator::find(&mut cwd_file_names.iter(), |&f| f == YARN_LOCKFILE_NAME).is_some() {
+            // package.json and yarn.lock exist
             match Yarn::collect_workspace_scripts(current_dir.clone()) {
                 Some(commands) => {
                     return Some(Yarn {
@@ -62,8 +63,9 @@ impl Yarn {
                 None => return None,
             }
         }
+        // package.json exists, but yarn.lock does not exist
 
-        // executed in child packages of yarn workspaces || using an other package manager
+        // executed in child packages of yarn workspaces || not in yarn workspace (including using an other package manager)
         match Self::get_yarn_version() {
             Some(yarn_version) => {
                 let workspace_output = match yarn_version {
@@ -80,7 +82,7 @@ impl Yarn {
                 };
                 let workspace_output = match workspace_output {
                     Ok(output) => output,
-                    Err(_) => return None,
+                    Err(_) => return None, //  failed to run above command
                 };
 
                 // If `yarn workspaces (info|list) --json` returns non-zero status code, it means that the current directory is not a yarn workspace.
@@ -88,6 +90,7 @@ impl Yarn {
                     return None;
                 }
 
+                // not in yarn workspace, but has a package.json
                 Self::collect_scripts_in_package_json(current_dir.clone()).map(|commands| Yarn {
                     path: current_dir,
                     commands,
@@ -127,14 +130,10 @@ impl Yarn {
         if let Ok(workspace_package_json_paths) = package_json_in_workspace {
             for path in workspace_package_json_paths {
                 if let Ok(c) = path_to_content::path_to_content(&path) {
-                    if let Some((name, parsing_result)) =
-                        js::JsPackageManager::parse_package_json(&c)
-                    {
+                    if let Some((name, parsing_result)) = js::JsPackageManager::parse_package_json(&c) {
                         for (key, _, line_number) in parsing_result {
                             result.push(command::Command::new(
-                                runner_type::RunnerType::JsPackageManager(
-                                    runner_type::JsPackageManager::Yarn,
-                                ),
+                                runner_type::RunnerType::JsPackageManager(runner_type::JsPackageManager::Yarn),
                                 // yarn executes workspace script following format: `yarn workspace {package_name} {script_name}`
                                 // e.g. `yarn workspace app4 build`
                                 format!("workspace {} {}", name.clone(), key.as_str()),
@@ -165,9 +164,7 @@ impl Yarn {
                 .iter()
                 .map(|(key, _value, line_number)| {
                     command::Command::new(
-                        runner_type::RunnerType::JsPackageManager(
-                            runner_type::JsPackageManager::Yarn,
-                        ),
+                        runner_type::RunnerType::JsPackageManager(runner_type::JsPackageManager::Yarn),
                         key.to_string(),
                         current_dir.clone().join(js::METADATA_FILE_NAME),
                         *line_number,
@@ -216,7 +213,7 @@ impl Yarn {
             .arg("info")
             .arg("--json")
             .output()?;
-        /* output is like:
+        /* Example output:
         yarn workspaces v1.22.22
         {
           "app1": {
@@ -236,7 +233,7 @@ impl Yarn {
 
         let workspaces_json = {
             let output = String::from_utf8(output.stdout)?;
-            /* output is like:
+            /* Example output:
             "yarn workspaces v1.22.22\n{\n  \"app1\": {\n    \"location\": \"packages/app\",\n    \"workspaceDependencies\": [],\n    \"mismatchedWorkspaceDependencies\": []\n  }\n}\nDone in 0.01s.\n"
             */
 
diff --git a/src/model/just/just_main.rs b/src/model/just/just_main.rs
index b25b0ee4..72aec16e 100644
--- a/src/model/just/just_main.rs
+++ b/src/model/just/just_main.rs
@@ -1,5 +1,6 @@
 use crate::model::{
     command::{self, Command},
+    file_util,
     runner_type::RunnerType,
 };
 use anyhow::{anyhow, bail, Result};
@@ -73,23 +74,11 @@ impl Just {
     }
 
     fn get_command(&self, command: command::Command) -> Option<command::Command> {
-        self.to_commands()
-            .iter()
-            .find(|c| **c == command)
-            .map(|_| command)
+        self.to_commands().iter().find(|c| **c == command).map(|_| command)
     }
 
     fn find_justfile(current_dir: PathBuf) -> Option<PathBuf> {
-        for path in current_dir.ancestors() {
-            for entry in PathBuf::from(path).read_dir().unwrap() {
-                let entry = entry.unwrap();
-                let file_name = entry.file_name().to_string_lossy().to_lowercase();
-                if file_name == "justfile" || file_name == ".justfile" {
-                    return Some(entry.path());
-                }
-            }
-        }
-        None
+        file_util::find_file_in_ancestors(current_dir, vec!["justfile", ".justfile"])
     }
 
     fn parse_justfile(justfile_path: PathBuf, source_code: String) -> Option<Vec<Command>> {
@@ -116,14 +105,12 @@ impl Just {
         'recipe: for recipes_and_its_siblings in tree.root_node().named_children(&mut tree.walk()) {
             if recipes_and_its_siblings.kind() == "recipe" {
                 let mut should_skip = false;
-                recipes_and_its_siblings
-                    .children(&mut tree.walk())
-                    .for_each(|attr| {
-                        let attr_name = &source_code[attr.byte_range()];
-                        if attr_name.contains("private") {
-                            should_skip = true;
-                        }
-                    });
+                recipes_and_its_siblings.children(&mut tree.walk()).for_each(|attr| {
+                    let attr_name = &source_code[attr.byte_range()];
+                    if attr_name.contains("private") {
+                        should_skip = true;
+                    }
+                });
                 if should_skip {
                     continue;
                 }
@@ -291,8 +278,7 @@ clippy:
         ];
 
         for case in cases {
-            let commands =
-                Just::parse_justfile(PathBuf::from("justfile"), case.source_code.to_string());
+            let commands = Just::parse_justfile(PathBuf::from("justfile"), case.source_code.to_string());
             assert_eq!(commands, case.expected, "{}", case.name);
         }
     }
diff --git a/src/model/make/make_main.rs b/src/model/make/make_main.rs
index 108ccca7..fc9b65bd 100644
--- a/src/model/make/make_main.rs
+++ b/src/model/make/make_main.rs
@@ -69,10 +69,7 @@ impl Make {
     }
 
     fn get_command(&self, command: command::Command) -> Option<command::Command> {
-        self.to_commands()
-            .iter()
-            .find(|c| **c == command)
-            .map(|_| command)
+        self.to_commands().iter().find(|c| **c == command).map(|_| command)
     }
 
     // I gave up writing tests using temp_dir because it was too difficult (it was necessary to change the implementation to some extent).
@@ -131,24 +128,9 @@ impl Make {
             path: env::current_dir().unwrap().join(Path::new("Test.mk")),
             include_files: vec![],
             targets: Targets(vec![
-                command::Command::new(
-                    runner_type::RunnerType::Make,
-                    "target0".to_string(),
-                    PathBuf::from(""),
-                    1,
-                ),
-                command::Command::new(
-                    runner_type::RunnerType::Make,
-                    "target1".to_string(),
-                    PathBuf::from(""),
-                    4,
-                ),
-                command::Command::new(
-                    runner_type::RunnerType::Make,
-                    "target2".to_string(),
-                    PathBuf::from(""),
-                    7,
-                ),
+                command::Command::new(runner_type::RunnerType::Make, "target0".to_string(), PathBuf::from(""), 1),
+                command::Command::new(runner_type::RunnerType::Make, "target1".to_string(), PathBuf::from(""), 4),
+                command::Command::new(runner_type::RunnerType::Make, "target2".to_string(), PathBuf::from(""), 7),
             ]),
         }
     }
@@ -255,10 +237,7 @@ mod test {
 
             assert_eq!(
                 expect,
-                Make::specify_makefile_name(
-                    env::current_dir().unwrap(),
-                    tmp_dir.to_string_lossy().to_string()
-                ),
+                Make::specify_makefile_name(env::current_dir().unwrap(), tmp_dir.to_string_lossy().to_string()),
                 "\nFailed: 🚨{:?}🚨\n",
                 case.title,
             );
@@ -289,33 +268,13 @@ mod test {
                     path: Path::new("path").to_path_buf(),
                     include_files: vec![],
                     targets: Targets(vec![
-                        command::Command::new(
-                            runner_type::RunnerType::Make,
-                            "test".to_string(),
-                            PathBuf::from(""),
-                            4,
-                        ),
-                        command::Command::new(
-                            runner_type::RunnerType::Make,
-                            "run".to_string(),
-                            PathBuf::from(""),
-                            4,
-                        ),
+                        command::Command::new(runner_type::RunnerType::Make, "test".to_string(), PathBuf::from(""), 4),
+                        command::Command::new(runner_type::RunnerType::Make, "run".to_string(), PathBuf::from(""), 4),
                     ]),
                 },
                 expect: vec![
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "test".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "run".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
+                    command::Command::new(runner_type::RunnerType::Make, "test".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "run".to_string(), PathBuf::from(""), 4),
                 ],
             },
             Case {
@@ -378,80 +337,25 @@ mod test {
                         },
                     ],
                     targets: Targets(vec![
-                        command::Command::new(
-                            runner_type::RunnerType::Make,
-                            "test1".to_string(),
-                            PathBuf::from(""),
-                            4,
-                        ),
-                        command::Command::new(
-                            runner_type::RunnerType::Make,
-                            "run1".to_string(),
-                            PathBuf::from(""),
-                            4,
-                        ),
+                        command::Command::new(runner_type::RunnerType::Make, "test1".to_string(), PathBuf::from(""), 4),
+                        command::Command::new(runner_type::RunnerType::Make, "run1".to_string(), PathBuf::from(""), 4),
                     ]),
                 },
                 expect: vec![
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "test1".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "run1".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "test2".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "run2".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "test2-1".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "run2-1".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "test3".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "run3".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
+                    command::Command::new(runner_type::RunnerType::Make, "test1".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "run1".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "test2".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "run2".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "test2-1".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "run2-1".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "test3".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "run3".to_string(), PathBuf::from(""), 4),
                 ],
             },
         ];
 
         for case in cases {
-            assert_eq!(
-                case.expect,
-                case.makefile.to_commands(),
-                "\nFailed: 🚨{:?}🚨\n",
-                case.title,
-            )
+            assert_eq!(case.expect, case.makefile.to_commands(), "\nFailed: 🚨{:?}🚨\n", case.title,)
         }
     }
 
@@ -522,34 +426,22 @@ mod test {
             Case {
                 title: "include one.mk two.mk",
                 line: "include one.mk two.mk",
-                expect: Some(vec![
-                    Path::new("one.mk").to_path_buf(),
-                    Path::new("two.mk").to_path_buf(),
-                ]),
+                expect: Some(vec![Path::new("one.mk").to_path_buf(), Path::new("two.mk").to_path_buf()]),
             },
             Case {
                 title: "-include",
                 line: "-include one.mk two.mk",
-                expect: Some(vec![
-                    Path::new("one.mk").to_path_buf(),
-                    Path::new("two.mk").to_path_buf(),
-                ]),
+                expect: Some(vec![Path::new("one.mk").to_path_buf(), Path::new("two.mk").to_path_buf()]),
             },
             Case {
                 title: "sinclude",
                 line: "sinclude hoge.mk fuga.mk",
-                expect: Some(vec![
-                    Path::new("hoge.mk").to_path_buf(),
-                    Path::new("fuga.mk").to_path_buf(),
-                ]),
+                expect: Some(vec![Path::new("hoge.mk").to_path_buf(), Path::new("fuga.mk").to_path_buf()]),
             },
             Case {
                 title: " include one.mk two.mk",
                 line: " include one.mk two.mk",
-                expect: Some(vec![
-                    Path::new("one.mk").to_path_buf(),
-                    Path::new("two.mk").to_path_buf(),
-                ]),
+                expect: Some(vec![Path::new("one.mk").to_path_buf(), Path::new("two.mk").to_path_buf()]),
             },
             Case {
                 title: "include one.mk two.mk(tab is not allowed)",
@@ -564,10 +456,7 @@ mod test {
             Case {
                 title: "include comment",
                 line: "include one.mk two.mk # three.mk",
-                expect: Some(vec![
-                    Path::new("one.mk").to_path_buf(),
-                    Path::new("two.mk").to_path_buf(),
-                ]),
+                expect: Some(vec![Path::new("one.mk").to_path_buf(), Path::new("two.mk").to_path_buf()]),
             },
             Case {
                 title: "# include one.mk two.mk # three.mk",
diff --git a/src/model/make/target.rs b/src/model/make/target.rs
index 77ebd036..1e5fd2fe 100644
--- a/src/model/make/target.rs
+++ b/src/model/make/target.rs
@@ -21,12 +21,8 @@ impl Targets {
                 LineType::Normal => {
                     if define_block_depth == 0 {
                         if let Some(t) = line_to_target(line.to_string()) {
-                            let command = command::Command::new(
-                                runner_type::RunnerType::Make,
-                                t,
-                                path.clone(),
-                                i as u32 + 1,
-                            );
+                            let command =
+                                command::Command::new(runner_type::RunnerType::Make, t, path.clone(), i as u32 + 1);
                             result.push(command);
                         }
                     }
@@ -72,15 +68,9 @@ fn get_line_type(line: &str) -> LineType {
 
 fn line_to_target(line: String) -> Option<String> {
     let regex = Regex::new(r"^ *[^.#\s　][^=]*:[^=]*$").unwrap();
-    regex.find(line.as_str()).map(|m| {
-        m.as_str()
-            .to_string()
-            .split_once(':')
-            .unwrap()
-            .0
-            .trim()
-            .to_string()
-    })
+    regex
+        .find(line.as_str())
+        .map(|m| m.as_str().to_string().split_once(':').unwrap().0.trim().to_string())
 }
 
 #[cfg(test)]
@@ -117,36 +107,11 @@ test: # run test
 echo:
 	@echo good",
                 expect: Targets(vec![
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "run".to_string(),
-                        PathBuf::from(""),
-                        3,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "build".to_string(),
-                        PathBuf::from(""),
-                        6,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "check".to_string(),
-                        PathBuf::from(""),
-                        9,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "test".to_string(),
-                        PathBuf::from(""),
-                        13,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "echo".to_string(),
-                        PathBuf::from(""),
-                        16,
-                    ),
+                    command::Command::new(runner_type::RunnerType::Make, "run".to_string(), PathBuf::from(""), 3),
+                    command::Command::new(runner_type::RunnerType::Make, "build".to_string(), PathBuf::from(""), 6),
+                    command::Command::new(runner_type::RunnerType::Make, "check".to_string(), PathBuf::from(""), 9),
+                    command::Command::new(runner_type::RunnerType::Make, "test".to_string(), PathBuf::from(""), 13),
+                    command::Command::new(runner_type::RunnerType::Make, "echo".to_string(), PathBuf::from(""), 16),
                 ]),
             },
             Case {
@@ -161,18 +126,8 @@ clone:
 build:
 		@cargo build",
                 expect: Targets(vec![
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "clone".to_string(),
-                        PathBuf::from(""),
-                        4,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "build".to_string(),
-                        PathBuf::from(""),
-                        7,
-                    ),
+                    command::Command::new(runner_type::RunnerType::Make, "clone".to_string(), PathBuf::from(""), 4),
+                    command::Command::new(runner_type::RunnerType::Make, "build".to_string(), PathBuf::from(""), 7),
                 ]),
             },
             Case {
@@ -194,18 +149,8 @@ endef
 my_script:
 	$(file >my_script,$(script-block))\n",
                 expect: Targets(vec![
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "all".to_string(),
-                        PathBuf::from(""),
-                        3,
-                    ),
-                    command::Command::new(
-                        runner_type::RunnerType::Make,
-                        "my_script".to_string(),
-                        PathBuf::from(""),
-                        9,
-                    ),
+                    command::Command::new(runner_type::RunnerType::Make, "all".to_string(), PathBuf::from(""), 3),
+                    command::Command::new(runner_type::RunnerType::Make, "my_script".to_string(), PathBuf::from(""), 9),
                 ]),
             },
             Case {
@@ -286,12 +231,7 @@ endef
         ];
 
         for case in cases {
-            assert_eq!(
-                case.expect,
-                get_line_type(case.line),
-                "\nFailed: 🚨{:?}🚨\n",
-                case.title,
-            );
+            assert_eq!(case.expect, get_line_type(case.line), "\nFailed: 🚨{:?}🚨\n", case.title,);
         }
     }
 
diff --git a/src/model/runner_type.rs b/src/model/runner_type.rs
index bf290d0c..a78613ad 100644
--- a/src/model/runner_type.rs
+++ b/src/model/runner_type.rs
@@ -31,12 +31,8 @@ impl RunnerType {
         match runner {
             runner::Runner::MakeCommand(_) => RunnerType::Make,
             runner::Runner::JsPackageManager(js) => match js {
-                js::JsPackageManager::JsPnpm(_) => {
-                    RunnerType::JsPackageManager(JsPackageManager::Pnpm)
-                }
-                js::JsPackageManager::JsYarn(_) => {
-                    RunnerType::JsPackageManager(JsPackageManager::Yarn)
-                }
+                js::JsPackageManager::JsPnpm(_) => RunnerType::JsPackageManager(JsPackageManager::Pnpm),
+                js::JsPackageManager::JsYarn(_) => RunnerType::JsPackageManager(JsPackageManager::Yarn),
             },
             runner::Runner::Just(_) => RunnerType::Just,
         }
@@ -88,12 +84,8 @@ impl Serialize for RunnerType {
     {
         match self {
             RunnerType::Make => serializer.serialize_str("make"),
-            RunnerType::JsPackageManager(JsPackageManager::Pnpm) => {
-                serializer.serialize_str("pnpm")
-            }
-            RunnerType::JsPackageManager(JsPackageManager::Yarn) => {
-                serializer.serialize_str("yarn")
-            }
+            RunnerType::JsPackageManager(JsPackageManager::Pnpm) => serializer.serialize_str("pnpm"),
+            RunnerType::JsPackageManager(JsPackageManager::Yarn) => serializer.serialize_str("yarn"),
             RunnerType::Just => serializer.serialize_str("just"),
         }
     }
diff --git a/src/usecase/tui/app.rs b/src/usecase/tui/app.rs
index b90a98d8..d5c791c0 100644
--- a/src/usecase/tui/app.rs
+++ b/src/usecase/tui/app.rs
@@ -89,10 +89,7 @@ impl Model<'_> {
     }
 
     // returns available commands in cwd from history file
-    fn get_histories(
-        current_working_directory: PathBuf,
-        runners: Vec<runner::Runner>,
-    ) -> Vec<Command> {
+    fn get_histories(current_working_directory: PathBuf, runners: Vec<runner::Runner>) -> Vec<Command> {
         let histories = toml::Histories::into(toml::Histories::get_history());
 
         for history in histories.histories {
@@ -112,8 +109,7 @@ impl Model<'_> {
     ) -> Vec<command::Command> {
         // make a hashmap in order to search commands by O(1).
         let command_hash_map: HashMap<runner_type::RunnerType, HashMap<String, command::Command>> = {
-            let mut map: HashMap<runner_type::RunnerType, HashMap<String, command::Command>> =
-                HashMap::new();
+            let mut map: HashMap<runner_type::RunnerType, HashMap<String, command::Command>> = HashMap::new();
             for runner in runners {
                 let mut inner_map = HashMap::new();
                 for c in runner.list_commands() {
@@ -136,11 +132,7 @@ impl Model<'_> {
         commands
     }
 
-    fn transition_to_execute_command_state(
-        &mut self,
-        runner: runner::Runner,
-        command: command::Command,
-    ) {
+    fn transition_to_execute_command_state(&mut self, runner: runner::Runner, command: command::Command) {
         self.app_state = AppState::ExecuteCommand(ExecuteCommandState::new(runner, command));
     }
 
@@ -200,8 +192,8 @@ pub async fn main(config: config::Config) -> Result<()> {
             runner.show_command(&command);
             runner.execute(&command)
         }
-        Ok(Ok(None)) => Ok(()), // no command was selected
-        Ok(Err(e)) => Err(e),   // Model::new or run returned Err
+        Ok(Ok(None)) => Ok(()),                                    // no command was selected
+        Ok(Err(e)) => Err(e),                                      // Model::new or run returned Err
         Err(e) => Err(anyhow!(any_to_string::any_to_string(&*e))), // panic occurred
     }
 }
@@ -219,8 +211,7 @@ async fn run<'a, B: Backend>(
     loop {
         if let AppState::SelectCommand(s) = &mut model.app_state {
             if s.latest_version.is_none() {
-                if let Some(new_version) = shared_version_hash_map.lock().unwrap().get(VERSION_KEY)
-                {
+                if let Some(new_version) = shared_version_hash_map.lock().unwrap().get(VERSION_KEY) {
                     s.latest_version = Some(new_version.to_string());
                 }
             }
@@ -247,11 +238,7 @@ fn shutdown_terminal(terminal: &mut Terminal<CrosstermBackend<Stderr>>) -> Resul
         return Err(anyhow!(e));
     }
 
-    execute!(
-        terminal.backend_mut(),
-        LeaveAlternateScreen,
-        DisableMouseCapture
-    )?;
+    execute!(terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture)?;
 
     if let Err(e) = terminal.show_cursor() {
         return Err(anyhow!(e));
@@ -263,13 +250,11 @@ fn shutdown_terminal(terminal: &mut Terminal<CrosstermBackend<Stderr>>) -> Resul
 const PKG_NAME: &str = "kyu08/fzf-make";
 async fn get_latest_version(share_clone: Arc<Mutex<HashMap<String, String>>>) {
     let current_version = env!("CARGO_PKG_VERSION").to_string();
-    let informer =
-        update_informer::new(registry::GitHub, PKG_NAME, current_version).interval(Duration::ZERO); // check version once a day
-                                                                                                    // .interval(Duration::from_secs(60 * 60 * 24)); // check version once a day
-    let version_result =
-        task::spawn_blocking(|| informer.check_version().map_err(|e| e.to_string()))
-            .await
-            .unwrap();
+    let informer = update_informer::new(registry::GitHub, PKG_NAME, current_version).interval(Duration::ZERO); // check version once a day
+                                                                                                               // .interval(Duration::from_secs(60 * 60 * 24)); // check version once a day
+    let version_result = task::spawn_blocking(|| informer.check_version().map_err(|e| e.to_string()))
+        .await
+        .unwrap();
     if let Ok(Some(new_version)) = version_result {
         let mut data = share_clone.lock().unwrap();
         data.insert(VERSION_KEY.to_string(), new_version.to_string().clone());
@@ -289,10 +274,7 @@ enum Message {
 
 // TODO: make this method Model's method
 fn handle_event(model: &Model) -> io::Result<Option<Message>> {
-    match (
-        crossterm::event::poll(std::time::Duration::from_millis(2000))?,
-        crossterm::event::read()?,
-    ) {
+    match (crossterm::event::poll(std::time::Duration::from_millis(2000))?, crossterm::event::read()?) {
         (true, crossterm::event::Event::Key(key)) => Ok(model.handle_key_input(key)),
         _ => Ok(None),
     }
@@ -382,8 +364,7 @@ impl SelectCommandState<'_> {
             if let Ok(f) = Make::new(current_dir.clone()) {
                 runners.push(Runner::MakeCommand(f));
             };
-            if let Some(js_package_manager) = js::get_js_package_manager_runner(current_dir.clone())
-            {
+            if let Some(js_package_manager) = js::get_js_package_manager_runner(current_dir.clone()) {
                 runners.push(Runner::JsPackageManager(js_package_manager));
             };
             if let Ok(just) = Just::new(current_dir.clone()) {
@@ -630,9 +611,7 @@ impl SelectCommandState<'_> {
 
                     // _ patterns. To prevent omission of corrections, _ is not used.
                     (runner_type::JsPackageManager::Pnpm, js::JsPackageManager::JsYarn(_))
-                    | (runner_type::JsPackageManager::Yarn, js::JsPackageManager::JsPnpm(_)) => {
-                        return None
-                    }
+                    | (runner_type::JsPackageManager::Yarn, js::JsPackageManager::JsPnpm(_)) => return None,
                 },
                 _ => continue,
             }
@@ -783,9 +762,7 @@ mod test {
                         ..SelectCommandState::new_for_test()
                     }),
                 },
-                message: Some(Message::SearchTextAreaKeyInput(KeyEvent::from(
-                    KeyCode::Char('a'),
-                ))),
+                message: Some(Message::SearchTextAreaKeyInput(KeyEvent::from(KeyCode::Char('a')))),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
                         search_text_area: {
@@ -801,22 +778,14 @@ mod test {
                 title: "when BackSpace is inputted, the selection should be reset",
                 model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(1),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(1)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
-                message: Some(Message::SearchTextAreaKeyInput(KeyEvent::from(
-                    KeyCode::Backspace,
-                ))),
+                message: Some(Message::SearchTextAreaKeyInput(KeyEvent::from(KeyCode::Backspace))),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(0),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(0)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
@@ -831,10 +800,7 @@ mod test {
                 message: Some(Message::NextCommand),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(1),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(1)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
@@ -843,20 +809,14 @@ mod test {
                 title: "Next(2 -> 0)",
                 model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(2),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(2)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
                 message: Some(Message::NextCommand),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(0),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(0)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
@@ -865,20 +825,14 @@ mod test {
                 title: "Previous(1 -> 0)",
                 model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(1),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(1)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
                 message: Some(Message::PreviousCommand),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(0),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(0)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
@@ -887,20 +841,14 @@ mod test {
                 title: "Previous(0 -> 2)",
                 model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(0),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(0)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
                 message: Some(Message::PreviousCommand),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(2),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(2)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
@@ -916,12 +864,7 @@ mod test {
                 expect_model: Model {
                     app_state: AppState::ExecuteCommand(ExecuteCommandState::new(
                         runner::Runner::MakeCommand(Make::new_for_test()),
-                        command::Command::new(
-                            runner_type::RunnerType::Make,
-                            "target0".to_string(),
-                            PathBuf::new(),
-                            1,
-                        ),
+                        command::Command::new(runner_type::RunnerType::Make, "target0".to_string(), PathBuf::new(), 1),
                     )),
                 },
             },
@@ -952,22 +895,14 @@ mod test {
                     was inputted when the command located not in top of the commands",
                 model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(1),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(1)),
                         ..SelectCommandState::new_for_test()
                     }),
                 },
-                message: Some(Message::SearchTextAreaKeyInput(KeyEvent::from(
-                    KeyCode::Char('a'),
-                ))),
+                message: Some(Message::SearchTextAreaKeyInput(KeyEvent::from(KeyCode::Char('a')))),
                 expect_model: Model {
                     app_state: AppState::SelectCommand(SelectCommandState {
-                        commands_list_state: ListState::with_selected(
-                            ListState::default(),
-                            Some(0),
-                        ),
+                        commands_list_state: ListState::with_selected(ListState::default(), Some(0)),
                         search_text_area: {
                             let mut text_area = TextArea::default();
                             text_area.input(KeyEvent::from(KeyCode::Char('a')));
@@ -982,19 +917,14 @@ mod test {
                 model: {
                     let mut m = Model {
                         app_state: AppState::SelectCommand(SelectCommandState {
-                            commands_list_state: ListState::with_selected(
-                                ListState::default(),
-                                None,
-                            ),
+                            commands_list_state: ListState::with_selected(ListState::default(), None),
                             ..SelectCommandState::new_for_test()
                         }),
                     };
                     update(
                         // There should not be commands because init_model has ["target0", "target1", "target2"] as command.
                         &mut m,
-                        Some(Message::SearchTextAreaKeyInput(KeyEvent::from(
-                            KeyCode::Char('w'),
-                        ))),
+                        Some(Message::SearchTextAreaKeyInput(KeyEvent::from(KeyCode::Char('w')))),
                     );
                     m
                 },
@@ -1017,19 +947,14 @@ mod test {
                 model: {
                     let mut m = Model {
                         app_state: AppState::SelectCommand(SelectCommandState {
-                            commands_list_state: ListState::with_selected(
-                                ListState::default(),
-                                None,
-                            ),
+                            commands_list_state: ListState::with_selected(ListState::default(), None),
                             ..SelectCommandState::new_for_test()
                         }),
                     };
                     update(
                         // There should not be commands because init_model has ["target0", "target1", "target2"] as command.
                         &mut m,
-                        Some(Message::SearchTextAreaKeyInput(KeyEvent::from(
-                            KeyCode::Char('w'),
-                        ))),
+                        Some(Message::SearchTextAreaKeyInput(KeyEvent::from(KeyCode::Char('w')))),
                     );
                     m
                 },
@@ -1127,11 +1052,7 @@ mod test {
 
         for mut case in cases {
             update(&mut case.model, case.message);
-            assert_eq!(
-                case.expect_model, case.model,
-                "\nFailed: 🚨{:?}🚨\n",
-                case.title,
-            );
+            assert_eq!(case.expect_model, case.model, "\nFailed: 🚨{:?}🚨\n", case.title,);
         }
     }
 }
diff --git a/src/usecase/tui/config.rs b/src/usecase/tui/config.rs
index 9109a668..087a2dba 100644
--- a/src/usecase/tui/config.rs
+++ b/src/usecase/tui/config.rs
@@ -8,9 +8,7 @@ impl Config {
     }
 
     pub fn default() -> Self {
-        Self {
-            focus_history: false,
-        }
+        Self { focus_history: false }
     }
 
     pub fn get_focus_history(&self) -> bool {
diff --git a/src/usecase/tui/ui.rs b/src/usecase/tui/ui.rs
index 945c9cb7..80168724 100644
--- a/src/usecase/tui/ui.rs
+++ b/src/usecase/tui/ui.rs
@@ -57,15 +57,11 @@ pub fn ui(f: &mut Frame, model: &mut Model) {
 
 const FG_COLOR_SELECTED: ratatui::style::Color = Color::Rgb(161, 220, 156);
 const FG_COLOR_NOT_SELECTED: ratatui::style::Color = Color::DarkGray;
-const BORDER_STYLE_SELECTED: ratatui::widgets::block::BorderType =
-    ratatui::widgets::BorderType::Thick;
-const BORDER_STYLE_NOT_SELECTED: ratatui::widgets::block::BorderType =
-    ratatui::widgets::BorderType::Plain;
+const BORDER_STYLE_SELECTED: ratatui::widgets::block::BorderType = ratatui::widgets::BorderType::Thick;
+const BORDER_STYLE_NOT_SELECTED: ratatui::widgets::block::BorderType = ratatui::widgets::BorderType::Plain;
 const TITLE_STYLE: ratatui::style::Style = Style::new().add_modifier(Modifier::BOLD);
 
-fn color_and_border_style_for_selectable(
-    is_selected: bool,
-) -> (Color, ratatui::widgets::block::BorderType) {
+fn color_and_border_style_for_selectable(is_selected: bool) -> (Color, ratatui::widgets::block::BorderType) {
     if is_selected {
         (FG_COLOR_SELECTED, BORDER_STYLE_SELECTED)
     } else {
@@ -75,8 +71,7 @@ fn color_and_border_style_for_selectable(
 
 fn render_preview_block(model: &SelectCommandState, f: &mut Frame, chunk: ratatui::layout::Rect) {
     let narrow_down_commands = model.narrow_down_commands();
-    let selecting_command =
-        narrow_down_commands.get(model.commands_list_state.selected().unwrap_or(0));
+    let selecting_command = narrow_down_commands.get(model.commands_list_state.selected().unwrap_or(0));
 
     let reader = match selecting_command.map(|c| File::open(c.file_path.clone())) {
         Some(Ok(file)) => Some(BufReader::new(file)),
@@ -84,8 +79,7 @@ fn render_preview_block(model: &SelectCommandState, f: &mut Frame, chunk: ratatu
     };
     let command_row_index = selecting_command.map(|c| c.line_number as usize - 1);
     let row_count = chunk.rows().count() - 2; // NOTE: chunk.rows().count() includes border lines
-    let start_index_and_end_index =
-        command_row_index.map(|c| determine_rendering_position(row_count, c));
+    let start_index_and_end_index = command_row_index.map(|c| determine_rendering_position(row_count, c));
     // NOTE: due to lifetime, source_lines need to be declared outside of `let lines = {/* ... */}`
     let source_lines: Vec<_> = match (selecting_command, start_index_and_end_index, reader) {
         (Some(_), Some((start_index, end_index)), Some(reader)) => {
@@ -101,11 +95,7 @@ fn render_preview_block(model: &SelectCommandState, f: &mut Frame, chunk: ratatu
     };
 
     let lines = {
-        match (
-            selecting_command,
-            start_index_and_end_index,
-            command_row_index,
-        ) {
+        match (selecting_command, start_index_and_end_index, command_row_index) {
             (Some(_), Some((start_index, _)), Some(command_row_index)) => {
                 let ss = SyntaxSet::load_defaults_newlines();
                 // HACK: `ml` is specified intentionally because it highlights `Makefile` and `json` files in a good way.(No unnecessary background color)
@@ -136,10 +126,7 @@ fn render_preview_block(model: &SelectCommandState, f: &mut Frame, chunk: ratatu
                         .collect();
 
                     // add row number
-                    spans.insert(
-                        0,
-                        Span::styled(format!("{:5} ", start_index + index + 1), Style::default()),
-                    );
+                    spans.insert(0, Span::styled(format!("{:5} ", start_index + index + 1), Style::default()));
 
                     lines.push(Line::from(spans));
                 }
@@ -149,17 +136,14 @@ fn render_preview_block(model: &SelectCommandState, f: &mut Frame, chunk: ratatu
         }
     };
 
-    let (fg_color_, border_style) =
-        color_and_border_style_for_selectable(model.current_pane.is_main());
+    let (fg_color_, border_style) = color_and_border_style_for_selectable(model.current_pane.is_main());
     let block = Block::default()
         .borders(Borders::ALL)
         .border_type(border_style)
         .border_style(Style::default().fg(fg_color_))
         .title(" ✨ Preview ")
         .title_style(TITLE_STYLE);
-    let preview_widget = Paragraph::new(lines)
-        .wrap(Wrap { trim: false })
-        .block(block);
+    let preview_widget = Paragraph::new(lines).wrap(Wrap { trim: false }).block(block);
     f.render_widget(preview_widget, chunk);
 }
 
@@ -179,17 +163,9 @@ fn determine_rendering_position(row_count: usize, command_row_index: usize) -> (
     }
 }
 
-fn render_commands_block(
-    model: &mut SelectCommandState,
-    f: &mut Frame,
-    chunk: ratatui::layout::Rect,
-) {
+fn render_commands_block(model: &mut SelectCommandState, f: &mut Frame, chunk: ratatui::layout::Rect) {
     f.render_stateful_widget(
-        commands_block(
-            " 📢 Commands ",
-            model.narrow_down_commands(),
-            model.current_pane.is_main(),
-        ),
+        commands_block(" 📢 Commands ", model.narrow_down_commands(), model.current_pane.is_main()),
         chunk,
         // NOTE: It is against TEA's way to update the model value on the UI side, but it is unavoidable so it is allowed.
         &mut model.commands_list_state,
@@ -197,8 +173,7 @@ fn render_commands_block(
 }
 
 fn render_input_block(model: &mut SelectCommandState, f: &mut Frame, chunk: ratatui::layout::Rect) {
-    let (fg_color, border_style) =
-        color_and_border_style_for_selectable(model.current_pane.is_main());
+    let (fg_color, border_style) = color_and_border_style_for_selectable(model.current_pane.is_main());
 
     let block = Block::default()
         .title(" 🔍 Search ")
@@ -218,19 +193,11 @@ fn render_input_block(model: &mut SelectCommandState, f: &mut Frame, chunk: rata
     f.render_widget(&model.search_text_area.0, chunk);
 }
 
-fn render_notification_block(
-    model: &mut SelectCommandState,
-    f: &mut Frame,
-    chunk: ratatui::layout::Rect,
-) {
+fn render_notification_block(model: &mut SelectCommandState, f: &mut Frame, chunk: ratatui::layout::Rect) {
     let text = match &model.latest_version {
         Some(has_update) => {
             if format!("v{}", env!("CARGO_PKG_VERSION")) != *has_update {
-                format!(
-                    "📦️ A new release is available! v{} → {}.",
-                    env!("CARGO_PKG_VERSION"),
-                    has_update.as_str()
-                )
+                format!("📦️ A new release is available! v{} → {}.", env!("CARGO_PKG_VERSION"), has_update.as_str())
             } else {
                 "".to_string()
             }
@@ -243,9 +210,7 @@ fn render_notification_block(
         .padding(ratatui::widgets::Padding::new(1, 0, 1, 1))
         .style(Style::new().add_modifier(Modifier::BOLD).fg(Color::Yellow));
 
-    let key_notes_footer = Paragraph::new(notification)
-        .wrap(Wrap { trim: true })
-        .block(block);
+    let key_notes_footer = Paragraph::new(notification).wrap(Wrap { trim: true }).block(block);
     f.render_widget(key_notes_footer, chunk);
 }
 
@@ -262,17 +227,9 @@ fn render_current_version_block(f: &mut Frame, chunk: ratatui::layout::Rect) {
     f.render_widget(key_notes_footer, chunk);
 }
 
-fn render_history_block(
-    model: &mut SelectCommandState,
-    f: &mut Frame,
-    chunk: ratatui::layout::Rect,
-) {
+fn render_history_block(model: &mut SelectCommandState, f: &mut Frame, chunk: ratatui::layout::Rect) {
     f.render_stateful_widget(
-        commands_block(
-            " 📚 History ",
-            model.get_history(),
-            model.current_pane.is_history(),
-        ),
+        commands_block(" 📚 History ", model.get_history(), model.current_pane.is_history()),
         chunk,
         // NOTE: It is against TEA's way to update the model value on the UI side, but it is unavoidable so it is allowed.
         &mut model.history_list_state,
@@ -296,11 +253,7 @@ fn render_hint_block(model: &mut SelectCommandState, f: &mut Frame, chunk: ratat
     f.render_widget(key_notes_footer, chunk);
 }
 
-fn commands_block(
-    title: &str,
-    narrowed_down_commands: Vec<command::Command>,
-    is_current: bool,
-) -> List<'_> {
+fn commands_block(title: &str, narrowed_down_commands: Vec<command::Command>, is_current: bool) -> List<'_> {
     let (fg_color, border_style) = color_and_border_style_for_selectable(is_current);
 
     let list: Vec<ListItem> = narrowed_down_commands


Test patch:
diff --git a/test_data/pnpm_monorepo/packages/app1/node_modules/package.json b/test_data/pnpm_monorepo/packages/app1/node_modules/package.json
deleted file mode 100644
index fc597748..00000000
--- a/test_data/pnpm_monorepo/packages/app1/node_modules/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "name": "app1-node-modules",
-  "version": "1.0.0",
-  "private": true,
-  "scripts": {
-    "build_": "these scripts should not be shown!!",
-    "start_": "these scripts should not be shown!!",
-    "test_": "these scripts should not be shown!!"
-  }
-}
diff --git a/test_data/pnpm_monorepo/packages/app4/package.json b/test_data/pnpm_monorepo/packages/app2/package.json
similarity index 60%
rename from test_data/pnpm_monorepo/packages/app4/package.json
rename to test_data/pnpm_monorepo/packages/app2/package.json
index 010a134a..01eaf32a 100644
--- a/test_data/pnpm_monorepo/packages/app4/package.json
+++ b/test_data/pnpm_monorepo/packages/app2/package.json
@@ -1,11 +1,11 @@
 {
-  "name": "app4",
+  "name": "app2",
   "version": "1.0.0",
   "private": true,
   "scripts": {
-    "build": "echo build app4",
-    "start": "echo start app4",
-    "test": "echo test app4"
+    "build": "echo build app2",
+    "start": "echo start app2",
+    "test": "echo test app2"
   },
   "devDependencies": {
     "@babel/cli": "7.12.10"
diff --git a/test_data/pnpm_monorepo/packages/package.json b/test_data/pnpm_monorepo/packages/package.json
index d70d06af..a35318f8 100644
--- a/test_data/pnpm_monorepo/packages/package.json
+++ b/test_data/pnpm_monorepo/packages/package.json
@@ -5,10 +5,6 @@
   "scripts": {
     "build": "echo build",
     "start": "echo start",
-    "app1": "pnpm -F \"app1\"",
-    "test-app1": "pnpm -F \"app1\" run test",
-    "app2": "pnpm -F \"app2\"",
-    "app4": "pnpm -C packages/app4",
     "test": "echo test"
   },
   "devDependencies": {
diff --git a/test_data/pnpm_monorepo/packages/pnpm-lock.yaml b/test_data/pnpm_monorepo/packages/pnpm-lock.yaml
deleted file mode 100644
index 18ec407e..00000000
--- a/test_data/pnpm_monorepo/packages/pnpm-lock.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-packages:
-  - 'packages/*'
diff --git a/test_data/pnpm_monorepo/packages/pnpm-workspace.yaml b/test_data/pnpm_monorepo/packages/pnpm-workspace.yaml
deleted file mode 100644
index 18ec407e..00000000
--- a/test_data/pnpm_monorepo/packages/pnpm-workspace.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-packages:
-  - 'packages/*'
diff --git a/test_data/pnpm_monorepo/packages/sub_packages/sub_app/package.json b/test_data/pnpm_monorepo/packages/sub_packages/sub_app/package.json
new file mode 100644
index 00000000..fbcc390f
--- /dev/null
+++ b/test_data/pnpm_monorepo/packages/sub_packages/sub_app/package.json
@@ -0,0 +1,16 @@
+{
+  "name": "sub_app",
+  "version": "1.0.0",
+  "private": true,
+  "scripts": {
+    "build_sub_app": "echo build sub_app",
+    "start_sub_app": "echo start sub_app",
+    "test_sub_app": "echo test sub_app"
+  },
+  "devDependencies": {
+    "@babel/cli": "7.12.10"
+  },
+  "dependencies": {
+    "firebase": "^8.6.8"
+  }
+}
diff --git a/test_data/pnpm_monorepo/pnpm-workspace.yaml b/test_data/pnpm_monorepo/pnpm-workspace.yaml
new file mode 100644
index 00000000..61ef7e9e
--- /dev/null
+++ b/test_data/pnpm_monorepo/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+packages:
+  - 'packages/*'
+  - 'packages/sub_packages/*'