UNPKG

@tryloop/oats

Version:

🌾 OATS - OpenAPI TypeScript Sync. The missing link between your OpenAPI specs and TypeScript applications. Automatically watch, generate, and sync TypeScript clients from your API definitions.

144 lines • 4.63 kB
import { promises as fs } from 'fs'; import path from 'path'; import { execa } from 'execa'; import { Logger } from './logger.js'; const logger = new Logger('Platform'); export class PlatformUtils { static isWindows = process.platform === 'win32'; static isMac = process.platform === 'darwin'; static isLinux = process.platform === 'linux'; static getConfig() { if (this.isWindows) { return { fileWatcherDebounce: 500, // Windows triggers multiple events portCleanupWait: 3000, // Windows holds ports longer processTermSignal: 'SIGKILL', // Windows doesn't support SIGTERM well useShell: true, // Windows needs shell for some commands }; } return { fileWatcherDebounce: 100, portCleanupWait: 1000, processTermSignal: 'SIGTERM', useShell: false, }; } /** * Touch a file to trigger HMR (cross-platform) */ static async touchFile(filePath) { try { if (this.isWindows) { // Windows: Update file timestamp const now = new Date(); await fs.utimes(filePath, now, now); logger.debug(`Touched file (Windows): ${filePath}`); } else { // Unix: Use native touch command await execa('touch', [filePath]); logger.debug(`Touched file (Unix): ${filePath}`); } } catch (error) { logger.warn(`Failed to touch file ${filePath}: ${error}`); // Fallback: Write empty string to trigger change await fs.appendFile(filePath, ''); } } /** * Kill a process (cross-platform) */ static async killProcess(pid, signal) { const config = this.getConfig(); const killSignal = signal || config.processTermSignal; try { if (this.isWindows) { // Windows: Use taskkill await execa('taskkill', ['/F', '/PID', pid.toString()], { shell: true, windowsHide: true, }); logger.debug(`Killed process ${pid} (Windows)`); } else { // Unix: Use standard kill process.kill(pid, killSignal); logger.debug(`Killed process ${pid} with signal ${killSignal} (Unix)`); } } catch (error) { if (error.code !== 'ESRCH') { // Process not found is OK throw error; } } } /** * Wait for port cleanup (platform-specific timing) */ static async waitForPortCleanup() { const config = this.getConfig(); logger.debug(`Waiting ${config.portCleanupWait}ms for port cleanup (${process.platform})`); await new Promise((resolve) => setTimeout(resolve, config.portCleanupWait)); } /** * Get file watcher debounce time */ static getFileWatcherDebounce() { const config = this.getConfig(); logger.debug(`File watcher debounce: ${config.fileWatcherDebounce}ms (${process.platform})`); return config.fileWatcherDebounce; } /** * Normalize path for the current platform */ static normalizePath(filePath) { if (this.isWindows) { // Convert forward slashes to backslashes on Windows return filePath.replace(/\//g, path.sep); } return filePath; } /** * Check if running with elevated privileges */ static async hasElevatedPrivileges() { if (this.isWindows) { try { await execa('net', ['session'], { shell: true }); return true; } catch { return false; } } else { return process.getuid?.() === 0; } } /** * Get npm/yarn executable with proper extension */ static getNpmExecutable(pm) { if (this.isWindows) { return `${pm}.cmd`; } return pm; } /** * Get debug info for platform-specific issues */ static getDebugInfo() { return { platform: process.platform, arch: process.arch, nodeVersion: process.version, isWindows: this.isWindows, isMac: this.isMac, isLinux: this.isLinux, config: this.getConfig(), }; } } //# sourceMappingURL=platform.js.map