UNPKG

shell-mirror

Version:

Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.

168 lines (146 loc) 4.28 kB
'use strict' const fs = require('fs') const path = require('path') /* istanbul ignore next */ const LCHOWN = fs.lchown ? 'lchown' : 'chown' /* istanbul ignore next */ const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync' /* istanbul ignore next */ const needEISDIRHandled = fs.lchown && !process.version.match(/v1[1-9]+\./) && !process.version.match(/v10\.[6-9]/) const lchownSync = (path, uid, gid) => { try { return fs[LCHOWNSYNC](path, uid, gid) } catch (er) { if (er.code !== 'ENOENT') throw er } } /* istanbul ignore next */ const chownSync = (path, uid, gid) => { try { return fs.chownSync(path, uid, gid) } catch (er) { if (er.code !== 'ENOENT') throw er } } /* istanbul ignore next */ const handleEISDIR = needEISDIRHandled ? (path, uid, gid, cb) => er => { // Node prior to v10 had a very questionable implementation of // fs.lchown, which would always try to call fs.open on a directory // Fall back to fs.chown in those cases. if (!er || er.code !== 'EISDIR') cb(er) else fs.chown(path, uid, gid, cb) } : (_, __, ___, cb) => cb /* istanbul ignore next */ const handleEISDirSync = needEISDIRHandled ? (path, uid, gid) => { try { return lchownSync(path, uid, gid) } catch (er) { if (er.code !== 'EISDIR') throw er chownSync(path, uid, gid) } } : (path, uid, gid) => lchownSync(path, uid, gid) // fs.readdir could only accept an options object as of node v6 const nodeVersion = process.version let readdir = (path, options, cb) => fs.readdir(path, options, cb) let readdirSync = (path, options) => fs.readdirSync(path, options) /* istanbul ignore next */ if (/^v4\./.test(nodeVersion)) readdir = (path, options, cb) => fs.readdir(path, cb) const chown = (cpath, uid, gid, cb) => { fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, er => { // Skip ENOENT error cb(er && er.code !== 'ENOENT' ? er : null) })) } const chownrKid = (p, child, uid, gid, cb) => { if (typeof child === 'string') return fs.lstat(path.resolve(p, child), (er, stats) => { // Skip ENOENT error if (er) return cb(er.code !== 'ENOENT' ? er : null) stats.name = child chownrKid(p, stats, uid, gid, cb) }) if (child.isDirectory()) { chownr(path.resolve(p, child.name), uid, gid, er => { if (er) return cb(er) const cpath = path.resolve(p, child.name) chown(cpath, uid, gid, cb) }) } else { const cpath = path.resolve(p, child.name) chown(cpath, uid, gid, cb) } } const chownr = (p, uid, gid, cb) => { readdir(p, { withFileTypes: true }, (er, children) => { // any error other than ENOTDIR or ENOTSUP means it's not readable, // or doesn't exist. give up. if (er) { if (er.code === 'ENOENT') return cb() else if (er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP') return cb(er) } if (er || !children.length) return chown(p, uid, gid, cb) let len = children.length let errState = null const then = er => { if (errState) return if (er) return cb(errState = er) if (-- len === 0) return chown(p, uid, gid, cb) } children.forEach(child => chownrKid(p, child, uid, gid, then)) }) } const chownrKidSync = (p, child, uid, gid) => { if (typeof child === 'string') { try { const stats = fs.lstatSync(path.resolve(p, child)) stats.name = child child = stats } catch (er) { if (er.code === 'ENOENT') return else throw er } } if (child.isDirectory()) chownrSync(path.resolve(p, child.name), uid, gid) handleEISDirSync(path.resolve(p, child.name), uid, gid) } const chownrSync = (p, uid, gid) => { let children try { children = readdirSync(p, { withFileTypes: true }) } catch (er) { if (er.code === 'ENOENT') return else if (er.code === 'ENOTDIR' || er.code === 'ENOTSUP') return handleEISDirSync(p, uid, gid) else throw er } if (children && children.length) children.forEach(child => chownrKidSync(p, child, uid, gid)) return handleEISDirSync(p, uid, gid) } module.exports = chownr chownr.sync = chownrSync