import { sync as findUpSync } from 'find-up'; import findYarnOrNpmWorkspaceRootUnsafe from 'find-yarn-workspace-root'; import fs from 'fs'; import yaml from 'js-yaml'; import micromatch from 'micromatch'; import path from 'path'; export const NPM_LOCK_FILE = 'package-lock.json'; export const YARN_LOCK_FILE = 'yarn.lock'; export const PNPM_LOCK_FILE = 'pnpm-lock.yaml'; export const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'; export const BUN_LOCK_FILE = 'bun.lockb'; /** Wraps `find-yarn-workspace-root` and guards against having an empty `package.json` file in an upper directory. */ export function findYarnOrNpmWorkspaceRoot(projectRoot: string): string | null { try { return findYarnOrNpmWorkspaceRootUnsafe(projectRoot); } catch (error: any) { if (error.message.includes('Unexpected end of JSON input')) { return null; } throw error; } } /** * Find the `pnpm-workspace.yaml` file that represents the root of the monorepo. * This is a synchronous function based on the original async library. * @see https://github.com/pnpm/pnpm/blob/main/packages/find-workspace-dir/src/index.ts */ export function findPnpmWorkspaceRoot(projectRoot: string): string | null { const workspaceEnvName = 'NPM_CONFIG_WORKSPACE_DIR'; const workspaceEnvValue = process.env[workspaceEnvName] ?? process.env[workspaceEnvName.toLowerCase()]; const workspaceFile = workspaceEnvValue ? path.join(workspaceEnvValue, PNPM_WORKSPACE_FILE) : findUpSync(PNPM_WORKSPACE_FILE, { cwd: projectRoot }); if (!workspaceFile || !fs.existsSync(workspaceFile)) { return null; } try { // See: https://pnpm.io/pnpm-workspace_yaml const { packages: workspaces } = yaml.load(fs.readFileSync(workspaceFile, 'utf8')); // See: https://github.com/square/find-yarn-workspace-root/blob/11f6e31d3fa15a5bb7b7419f0091390e4c16204c/index.js#L26-L33 const workspaceRoot = path.dirname(workspaceFile); const relativePath = path.relative(workspaceRoot, projectRoot); if (relativePath === '' || micromatch([relativePath], workspaces).length > 0) { return workspaceRoot; } } catch { // TODO: implement debug logger? return null; } return null; }