1import { sync as findUpSync } from 'find-up'; 2import findYarnOrNpmWorkspaceRootUnsafe from 'find-yarn-workspace-root'; 3import fs from 'fs'; 4import yaml from 'js-yaml'; 5import micromatch from 'micromatch'; 6import path from 'path'; 7 8export const NPM_LOCK_FILE = 'package-lock.json'; 9export const YARN_LOCK_FILE = 'yarn.lock'; 10export const PNPM_LOCK_FILE = 'pnpm-lock.yaml'; 11export const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'; 12 13/** Wraps `find-yarn-workspace-root` and guards against having an empty `package.json` file in an upper directory. */ 14export function findYarnOrNpmWorkspaceRoot(projectRoot: string): string | null { 15 try { 16 return findYarnOrNpmWorkspaceRootUnsafe(projectRoot); 17 } catch (error: any) { 18 if (error.message.includes('Unexpected end of JSON input')) { 19 return null; 20 } 21 throw error; 22 } 23} 24 25/** 26 * Find the `pnpm-workspace.yaml` file that represents the root of the monorepo. 27 * This is a synchronous function based on the original async library. 28 * @see https://github.com/pnpm/pnpm/blob/main/packages/find-workspace-dir/src/index.ts 29 */ 30export function findPnpmWorkspaceRoot(projectRoot: string): string | null { 31 const workspaceEnvName = 'NPM_CONFIG_WORKSPACE_DIR'; 32 const workspaceEnvValue = 33 process.env[workspaceEnvName] ?? process.env[workspaceEnvName.toLowerCase()]; 34 35 const workspaceFile = workspaceEnvValue 36 ? path.join(workspaceEnvValue, PNPM_WORKSPACE_FILE) 37 : findUpSync(PNPM_WORKSPACE_FILE, { cwd: projectRoot }); 38 39 if (!workspaceFile || !fs.existsSync(workspaceFile)) { 40 return null; 41 } 42 43 try { 44 // See: https://pnpm.io/pnpm-workspace_yaml 45 const { packages: workspaces } = yaml.load(fs.readFileSync(workspaceFile, 'utf8')); 46 // See: https://github.com/square/find-yarn-workspace-root/blob/11f6e31d3fa15a5bb7b7419f0091390e4c16204c/index.js#L26-L33 47 const workspaceRoot = path.dirname(workspaceFile); 48 const relativePath = path.relative(workspaceRoot, projectRoot); 49 50 if (relativePath === '' || micromatch([relativePath], workspaces).length > 0) { 51 return workspaceRoot; 52 } 53 } catch { 54 // TODO: implement debug logger? 55 return null; 56 } 57 58 return null; 59} 60