Comparing version 0.2.1 to 0.2.2
@@ -14,2 +14,3 @@ /*! | ||
const {ArgError, FSError} = require('./error'); | ||
const features = require('./features'); | ||
const util = require('./util'); | ||
@@ -119,3 +120,2 @@ const {EIO, ENOTDIR} = FSError; | ||
const flag = (flags & COPYFILE_EXCL) ? 'wx' : 'w'; | ||
const slab = Buffer.allocUnsafe(64 * 1024); | ||
@@ -126,2 +126,3 @@ let rfd = null; | ||
let pos = 0; | ||
let slab = null; | ||
@@ -133,11 +134,21 @@ try { | ||
// Maximum size of `off_t` on linux. | ||
if (stat.size > 0x7ffff000) | ||
throw new FSError(EIO, 'read', fromPath(src)); | ||
slab = Buffer.allocUnsafe(Math.min(64 * 1024, stat.size)); | ||
while (pos < stat.size) { | ||
const length = Math.min(stat.size - pos, slab.length); | ||
const bytes = fs.readSync(rfd, slab, 0, length, pos); | ||
const bytesRead = fs.readSync(rfd, slab, 0, length, pos); | ||
if (bytes !== length) | ||
if (bytesRead !== length) | ||
throw new FSError(EIO, 'read', fromPath(src)); | ||
fs.writeSync(wfd, slab, 0, length, null); | ||
pos += bytes; | ||
const bytesWritten = fs.writeSync(wfd, slab, 0, length, null); | ||
if (bytesWritten !== length) | ||
throw new FSError(EIO, 'write', fromPath(src)); | ||
pos += length; | ||
} | ||
@@ -384,2 +395,127 @@ } finally { | ||
/* | ||
* opendir() | ||
*/ | ||
async function opendir(path, options) { | ||
if (typeof options === 'string') | ||
options = { encoding: options }; | ||
const list = await readdir(path, { | ||
encoding: options ? options.encoding : undefined, | ||
withFileTypes: true | ||
}); | ||
return new Dir(path, list); | ||
} | ||
function opendirSync(path, options) { | ||
if (typeof options === 'string') | ||
options = { encoding: options }; | ||
const list = readdirSync(path, { | ||
encoding: options ? options.encoding : undefined, | ||
withFileTypes: true | ||
}); | ||
return new Dir(path, list); | ||
} | ||
/** | ||
* Dir | ||
*/ | ||
class Dir { | ||
constructor(path, list) { | ||
this.path = path; | ||
this._list = list; | ||
this._index = 0; | ||
} | ||
_error() { | ||
const err = new Error('Directory handle was closed'); | ||
err.code = 'ERR_DIR_CLOSED'; | ||
err.name = `Error [${err.code}]`; | ||
if (Error.captureStackTrace) | ||
Error.captureStackTrace(err, this._error); | ||
return err; | ||
} | ||
async close(callback) { | ||
if (typeof callback === 'function') { | ||
try { | ||
this.closeSync(); | ||
} catch (e) { | ||
callback(e); | ||
return; | ||
} | ||
callback(); | ||
return; | ||
} | ||
this.closeSync(); | ||
} | ||
closeSync() { | ||
if (this._index === -1) | ||
throw this._error(); | ||
this._index = -1; | ||
} | ||
async read(callback) { | ||
if (typeof callback === 'function') { | ||
let item; | ||
try { | ||
item = this.readSync(); | ||
} catch (e) { | ||
callback(e); | ||
return undefined; | ||
} | ||
callback(null, item); | ||
return undefined; | ||
} | ||
return this.readSync(); | ||
} | ||
readSync() { | ||
if (this._index === -1) | ||
throw this._error(); | ||
if (this._index === this._list.length) | ||
return null; | ||
return this._list[this._index++]; | ||
} | ||
entries() { | ||
return { | ||
next: async () => { | ||
let item; | ||
try { | ||
item = this.readSync(); | ||
} catch (e) { | ||
item = null; | ||
} | ||
if (item === null) { | ||
this._index = -1; | ||
return { value: undefined, done: true }; | ||
} | ||
return { value: item, done: false }; | ||
} | ||
}; | ||
} | ||
[Symbol.asyncIterator || 'asyncIterator']() { | ||
return this.entries(); | ||
} | ||
} | ||
/* | ||
* realpath.native() | ||
@@ -405,2 +541,199 @@ */ | ||
/* | ||
* rmdir() | ||
*/ | ||
async function rmdir(path, options) { | ||
if (options && options.recursive) { | ||
path = fromPath(path); | ||
let {maxRetries, retryDelay} = options; | ||
if (maxRetries == null) | ||
maxRetries = 0; | ||
if (retryDelay == null) | ||
retryDelay = 100; | ||
if ((maxRetries >>> 0) !== maxRetries) | ||
throw new ArgError('maxRetries', maxRetries, 'integer'); | ||
if ((retryDelay >>> 0) !== retryDelay) | ||
throw new ArgError('retryDelay', retryDelay, 'integer'); | ||
let tries = 0; | ||
for (;;) { | ||
try { | ||
await _rmdir(path); | ||
} catch (e) { | ||
const retry = e.code === 'EBUSY' | ||
|| e.code === 'ENOTEMPTY' | ||
|| e.code === 'EPERM' | ||
|| e.code === 'EMFILE' | ||
|| e.code === 'ENFILE'; | ||
if (retry && tries < maxRetries) { | ||
tries += 1; | ||
await wait(tries * retryDelay); | ||
continue; | ||
} | ||
throw e; | ||
} | ||
break; | ||
} | ||
return undefined; | ||
} | ||
return call(fs.rmdir, [path]); | ||
} | ||
async function _rmdir(path) { | ||
let stat = null; | ||
try { | ||
stat = await safeStat(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
if (stat.isDirectory()) { | ||
let list = null; | ||
try { | ||
list = await call(fs.readdir, [path]); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
for (const name of list) | ||
await _rmdir(join(path, name)); | ||
try { | ||
await call(fs.rmdir, [path]); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
return; | ||
} | ||
try { | ||
await call(fs.unlink, [path]); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
} | ||
function rmdirSync(path, options) { | ||
if (options && options.recursive) { | ||
path = fromPath(path); | ||
let {maxRetries} = options; | ||
if (maxRetries == null) | ||
maxRetries = 0; | ||
if ((maxRetries >>> 0) !== maxRetries) | ||
throw new ArgError('maxRetries', maxRetries, 'integer'); | ||
let tries = 0; | ||
for (;;) { | ||
try { | ||
_rmdirSync(path, maxRetries); | ||
} catch (e) { | ||
const retry = e.code === 'EBUSY' | ||
|| e.code === 'ENOTEMPTY' | ||
|| e.code === 'EPERM' | ||
|| e.code === 'EMFILE' | ||
|| e.code === 'ENFILE'; | ||
if (retry && tries < maxRetries) { | ||
tries += 1; | ||
continue; | ||
} | ||
throw e; | ||
} | ||
break; | ||
} | ||
return; | ||
} | ||
fs.rmdirSync(path); | ||
} | ||
function _rmdirSync(path, maxRetries) { | ||
let stat = null; | ||
try { | ||
stat = safeStatSync(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
if (stat.isDirectory()) { | ||
let list = null; | ||
try { | ||
list = fs.readdirSync(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
for (const name of list) | ||
_rmdirSync(join(path, name), maxRetries); | ||
let tries = 0; | ||
for (;;) { | ||
try { | ||
fs.rmdirSync(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
if (e.code === 'ENOTEMPTY' && process.platform === 'win32') { | ||
if (tries < maxRetries + 1) { | ||
tries += 1; | ||
continue; | ||
} | ||
} | ||
throw e; | ||
} | ||
break; | ||
} | ||
return; | ||
} | ||
try { | ||
fs.unlinkSync(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return; | ||
throw e; | ||
} | ||
} | ||
/* | ||
* stat() | ||
@@ -410,2 +743,9 @@ */ | ||
function wrapStat(statter) { | ||
if (features.HAS_STAT_BIGINTS) { | ||
return async function stat(file, options) { | ||
if (options == null) | ||
options = {}; | ||
return convertStat(await call(statter, [file, options]), options); | ||
}; | ||
} | ||
return async function stat(file, options) { | ||
@@ -417,2 +757,9 @@ return convertStat(await call(statter, [file]), options); | ||
function wrapStatSync(statter) { | ||
if (features.HAS_STAT_BIGINTS) { | ||
return function statSync(file, options) { | ||
if (options == null) | ||
options = {}; | ||
return convertStat(statter(file, options), options); | ||
}; | ||
} | ||
return function statSync(file, options) { | ||
@@ -436,3 +783,3 @@ return convertStat(statter(file), options); | ||
if (typeof BigInt !== 'function') | ||
throw new Error('Bigint is not supported.'); | ||
throw new Error('BigInt is not supported.'); | ||
@@ -455,2 +802,12 @@ stats.dev = BigInt(stats.dev); | ||
if (bigint && stats.atimeNs == null) { | ||
if (typeof BigInt !== 'function') | ||
throw new Error('BigInt is not supported.'); | ||
stats.atimeNs = stats.atimeMs * BigInt(1000000); | ||
stats.mtimeNs = stats.mtimeMs * BigInt(1000000); | ||
stats.ctimeNs = stats.ctimeMs * BigInt(1000000); | ||
stats.birthtimeNs = stats.birthtimeMs * BigInt(1000000); | ||
} | ||
return stats; | ||
@@ -502,2 +859,50 @@ } | ||
/* | ||
* writev() | ||
*/ | ||
async function writev(fd, buffers, position) { | ||
if (!Array.isArray(buffers)) | ||
throw new ArgError('buffers', buffers, 'ArrayBufferView[]'); | ||
let written = 0; | ||
for (const array of buffers) { | ||
const buf = toBuffer(array); | ||
const bytes = await call(fs.write, [fd, buf, 0, buf.length, position]); | ||
if (bytes !== buf.length) | ||
throw new FSError(EIO, 'writev'); | ||
if (typeof position === 'number') | ||
position += bytes; | ||
written += bytes; | ||
} | ||
return written; | ||
} | ||
function writevSync(fd, buffers, position) { | ||
if (!Array.isArray(buffers)) | ||
throw new ArgError('buffers', buffers, 'ArrayBufferView[]'); | ||
let written = 0; | ||
for (const array of buffers) { | ||
const buf = toBuffer(array); | ||
const bytes = fs.writeSync(fd, buf, 0, buf.length, position); | ||
if (bytes !== buf.length) | ||
throw new FSError(EIO, 'writev'); | ||
if (typeof position === 'number') | ||
position += bytes; | ||
written += bytes; | ||
} | ||
return written; | ||
} | ||
/** | ||
@@ -577,2 +982,9 @@ * FileHandle | ||
} | ||
async writev(...args) { | ||
return { | ||
bytesWritten: await writev(this._fd, ...args), | ||
buffers: args[0] | ||
}; | ||
} | ||
} | ||
@@ -600,2 +1012,3 @@ | ||
}, | ||
opendir, | ||
readdir, | ||
@@ -606,3 +1019,3 @@ readFile: promisify(fs.readFile), | ||
rename: promisify(fs.rename), | ||
rmdir: promisify(fs.rmdir), | ||
rmdir, | ||
stat, | ||
@@ -634,2 +1047,3 @@ symlink: promisify(fs.symlink), | ||
open: promises.open, | ||
opendir: promises.opendir, | ||
readdir: promises.readdir, | ||
@@ -650,3 +1064,4 @@ readFile: promises.readFile, | ||
function patchTypedArray(promises) { | ||
function patchHandle(name, promises, callback) { | ||
const key = `__patch_${name}`; | ||
const {open} = promises; | ||
@@ -661,29 +1076,6 @@ | ||
const FileHandle = handle.constructor; | ||
const proto = FileHandle.prototype; | ||
const {read, write, writeFile} = proto; | ||
if (!FileHandle.__hasPatch) { | ||
// eslint-disable-next-line | ||
proto.read = function _read(...args) { | ||
args[0] = toBuffer(args[0]); | ||
return read.call(this, ...args); | ||
}; | ||
// eslint-disable-next-line | ||
proto.write = function _write(...args) { | ||
if (typeof args[0] !== 'string') | ||
args[0] = toBuffer(args[0]); | ||
return write.call(this, ...args); | ||
}; | ||
// eslint-disable-next-line | ||
proto.writeFile = function _writeFile(...args) { | ||
if (typeof args[0] !== 'string') | ||
args[0] = toBuffer(args[0]); | ||
return writeFile.call(this, ...args); | ||
}; | ||
FileHandle.__hasPatch = true; | ||
if (!FileHandle[key]) { | ||
callback(FileHandle.prototype); | ||
FileHandle[key] = true; | ||
} | ||
@@ -702,2 +1094,41 @@ | ||
function patchStat(promises) { | ||
patchHandle('stat', promises, (proto) => { | ||
const {stat} = proto; | ||
// eslint-disable-next-line | ||
proto.stat = async function _stat(options) { | ||
return convertStat(await stat.call(this, options), options); | ||
}; | ||
}); | ||
} | ||
function patchTypedArray(promises) { | ||
patchHandle('typedArray', promises, (proto) => { | ||
const {read, write, writeFile} = proto; | ||
// eslint-disable-next-line | ||
proto.read = function _read(...args) { | ||
args[0] = toBuffer(args[0]); | ||
return read.call(this, ...args); | ||
}; | ||
// eslint-disable-next-line | ||
proto.write = function _write(...args) { | ||
if (typeof args[0] !== 'string') | ||
args[0] = toBuffer(args[0]); | ||
return write.call(this, ...args); | ||
}; | ||
// eslint-disable-next-line | ||
proto.writeFile = function _writeFile(...args) { | ||
if (typeof args[0] !== 'string') | ||
args[0] = toBuffer(args[0]); | ||
return writeFile.call(this, ...args); | ||
}; | ||
}); | ||
} | ||
function patchOpenFlags(promises) { | ||
@@ -714,3 +1145,72 @@ const {open} = promises; | ||
function patchWritev(promises) { | ||
patchHandle('writev', promises, (proto) => { | ||
proto.writev = async function writev(buffers, position) { | ||
if (!Array.isArray(buffers)) | ||
throw new ArgError('buffers', buffers, 'ArrayBufferView[]'); | ||
let written = 0; | ||
for (const array of buffers) { | ||
const buf = toBuffer(array); | ||
const {bytesWritten} = await this.write(buf, 0, buf.length, position); | ||
if (bytesWritten !== buf.length) | ||
throw new FSError(EIO, 'writev'); | ||
if (typeof position === 'number') | ||
position += bytesWritten; | ||
written += bytesWritten; | ||
} | ||
return { | ||
bytesWritten: written, | ||
buffers | ||
}; | ||
}; | ||
}); | ||
} | ||
/* | ||
* Helpers | ||
*/ | ||
function wait(ms) { | ||
return new Promise(r => setTimeout(r, ms)); | ||
} | ||
async function safeStat(path) { | ||
try { | ||
return await call(fs.lstat, [path]); | ||
} catch (e) { | ||
if (e.code === 'EPERM' && process.platform === 'win32') { | ||
try { | ||
await call(fs.chmod, [path, 0o666]); | ||
} catch (e) { | ||
; | ||
} | ||
return call(fs.lstat, [path]); | ||
} | ||
throw e; | ||
} | ||
} | ||
function safeStatSync(path) { | ||
try { | ||
return fs.lstatSync(path); | ||
} catch (e) { | ||
if (e.code === 'EPERM' && process.platform === 'win32') { | ||
try { | ||
fs.chmodSync(path, 0o666); | ||
} catch (e) { | ||
; | ||
} | ||
return fs.lstatSync(path); | ||
} | ||
throw e; | ||
} | ||
} | ||
/* | ||
* Expose | ||
@@ -733,4 +1233,9 @@ */ | ||
exports.Dirent = Dirent; | ||
exports.opendir = opendir; | ||
exports.opendirSync = opendirSync; | ||
exports.Dir = Dir; | ||
exports.realpath = realpath; | ||
exports.realpathSync = realpathSync; | ||
exports.rmdir = rmdir; | ||
exports.rmdirSync = rmdirSync; | ||
exports.fstat = fstat; | ||
@@ -746,5 +1251,9 @@ exports.fstatSync = fstatSync; | ||
exports.writeFileSync = writeFileSync; | ||
exports.writev = writev; | ||
exports.writevSync = writevSync; | ||
exports.promises = promises; | ||
exports.clonePromises = clonePromises; | ||
exports.patchStat = patchStat; | ||
exports.patchTypedArray = patchTypedArray; | ||
exports.patchOpenFlags = patchOpenFlags; | ||
exports.patchWritev = patchWritev; |
@@ -52,8 +52,10 @@ /*! | ||
if (args.length === 2) | ||
[message, syscall, path] = [desc.message, ...args]; | ||
else if (args.length === 3) | ||
message = desc.message; | ||
if (args.length === 3) | ||
[message, syscall, path] = args; | ||
else | ||
throw new TypeError('invalid arguments for fs error'); | ||
else if (args.length === 2) | ||
[syscall, path] = args; | ||
else if (args.length === 1) | ||
[syscall] = args; | ||
@@ -60,0 +62,0 @@ let msg = `${desc.code}:`; |
298
lib/extra.js
@@ -62,3 +62,3 @@ /*! | ||
} catch (e) { | ||
if (!isNoEntry(e)) | ||
if (!isIgnorable(e)) | ||
throw e; | ||
@@ -172,3 +172,3 @@ } | ||
} catch (e) { | ||
if (!isNoEntry(e)) | ||
if (!isIgnorable(e)) | ||
throw e; | ||
@@ -250,2 +250,40 @@ } | ||
async function empty(path, mode) { | ||
const dir = fromPath(path); | ||
let list = null; | ||
try { | ||
list = await fs.readdir(dir); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return mkdirp(dir, mode); | ||
throw e; | ||
} | ||
for (const name of list) | ||
await remove(join(dir, name)); | ||
return undefined; | ||
} | ||
function emptySync(path, mode) { | ||
const dir = fromPath(path); | ||
let list = null; | ||
try { | ||
list = fs.readdirSync(dir); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return mkdirpSync(dir, mode); | ||
throw e; | ||
} | ||
for (const name of list) | ||
removeSync(join(dir, name)); | ||
return undefined; | ||
} | ||
async function exists(file, mode) { | ||
@@ -259,3 +297,3 @@ if (mode == null) | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return false; | ||
@@ -274,3 +312,3 @@ throw e; | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return false; | ||
@@ -285,3 +323,3 @@ throw e; | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return null; | ||
@@ -296,3 +334,3 @@ throw e; | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return null; | ||
@@ -344,11 +382,17 @@ throw e; | ||
async function outputFile(path, data, options) { | ||
if (options == null) | ||
options = {}; | ||
if (typeof options === 'string') | ||
options = { encoding: options }; | ||
const file = fromPath(path); | ||
const dir = dirname(file); | ||
let mode = null; | ||
let mode = options.mode; | ||
if (options && typeof options === 'object') | ||
mode = options.mode; | ||
if ((mode & 0o777) === mode) | ||
mode |= (mode & 0o444) >>> 2; | ||
await fs.mkdirp(dir, mode); | ||
await mkdirp(dir, mode); | ||
await fs.writeFile(file, data, options); | ||
@@ -358,11 +402,17 @@ } | ||
function outputFileSync(path, data, options) { | ||
if (options == null) | ||
options = {}; | ||
if (typeof options === 'string') | ||
options = { encoding: options }; | ||
const file = fromPath(path); | ||
const dir = dirname(file); | ||
let mode = null; | ||
let mode = options.mode; | ||
if (options && typeof options === 'object') | ||
mode = options.mode; | ||
if ((mode & 0o777) === mode) | ||
mode |= (mode & 0o444) >>> 2; | ||
fs.mkdirpSync(dir, mode); | ||
mkdirpSync(dir, mode); | ||
fs.writeFileSync(file, data, options); | ||
@@ -390,6 +440,34 @@ } | ||
let ret = 0; | ||
let error = null; | ||
for (const path of paths) | ||
ret += await _remove(path, options, 0); | ||
for (const path of paths) { | ||
let tries = 0; | ||
for (;;) { | ||
try { | ||
ret += await _remove(path, options, 0); | ||
} catch (e) { | ||
const retry = e.code === 'EBUSY' | ||
|| e.code === 'ENOTEMPTY' | ||
|| e.code === 'EPERM' | ||
|| e.code === 'EMFILE' | ||
|| e.code === 'ENFILE'; | ||
if (retry && tries < options.maxRetries) { | ||
tries += 1; | ||
await wait(tries * options.retryDelay); | ||
continue; | ||
} | ||
if (!error) | ||
error = e; | ||
} | ||
break; | ||
} | ||
} | ||
if (error) | ||
throw error; | ||
return ret; | ||
@@ -403,6 +481,6 @@ } | ||
try { | ||
stat = await fs.lstat(path); | ||
stat = await safeStat(path); | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
return ret + 1; | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
@@ -422,4 +500,4 @@ } | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
return ret + 1; | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
@@ -431,8 +509,10 @@ } | ||
try { | ||
await fs.rmdir(path); | ||
} catch (e) { | ||
if (isNoEntry(e) || e.code === 'ENOTEMPTY') | ||
return ret + 1; | ||
throw e; | ||
if (ret === 0) { | ||
try { | ||
await fs.rmdir(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
} | ||
} | ||
@@ -446,4 +526,4 @@ | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
return ret + 1; | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
@@ -460,6 +540,33 @@ } | ||
let ret = 0; | ||
let error = null; | ||
for (const path of paths) | ||
ret += _removeSync(path, options, 0); | ||
for (const path of paths) { | ||
let tries = 0; | ||
for (;;) { | ||
try { | ||
ret += _removeSync(path, options, 0); | ||
} catch (e) { | ||
const retry = e.code === 'EBUSY' | ||
|| e.code === 'ENOTEMPTY' | ||
|| e.code === 'EPERM' | ||
|| e.code === 'EMFILE' | ||
|| e.code === 'ENFILE'; | ||
if (retry && tries < options.maxRetries) { | ||
tries += 1; | ||
continue; | ||
} | ||
if (!error) | ||
error = e; | ||
} | ||
break; | ||
} | ||
} | ||
if (error) | ||
throw error; | ||
return ret; | ||
@@ -473,6 +580,6 @@ } | ||
try { | ||
stat = fs.lstatSync(path); | ||
stat = safeStatSync(path); | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
return ret + 1; | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
@@ -492,4 +599,4 @@ } | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
return ret + 1; | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
@@ -501,8 +608,24 @@ } | ||
try { | ||
fs.rmdirSync(path); | ||
} catch (e) { | ||
if (isNoEntry(e) || e.code === 'ENOTEMPTY') | ||
return ret + 1; | ||
throw e; | ||
if (ret === 0) { | ||
let tries = 0; | ||
for (;;) { | ||
try { | ||
fs.rmdirSync(path); | ||
} catch (e) { | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
if (e.code === 'ENOTEMPTY' && process.platform === 'win32') { | ||
if (tries < options.maxRetries + 1) { | ||
tries += 1; | ||
continue; | ||
} | ||
} | ||
throw e; | ||
} | ||
break; | ||
} | ||
} | ||
@@ -516,4 +639,4 @@ | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
return ret + 1; | ||
if (e.code === 'ENOENT') | ||
return ret; | ||
throw e; | ||
@@ -529,3 +652,3 @@ } | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return null; | ||
@@ -540,3 +663,3 @@ throw e; | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return null; | ||
@@ -554,3 +677,3 @@ throw e; | ||
} catch (e) { | ||
if (!isNoEntry(e)) | ||
if (!isIgnorable(e)) | ||
throw e; | ||
@@ -570,3 +693,3 @@ } | ||
} catch (e) { | ||
if (!isNoEntry(e)) | ||
if (!isIgnorable(e)) | ||
throw e; | ||
@@ -583,3 +706,3 @@ } | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return null; | ||
@@ -594,3 +717,3 @@ throw e; | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return null; | ||
@@ -729,3 +852,3 @@ throw e; | ||
} catch (e) { | ||
if (!isNoEntry(e)) | ||
if (!isIgnorable(e)) | ||
throw e; | ||
@@ -745,3 +868,3 @@ } | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return; | ||
@@ -751,6 +874,6 @@ throw e; | ||
const items = []; | ||
const items = new Array(list.length); | ||
for (let i = list.length - 1; i >= 0; i--) | ||
items.push(join(path, list[i])); | ||
for (let i = 0; i < list.length; i++) | ||
items[i] = join(path, list[list.length - 1 - i]); | ||
@@ -813,3 +936,3 @@ this.push(items); | ||
} catch (e) { | ||
if (!isNoEntry(e)) | ||
if (!isIgnorable(e)) | ||
throw e; | ||
@@ -829,3 +952,3 @@ } | ||
} catch (e) { | ||
if (isNoEntry(e)) | ||
if (isIgnorable(e)) | ||
return; | ||
@@ -941,3 +1064,3 @@ throw e; | ||
if (options == null) | ||
return { filter: null }; | ||
options = {}; | ||
@@ -953,3 +1076,3 @@ if (typeof options === 'function') | ||
let {filter} = options; | ||
let {filter, maxRetries, retryDelay} = options; | ||
@@ -959,6 +1082,18 @@ if (filter == null) | ||
if (maxRetries == null) | ||
maxRetries = 3; | ||
if (retryDelay == null) | ||
retryDelay = 100; | ||
if (filter != null && typeof filter !== 'function') | ||
throw new ArgError('filter', filter, 'function'); | ||
return { filter }; | ||
if ((maxRetries >>> 0) !== maxRetries) | ||
throw new ArgError('maxRetries', maxRetries, 'integer'); | ||
if ((retryDelay >>> 0) !== retryDelay) | ||
throw new ArgError('retryDelay', retryDelay, 'integer'); | ||
return { filter, maxRetries, retryDelay }; | ||
} | ||
@@ -1141,6 +1276,3 @@ | ||
function isNoEntry(err) { | ||
if (!err) | ||
return false; | ||
function isIgnorable(err) { | ||
return err.code === 'ENOENT' | ||
@@ -1152,2 +1284,38 @@ || err.code === 'EACCES' | ||
function wait(ms) { | ||
return new Promise(r => setTimeout(r, ms)); | ||
} | ||
async function safeStat(path) { | ||
try { | ||
return await fs.lstat(path); | ||
} catch (e) { | ||
if (e.code === 'EPERM' && process.platform === 'win32') { | ||
try { | ||
await fs.chmod(path, 0o666); | ||
} catch (e) { | ||
; | ||
} | ||
return fs.lstat(path); | ||
} | ||
throw e; | ||
} | ||
} | ||
function safeStatSync(path) { | ||
try { | ||
return fs.lstatSync(path); | ||
} catch (e) { | ||
if (e.code === 'EPERM' && process.platform === 'win32') { | ||
try { | ||
fs.chmodSync(path, 0o666); | ||
} catch (e) { | ||
; | ||
} | ||
return fs.lstatSync(path); | ||
} | ||
throw e; | ||
} | ||
} | ||
function shouldShow(options, dir) { | ||
@@ -1204,2 +1372,4 @@ return dir ? options.dirs : options.files; | ||
exports.copySync = copySync; | ||
exports.empty = empty; | ||
exports.emptySync = emptySync; | ||
exports.exists = exists; | ||
@@ -1206,0 +1376,0 @@ exports.existsSync = existsSync; |
@@ -65,18 +65,35 @@ /*! | ||
// For whenever promises are marked non-experimental. | ||
let HAS_STABLE_PROMISES = false; | ||
// Promises are considered stable as of 11.14.0. | ||
let HAS_STABLE_PROMISES = version >= 0x0b0e00; | ||
// The current highest modern version (11.1.0). | ||
// This _would_ be based on the value of HAS_WRITE_PENDING | ||
// instead of HAS_OPTIONAL_FLAGS, but we don't create | ||
// fallback for HAS_WRITE_PENDING right now. | ||
let HAS_ALL = HAS_OPTIONAL_FLAGS | ||
// Whether to actually use stable promises. | ||
let USE_STABLE_PROMISES = HAS_STABLE_PROMISES | ||
&& process.env.BFILE_USE_STABLE === '1'; | ||
// fs.writev{,Sync} was added in 12.9.0. | ||
let HAS_WRITEV = version >= 0x0c0900; | ||
let HAS_WRITEV_IMPL = typeof fs.writev === 'function'; | ||
// Stats objects have nanosecond precision as of 12.10.0. | ||
let HAS_STAT_NANO = version >= 0x0c0a00; | ||
// fs.rmdir{,Sync} got an options parameter to allow for recursion in 12.10.0. | ||
let HAS_RECURSIVE_RMDIR = version >= 0x0c0a00; | ||
// fs.opendir{,Sync} are present as of 12.12.0. | ||
let HAS_OPENDIR = version >= 0x0c0c00; | ||
let HAS_OPENDIR_IMPL = typeof fs.opendir === 'function'; | ||
// The current highest modern version (12.12.0). | ||
let HAS_ALL = HAS_OPENDIR | ||
&& HAS_COPY_FILE_IMPL | ||
&& HAS_REALPATH_NATIVE_IMPL | ||
&& HAS_PROMISES_IMPL | ||
&& HAS_DIRENT_IMPL; | ||
&& HAS_DIRENT_IMPL | ||
&& HAS_WRITEV_IMPL | ||
&& HAS_OPENDIR_IMPL; | ||
// Force stable promises with an env variable. | ||
if (process.env.BFILE_FORCE_STABLE === '1') | ||
HAS_STABLE_PROMISES = true; | ||
if (process.env.BFILE_FORCE_STABLE === '1' && HAS_PROMISES_IMPL) | ||
USE_STABLE_PROMISES = true; | ||
@@ -103,2 +120,9 @@ // Force compat mode with an env variable. | ||
HAS_STABLE_PROMISES = false; | ||
USE_STABLE_PROMISES = false; | ||
HAS_WRITEV = false; | ||
HAS_WRITEV_IMPL = false; | ||
HAS_STAT_NANO = false; | ||
HAS_RECURSIVE_RMDIR = false; | ||
HAS_OPENDIR = false; | ||
HAS_OPENDIR_IMPL = false; | ||
HAS_ALL = false; | ||
@@ -130,2 +154,9 @@ } | ||
exports.HAS_STABLE_PROMISES = HAS_STABLE_PROMISES; | ||
exports.USE_STABLE_PROMISES = USE_STABLE_PROMISES; | ||
exports.HAS_WRITEV = HAS_WRITEV; | ||
exports.HAS_WRITEV_IMPL = HAS_WRITEV_IMPL; | ||
exports.HAS_STAT_NANO = HAS_STAT_NANO; | ||
exports.HAS_RECURSIVE_RMDIR = HAS_RECURSIVE_RMDIR; | ||
exports.HAS_OPENDIR = HAS_OPENDIR; | ||
exports.HAS_OPENDIR_IMPL = HAS_OPENDIR_IMPL; | ||
exports.HAS_ALL = HAS_ALL; |
@@ -215,2 +215,4 @@ /*! | ||
exports.openSync = enoentSync('open'); | ||
exports.opendir = enoent('opendir'); | ||
exports.opendirSync = enoentSync('opendir'); | ||
exports.read = ebadf('read'); | ||
@@ -249,2 +251,4 @@ exports.readSync = ebadfSync('read'); | ||
exports.writeFileSync = noopSync; | ||
exports.writev = ebadf('writev'); | ||
exports.writevSync = ebadfSync('writev'); | ||
@@ -256,2 +260,3 @@ exports.F_OK = exports.constants.F_OK || 0; | ||
exports.Dir = class Dir {}; | ||
exports.Dirent = class Dirent {}; | ||
@@ -268,2 +273,4 @@ exports.Stats = class Stats {}; | ||
exports.copySync = noopSync; | ||
exports.empty = noop; | ||
exports.emptySync = noopSync; | ||
exports.exists = async () => false; | ||
@@ -320,2 +327,9 @@ exports.existsSync = () => false; | ||
HAS_STABLE_PROMISES: false, | ||
USE_STABLE_PROMISES: false, | ||
HAS_WRITEV: false, | ||
HAS_WRITEV_IMPL: false, | ||
HAS_STAT_NANO: false, | ||
HAS_RECURSIVE_RMDIR: false, | ||
HAS_OPENDIR: false, | ||
HAS_OPENDIR_IMPL: false, | ||
HAS_ALL: false | ||
@@ -322,0 +336,0 @@ }; |
@@ -19,2 +19,4 @@ /*! | ||
fs.copySync = extra.copySync; | ||
fs.empty = extra.empty; | ||
fs.emptySync = extra.emptySync; | ||
fs.exists = extra.exists; | ||
@@ -53,3 +55,3 @@ fs.existsSync = extra.existsSync; | ||
if (features.HAS_STABLE_PROMISES) { | ||
if (features.USE_STABLE_PROMISES) { | ||
const native = fs.realpath.native; | ||
@@ -68,2 +70,3 @@ | ||
fs.mkdtemp = fs.promises.mkdtemp; | ||
fs.opendir = fs.promises.opendir; | ||
fs.handle = fs.promises.open; | ||
@@ -70,0 +73,0 @@ fs.readdir = fs.promises.readdir; |
@@ -32,3 +32,4 @@ /*! | ||
if (!features.HAS_STAT_NUMBERS | ||
|| !features.HAS_STAT_BIGINTS) { | ||
|| !features.HAS_STAT_BIGINTS | ||
|| !features.HAS_STAT_NANO) { | ||
fs.fstat = compat.fstat; | ||
@@ -86,9 +87,42 @@ fs.fstatSync = compat.fstatSync; | ||
if (!features.HAS_OPTIONAL_FLAGS) | ||
if (!features.HAS_OPTIONAL_FLAGS) { | ||
fs.open = compat.open; | ||
fs.openSync = compat.openSync; | ||
} | ||
if (!features.HAS_WRITEV_IMPL) { | ||
fs.writev = compat.writev; | ||
fs.writevSync = compat.writevSync; | ||
} | ||
if (!features.HAS_RECURSIVE_RMDIR) { | ||
fs.rmdir = compat.rmdir; | ||
fs.rmdirSync = compat.rmdirSync; | ||
} | ||
if (!features.HAS_OPENDIR_IMPL) { | ||
fs.opendir = compat.opendir; | ||
fs.opendirSync = compat.opendirSync; | ||
fs.Dir = compat.Dir; | ||
} | ||
// A few things still need patching even if we have native promises. | ||
if (features.HAS_PROMISES_IMPL && !features.HAS_OPTIONAL_FLAGS) { | ||
if (features.HAS_PROMISES_IMPL && !features.HAS_OPENDIR_IMPL) { | ||
const getter = Object.getOwnPropertyDescriptor(fs, 'promises').get; | ||
const getPromises = () => { | ||
if (features.HAS_STABLE_PROMISES) | ||
return getter(); | ||
const emit = process.emitWarning; | ||
process.emitWarning = () => {}; | ||
try { | ||
return getter(); | ||
} finally { | ||
process.emitWarning = emit; | ||
} | ||
}; | ||
let promises = null; | ||
@@ -103,4 +137,10 @@ | ||
promises = compat.clonePromises(getter()); | ||
promises = compat.clonePromises(getPromises()); | ||
if (!features.HAS_STAT_BIGINTS | ||
|| !features.HAS_STAT_NANO) { | ||
promises.stat = compat.promises.stat; | ||
compat.patchStat(promises); | ||
} | ||
if (!features.HAS_DIRENT_IMPL) | ||
@@ -120,2 +160,11 @@ promises.readdir = compat.promises.readdir; | ||
if (!features.HAS_WRITEV_IMPL) | ||
compat.patchWritev(promises); | ||
if (!features.HAS_RECURSIVE_RMDIR) | ||
promises.rmdir = compat.promises.rmdir; | ||
if (!features.HAS_OPENDIR_IMPL) | ||
promises.opendir = compat.promises.opendir; | ||
return promises; | ||
@@ -122,0 +171,0 @@ } |
@@ -15,8 +15,2 @@ /*! | ||
/* | ||
* Constants | ||
*/ | ||
let promises = null; | ||
/* | ||
* Expose | ||
@@ -70,2 +64,4 @@ */ | ||
exports.openSync = fs.openSync; | ||
exports.opendir = promisify(fs.opendir); | ||
exports.opendirSync = fs.opendirSync; | ||
exports.read = null; | ||
@@ -103,2 +99,4 @@ exports.readSync = fs.readSync; | ||
exports.writeFileSync = fs.writeFileSync; | ||
exports.writev = promisify(fs.writev); | ||
exports.writevSync = fs.writevSync; | ||
@@ -209,17 +207,5 @@ exports.exists = function exists(file) { | ||
get() { | ||
if (!promises) { | ||
const emit = process.emitWarning; | ||
process.emitWarning = () => {}; | ||
try { | ||
promises = fs.promises; | ||
} finally { | ||
process.emitWarning = emit; | ||
} | ||
} | ||
return promises; | ||
return fs.promises; | ||
} | ||
} | ||
}); |
{ | ||
"name": "bfile", | ||
"version": "0.2.1", | ||
"version": "0.2.2", | ||
"description": "Filesystem wrapper for node.js", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -40,2 +40,4 @@ # bfile | ||
- `fs.copySync(src, dest, [filter(path, stat)])` - Synchronous `fs.copy`. | ||
- `fs.empty(path, [mode])` (async) - Ensure an empty directory at `path`. | ||
- `fs.emptySync(path, [mode])` - Synchronous `fs.empty`. | ||
- `fs.exists(path, [mode])` (async) - A fixed version of `fs.exists`. Basically | ||
@@ -114,2 +116,5 @@ a wrapper around `fs.access` which returns false on `ENOENT` or `EACCESS`. | ||
files are removed (default: `null`). | ||
- `maxRetries` (number) - Number of retries for `EBUSY`, `EMFILE`, `ENFILE`, | ||
`ENOTEMPTY`, or `EPERM` (default: `3`). | ||
- `retryDelay` (number) - Number of milliseconds to wait in between retries. | ||
@@ -177,4 +182,2 @@ Options may also be a function as an alias for the `filter` option. | ||
### Options | ||
## Contribution and License Agreement | ||
@@ -181,0 +184,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
95959
3239
195
7