Repository: spotify/backstage
File: packages/cli-node/src/pacman/PackageManager.ts (TypeScript)

Code before:
  supportsBackstageVersionProtocol(): Promise<boolean>;

  /** A string representation of the package manager. */
  toString(): string;
}

/**
 * Uses several mechanisms to detect the currently used package manager. The
 * detection methods are intended to be ordered roughly from fastest to slowest
 * in order to make this method as fast as possible.
 *
 * @public
 */
export async function detectPackageManager(): Promise<PackageManager> {
  const hasYarnLockfile = await fileExists(
    paths.resolveTargetRoot('yarn.lock'),
  );
  if (hasYarnLockfile) {
    return await Yarn.create();
  }

  try {
    const packageJson = await fs.readJson(
      paths.resolveTargetRoot('package.json'),
    );
    if (getWorkspacesPatterns(packageJson).length > 0) {
      // technically this could be NPM as well
      return await Yarn.create();
    }

    const declaredPacman = packageJson.packageManager;
    if (declaredPacman) {
      const [name, _version] = declaredPacman.split('@');
      switch (name) {
        case 'yarn':
          return await Yarn.create();
        default:
          console.log(`Detected unsupported package manager: ${name}.`);
          return await Yarn.create();
      }
    }
  } catch (error) {
    console.log(`Error during package manager detection: ${error}`);
  }

  // currently yarn is the only package manager supported so just log an error and use it anyway
  console.log(
    'Yarn was not detected, but is the only supported package manager.',
  );
  return await Yarn.create();
}

Review comment (suggestion):
Some logic change here, as it would have previously matched on `workspaces.packages: []`

Seems more correct but feedback is welcomed!

Code after:
/**
 * Uses several mechanisms to detect the currently used package manager. The
 * detection methods are intended to be ordered roughly from fastest to slowest
 * in order to make this method as fast as possible.
 *
 * @public
 */
export async function detectPackageManager(): Promise<PackageManager> {
  const hasYarnLockfile = await fileExists(
    targetPaths.resolveRoot('yarn.lock'),
  );
  if (hasYarnLockfile) {
    return await Yarn.create();
  }

  try {
    const packageJson = await fs.readJson(
      targetPaths.resolveRoot('package.json'),
    );
    if (packageJson.workspaces) {
      // technically this could be NPM as well
      return await Yarn.create();
    }

    const declaredPacman = packageJson.packageManager;
    if (declaredPacman) {
      const [name, _version] = declaredPacman.split('@');
      switch (name) {
        case 'yarn':
          return await Yarn.create();
        default:
          console.log(`Detected unsupported package manager: ${name}.`);
          return await Yarn.create();
      }
    }
  } catch (error) {
    console.log(`Error during package manager detection: ${error}`);
  }

  // currently yarn is the only package manager supported so just log an error and use it anyway
  console.log(
    'Yarn was not detected, but is the only supported package manager.',
  );
  return await Yarn.create();
}

async function fileExists(filePath: string): Promise<boolean> {
  try {
    await fs.access(filePath);
    return true;
  } catch (error) {