listenForChanges = (projectDirs, pluginDirs) => {
  // watch project dirs
  for (const projectDir of projectDirs) {
    activeWatchers.push(
      watch(
        projectDir,
        watchCfg,
        // event is either 'update' or 'remove'
        (event, file) => {
          console.log("'%s' changed \n re-starting now", file);
          closeWatchers();
          spawnSync("npm", ["run", "tsc"], {
            stdio: "inherit",
          });
          process.exit();
        }
      )
    );
  }
  // watch plugin dirs
  for (const pluginDir of pluginDirs) {
    activeWatchers.push(
      watch(
        pluginDir,
        watchCfg,
        // event is either 'update' or 'remove'
        (event, file) => {
          console.log("'%s' changed \n re-starting now", file);
          closeWatchers();
          process.exit();
        }
      )
    );
  }
}
register many file change listener and do re-starts on changes
The listener calls process.exit() and assumes
that pm2 does the actual re-start.
@param {string[]} projectDirs package paths that should trigger re-starts.
@param {string[]} pluginDirs plugin paths that should trigger re-starts.