UNPKG

@rechunk/utils

Version:

Utility functions and helpers for ReChunk packages and implementations

179 lines (161 loc) 5.7 kB
import {execFileSync} from 'child_process'; import findUp from 'find-up'; import fs from 'fs'; import {basename, dirname, resolve} from 'path'; /** * Resolves the real native path for a given file path. * * This is particularly useful for case-insensitive file systems, * where different casing can lead to discrepancies in file paths. * For example, accessing the file as `C:\Code\Project` vs. `c:\code\projects` * can cause issues when installing packages with the `-w` flag if the root directory * is not consistently cased. * * @param {string} filePath - The path of the file to resolve * @returns {string} The resolved real path, or the original path if resolution fails */ export function getRealPath(filePath: string): string { try { return fs.realpathSync.native(filePath); } catch { return filePath; } } /** * Recursively searches for the closest JSON file, starting from a given directory. * * @param {string} filename - The name of the JSON file to search for * @param {string} start - The directory to start searching from, defaults to current working directory * @param {number} level - The current depth of the recursive search, used to limit recursion * @returns {any} The parsed JSON content, or an empty object if file not found after 10 levels */ export function findClosestJSON( filename: string, start: string = process.cwd(), level: number = 0, ): any { try { const path = resolve(start, filename); const content = fs.readFileSync(path, {encoding: 'utf8'}); return JSON.parse(content); } catch { return level >= 10 ? {} : findClosestJSON(filename, dirname(start), level + 1); } } /** * Finds the workspace directory by locating the workspace manifest file. * * Searches for workspace-related files using the `find-up` package * in the current directory and its parents. Looks for: * - package.json with workspaces field (npm/yarn/bun) * - yarn.lock, pnpm-lock.yaml, or bun.lockb files * * @param {string} cwd - The current working directory from which to start searching * @returns {string | undefined} The path to the workspace directory if found, or undefined if not found */ export function findWorkspaceDir(cwd: string): string | undefined { if (process.env.WORKSPACE_DIR) { return process.env.WORKSPACE_DIR; } const manifestLocation = findUp.sync( ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb'], { cwd: getRealPath(cwd), }, ); return manifestLocation ? dirname(manifestLocation) : undefined; } /** * Options for process listing operations */ export interface ProcessOptions { /** Whether to show all processes. Defaults to true */ all?: boolean; } /** * Information about a running process */ export interface ProcessInfo { /** Process ID */ pid: number; /** Parent Process ID */ ppid: number; /** User ID */ uid: number; /** CPU usage percentage */ cpu: number; /** Memory usage percentage */ memory: number; /** Process name */ name: string; /** Full command line */ cmd: string; } const TEN_MEGABYTES = 1000 * 1000 * 10; const PS_OUTPUT_REGEX = /^[ \t]*(?<pid>\d+)[ \t]+(?<ppid>\d+)[ \t]+(?<uid>[-\d]+)[ \t]+(?<cpu>\d+\.\d+)[ \t]+(?<memory>\d+\.\d+)[ \t]+(?<comm>.*)?/; /** * Retrieves information about running processes on a non-Windows system using the `ps` command. * * @param {ProcessOptions} options - Optional settings for the command * @returns {ProcessInfo[]} Array of objects containing process information * @throws {Error} If parsing the output from `ps` fails */ export function nonWindowsCall(options: ProcessOptions = {}): ProcessInfo[] { const flags = options.all === false ? 'wwxo' : 'awwxo'; const psOutput = execFileSync('ps', [flags, 'pid,ppid,uid,%cpu,%mem,comm'], { maxBuffer: TEN_MEGABYTES, }).toString(); const psArgsOutput = execFileSync('ps', [flags, 'pid,args'], { maxBuffer: TEN_MEGABYTES, }).toString(); const psLines = psOutput.trim().split('\n'); const psArgsLines = psArgsOutput.trim().split('\n'); psLines.shift(); psArgsLines.shift(); const processCmds: Record<string, string> = {}; for (const line of psArgsLines) { const [pid, ...cmds] = line.trim().split(' '); processCmds[pid] = cmds.join(' '); } return psLines.map(line => { const match = PS_OUTPUT_REGEX.exec(line); if (!match?.groups) throw new Error('ps output parsing failed'); const {pid, ppid, uid, cpu, memory, comm} = match.groups; return { pid: parseInt(pid, 10), ppid: parseInt(ppid, 10), uid: parseInt(uid, 10), cpu: parseFloat(cpu), memory: parseFloat(memory), name: basename(comm), cmd: processCmds[pid], }; }); } /** * Checks if the Rechunk development server is currently running. * * Inspects the system's running processes, looking for processes * that match the `rechunk dev-server` command. Checks for matches that either: * - Start with `node ./node_modules` * - Start with `node <workspaceDir>` * * @returns {boolean} `true` if the Rechunk development server is running, otherwise `false` */ export function isRechunkDevServerRunning(): boolean { const processes = nonWindowsCall(); const workspaceDir = findWorkspaceDir(process.cwd()); const NODE_IDENTIFIER = 'node'; const NODE_MODULES_PATH = './node_modules'; const DEV_SERVER_CMD = 'rechunk dev-server'; return processes.some( process => typeof process.cmd === 'string' && process.cmd.includes(DEV_SERVER_CMD) && (process.cmd.startsWith(`${NODE_IDENTIFIER} ${NODE_MODULES_PATH}`) || process.cmd.startsWith(`${NODE_IDENTIFIER} ${workspaceDir}`)), ); }