/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
"use strict";
/* eslint-disable no-bitwise */
// $FlowFixMe: not defined by Flow
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
const constants = require("constants");
const stream = require("stream");
const _require = require("events"),
EventEmitter = _require.EventEmitter;
const FLAGS_SPECS = {
r: {
mustExist: true,
readable: true
},
"r+": {
mustExist: true,
readable: true,
writable: true
},
"rs+": {
mustExist: true,
readable: true,
writable: true
},
w: {
truncate: true,
writable: true
},
wx: {
exclusive: true,
truncate: true,
writable: true
},
"w+": {
readable: true,
truncate: true,
writable: true
},
"wx+": {
exclusive: true,
readable: true,
truncate: true,
writable: true
}
};
const ASYNC_FUNC_NAMES = [
"access",
"close",
"copyFile",
"fstat",
"fsync",
"fdatasync",
"lstat",
"open",
"read",
"readdir",
"readFile",
"readlink",
"realpath",
"stat",
"unlink",
"write",
"writeFile"
];
/**
* Simulates `fs` API in an isolated, memory-based filesystem. This is useful
* for testing systems that rely on `fs` without affecting the real filesystem.
* This is meant to be a drop-in replacement/mock for `fs`, so it mimics
* closely the behavior of file path resolution and file accesses.
*/
class MemoryFs {
constructor(_options) {
var _this = this;
_defineProperty(this, "constants", constants);
_defineProperty(this, "accessSync", (filePath, mode) => {
if (mode == null) {
mode = constants.F_OK;
}
const stats = this.statSync(filePath);
if (mode == constants.F_OK) {
return;
}
const filePathStr = pathStr(filePath);
if ((mode & constants.R_OK) !== 0) {
if (
!(
(stats.mode & constants.S_IROTH) !== 0 ||
((stats.mode & constants.S_IRGRP) !== 0 &&
stats.gid === getgid()) ||
((stats.mode & constants.S_IRUSR) !== 0 && stats.uid === getuid())
)
) {
throw makeError("EPERM", filePathStr, "file cannot be read");
}
}
if ((mode & constants.W_OK) !== 0) {
if (
!(
(stats.mode & constants.S_IWOTH) !== 0 ||
((stats.mode & constants.S_IWGRP) !== 0 &&
stats.gid === getgid()) ||
((stats.mode & constants.S_IWUSR) !== 0 && stats.uid === getuid())
)
) {
throw makeError("EPERM", filePathStr, "file cannot be written to");
}
}
if ((mode & constants.X_OK) !== 0) {
if (
!(
(stats.mode & constants.S_IXOTH) !== 0 ||
((stats.mode & constants.S_IXGRP) !== 0 &&
stats.gid === getgid()) ||
((stats.mode & constants.S_IXUSR) !== 0 && stats.uid === getuid())
)
) {
throw makeError("EPERM", filePathStr, "file cannot be executed");
}
}
});
_defineProperty(this, "closeSync", fd => {
const desc = this._getDesc(fd);
if (desc.writable) {
this._emitFileChange(desc.nodePath.slice(), {
eventType: "change"
});
}
this._fds.delete(fd);
});
_defineProperty(this, "copyFileSync", function(src, dest) {
let flags =
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
const options =
flags & constants.COPYFILE_EXCL
? {
flag: "wx"
}
: {};
_this.writeFileSync(dest, _this.readFileSync(src), options);
});
_defineProperty(this, "fsyncSync", fd => {
this._getDesc(fd);
});
_defineProperty(this, "fdatasyncSync", fd => {
this._getDesc(fd);
});
_defineProperty(this, "openSync", (filePath, flags, mode) => {
if (typeof flags === "number") {
throw new Error(`numeric flags not supported: ${flags}`);
}
return this._open(pathStr(filePath), flags, mode);
});
_defineProperty(
this,
"readSync",
(fd, buffer, offset, length, position) => {
const desc = this._getDesc(fd);
if (!desc.readable) {
throw makeError(
"EBADF",
null,
"file descriptor cannot be written to"
);
}
if (position != null) {
desc.position = position;
}
const endPos = Math.min(
desc.position + length,
desc.node.content.length
);
desc.node.content.copy(buffer, offset, desc.position, endPos);
const bytesRead = endPos - desc.position;
desc.position = endPos;
return bytesRead;
}
);
_defineProperty(this, "readdirSync", (filePath, options) => {
let encoding;
if (typeof options === "string") {
encoding = options;
} else if (options != null) {
encoding = options.encoding;
}
filePath = pathStr(filePath);
const _this$_resolve = this._resolve(filePath),
node = _this$_resolve.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
if (node.type !== "directory") {
throw makeError("ENOTDIR", filePath, "not a directory");
}
return Array.from(node.entries.keys()).map(str => {
if (encoding === "utf8") {
return str;
}
const buffer = Buffer.from(str);
if (encoding === "buffer") {
return buffer;
}
return buffer.toString(encoding);
});
});
_defineProperty(this, "readFileSync", (filePath, options) => {
let encoding, flag;
if (typeof options === "string") {
encoding = options;
} else if (options != null) {
encoding = options.encoding;
flag = options.flag;
}
const fd = this._open(pathStr(filePath), flag || "r");
const chunks = [];
try {
const buffer = Buffer.alloc(1024);
let bytesRead;
do {
bytesRead = this.readSync(fd, buffer, 0, buffer.length, null);
if (bytesRead === 0) {
continue;
}
const chunk = Buffer.alloc(bytesRead);
buffer.copy(chunk, 0, 0, bytesRead);
chunks.push(chunk);
} while (bytesRead > 0);
} finally {
this.closeSync(fd);
}
const result = Buffer.concat(chunks);
if (encoding == null) {
return result;
}
return result.toString(encoding);
});
_defineProperty(this, "readlinkSync", (filePath, options) => {
let encoding;
if (typeof options === "string") {
encoding = options;
} else if (options != null) {
encoding = options.encoding;
}
filePath = pathStr(filePath);
const _this$_resolve2 = this._resolve(filePath, {
keepFinalSymlink: true
}),
node = _this$_resolve2.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
if (node.type !== "symbolicLink") {
throw makeError("EINVAL", filePath, "entity is not a symlink");
}
if (encoding == null || encoding === "utf8") {
return node.target;
}
const buf = Buffer.from(node.target);
if (encoding == "buffer") {
return buf;
}
return buf.toString(encoding);
});
_defineProperty(this, "realpathSync", filePath => {
return this._resolve(pathStr(filePath)).realpath;
});
_defineProperty(
this,
"writeSync",
(fd, bufferOrString, offsetOrPosition, lengthOrEncoding, position) => {
let encoding, offset, length, buffer;
if (typeof bufferOrString === "string") {
position = offsetOrPosition;
encoding = lengthOrEncoding;
buffer = Buffer.from(bufferOrString, encoding || "utf8");
} else {
offset = offsetOrPosition;
if (
lengthOrEncoding != null &&
typeof lengthOrEncoding !== "number"
) {
throw new Error("invalid length");
}
length = lengthOrEncoding;
buffer = bufferOrString;
}
if (offset == null) {
offset = 0;
}
if (length == null) {
length = buffer.length;
}
return this._write(fd, buffer, offset, length, position);
}
);
_defineProperty(this, "writeFileSync", (filePathOrFd, data, options) => {
let encoding, mode, flag;
if (typeof options === "string") {
encoding = options;
} else if (options != null) {
encoding = options.encoding;
mode = options.mode;
flag = options.flag;
}
if (encoding == null) {
encoding = "utf8";
}
if (typeof data === "string") {
data = Buffer.from(data, encoding);
}
const fd =
typeof filePathOrFd === "number"
? filePathOrFd
: this._open(pathStr(filePathOrFd), flag || "w", mode);
try {
this._write(fd, data, 0, data.length);
} finally {
if (typeof filePathOrFd !== "number") {
this.closeSync(fd);
}
}
});
_defineProperty(this, "mkdirSync", (dirPath, mode) => {
if (mode == null) {
mode = 0o777;
}
dirPath = pathStr(dirPath);
const _this$_resolve3 = this._resolve(dirPath),
dirNode = _this$_resolve3.dirNode,
node = _this$_resolve3.node,
basename = _this$_resolve3.basename;
if (node != null) {
throw makeError("EEXIST", dirPath, "directory or file already exists");
}
dirNode.entries.set(basename, this._makeDir(mode));
});
_defineProperty(this, "symlinkSync", (target, filePath, type) => {
if (type == null) {
type = "file";
}
if (type !== "file") {
throw new Error("symlink type not supported");
}
filePath = pathStr(filePath);
const _this$_resolve4 = this._resolve(filePath),
dirNode = _this$_resolve4.dirNode,
node = _this$_resolve4.node,
basename = _this$_resolve4.basename;
if (node != null) {
throw makeError("EEXIST", filePath, "directory or file already exists");
}
dirNode.entries.set(basename, {
id: this._getId(),
gid: getgid(),
target: pathStr(target),
mode: 0o666,
uid: getuid(),
type: "symbolicLink",
watchers: []
});
});
_defineProperty(this, "existsSync", filePath => {
try {
const _this$_resolve5 = this._resolve(pathStr(filePath)),
node = _this$_resolve5.node;
return node != null;
} catch (error) {
if (error.code === "ENOENT") {
return false;
}
throw error;
}
});
_defineProperty(this, "statSync", filePath => {
filePath = pathStr(filePath);
const _this$_resolve6 = this._resolve(filePath),
node = _this$_resolve6.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
return new Stats(node);
});
_defineProperty(this, "lstatSync", filePath => {
filePath = pathStr(filePath);
const _this$_resolve7 = this._resolve(filePath, {
keepFinalSymlink: true
}),
node = _this$_resolve7.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
return new Stats(node);
});
_defineProperty(this, "fstatSync", fd => {
const desc = this._getDesc(fd);
return new Stats(desc.node);
});
_defineProperty(this, "createReadStream", (filePath, options) => {
let autoClose, encoding, fd, flags, mode, start, end, highWaterMark;
if (typeof options === "string") {
encoding = options;
} else if (options != null) {
autoClose = options.autoClose;
encoding = options.encoding;
fd = options.fd;
flags = options.flags;
mode = options.mode;
start = options.start;
end = options.end;
highWaterMark = options.highWaterMark;
}
let st = null;
if (fd == null) {
fd = this._open(pathStr(filePath), flags || "r", mode);
process.nextTick(() => st.emit("open", fd));
}
const ffd = fd;
const readSync = this.readSync;
const ropt = {
filePath,
encoding,
fd,
highWaterMark,
start,
end,
readSync
};
const rst = new ReadFileSteam(ropt);
st = rst;
if (autoClose !== false) {
const doClose = () => {
this.closeSync(ffd);
rst.emit("close");
};
rst.on("end", doClose);
rst.on("error", doClose);
}
return rst;
});
_defineProperty(this, "unlinkSync", filePath => {
filePath = pathStr(filePath);
const _this$_resolve8 = this._resolve(filePath, {
keepFinalSymlink: true
}),
basename = _this$_resolve8.basename,
dirNode = _this$_resolve8.dirNode,
dirPath = _this$_resolve8.dirPath,
node = _this$_resolve8.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
if (node.type !== "file" && node.type !== "symbolicLink") {
throw makeError("EISDIR", filePath, "cannot unlink a directory");
}
dirNode.entries.delete(basename);
this._emitFileChange(dirPath.concat([[basename, node]]), {
eventType: "rename"
});
});
_defineProperty(this, "createWriteStream", (filePath, options) => {
let autoClose, fd, flags, mode, start;
if (typeof options !== "string" && options != null) {
autoClose = options.autoClose;
fd = options.fd;
flags = options.flags;
mode = options.mode;
start = options.start;
}
let st = null;
if (fd == null) {
fd = this._open(pathStr(filePath), flags || "w", mode);
process.nextTick(() => st.emit("open", fd));
}
const ffd = fd;
const ropt = {
fd,
writeSync: this._write.bind(this),
filePath,
start
};
const rst = new WriteFileStream(ropt);
st = rst;
if (autoClose !== false) {
const doClose = () => {
this.closeSync(ffd);
rst.emit("close");
};
rst.on("finish", doClose);
rst.on("error", doClose);
}
return st;
});
_defineProperty(this, "watch", (filePath, options, listener) => {
filePath = pathStr(filePath);
const _this$_resolve9 = this._resolve(filePath),
node = _this$_resolve9.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
let encoding, recursive, persistent;
if (typeof options === "string") {
encoding = options;
} else if (options != null) {
encoding = options.encoding;
recursive = options.recursive;
persistent = options.persistent;
}
const watcher = new FSWatcher(node, {
encoding: encoding != null ? encoding : "utf8",
recursive: recursive != null ? recursive : false,
persistent: persistent != null ? persistent : false
});
if (listener != null) {
watcher.on("change", listener);
}
return watcher;
});
this._platform = (_options && _options.platform) || "posix";
this._cwd = _options && _options.cwd;
this._pathSep = this._platform === "win32" ? "\\" : "/";
this.reset();
ASYNC_FUNC_NAMES.forEach(funcName => {
const func = this[`${funcName}Sync`];
this[funcName] = function() {
for (
var _len = arguments.length, args = new Array(_len), _key = 0;
_key < _len;
_key++
) {
args[_key] = arguments[_key];
}
const callback = args.pop();
process.nextTick(() => {
let retval;
try {
retval = func.apply(null, args);
} catch (error) {
callback(error);
return;
}
callback(null, retval);
});
};
});
}
reset() {
this._nextId = 1;
this._roots = new Map();
if (this._platform === "posix") {
this._roots.set("", this._makeDir(0o777));
} else if (this._platform === "win32") {
this._roots.set("C:", this._makeDir(0o777));
}
this._fds = new Map();
}
_makeDir(mode) {
return {
entries: new Map(),
gid: getgid(),
id: this._getId(),
mode,
uid: getuid(),
type: "directory",
watchers: []
};
}
_getId() {
return ++this._nextId;
}
_open(filePath, flags, mode) {
if (mode == null) {
mode = 0o666;
}
const spec = FLAGS_SPECS[flags];
if (spec == null) {
throw new Error(`flags not supported: \`${flags}\``);
}
const _spec$writable = spec.writable,
writable = _spec$writable === void 0 ? false : _spec$writable,
_spec$readable = spec.readable,
readable = _spec$readable === void 0 ? false : _spec$readable;
const exclusive = spec.exclusive,
mustExist = spec.mustExist,
truncate = spec.truncate;
let _this$_resolve10 = this._resolve(filePath),
dirNode = _this$_resolve10.dirNode,
node = _this$_resolve10.node,
basename = _this$_resolve10.basename,
dirPath = _this$_resolve10.dirPath;
let nodePath;
if (node == null) {
if (mustExist) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
node = {
content: Buffer.alloc(0),
gid: getgid(),
id: this._getId(),
mode,
uid: getuid(),
type: "file",
watchers: []
};
dirNode.entries.set(basename, node);
nodePath = dirPath.concat([[basename, node]]);
this._emitFileChange(nodePath.slice(), {
eventType: "rename"
});
} else {
if (exclusive) {
throw makeError("EEXIST", filePath, "directory or file already exists");
}
if (node.type !== "file") {
throw makeError("EISDIR", filePath, "cannot read/write to a directory");
}
if (truncate) {
node.content = Buffer.alloc(0);
}
nodePath = dirPath.concat([[basename, node]]);
}
return this._getFd(filePath, {
nodePath,
node,
position: 0,
readable,
writable
});
}
_parsePath(filePath) {
let drive;
const sep = this._platform === "win32" ? /[\\/]/ : /\//;
if (this._platform === "win32" && filePath.match(/^[a-zA-Z]:[\\/]/)) {
drive = filePath.substring(0, 2);
filePath = filePath.substring(3);
}
if (sep.test(filePath[0])) {
if (this._platform === "posix") {
drive = "";
filePath = filePath.substring(1);
} else {
throw makeError(
"EINVAL",
filePath,
"path is invalid because it cannot start with a separator"
);
}
}
return {
entNames: filePath.split(sep),
drive
};
}
/**
* Implemented according with
* http://man7.org/linux/man-pages/man7/path_resolution.7.html
*/
_resolve(filePath, options) {
let keepFinalSymlink = false;
if (options != null) {
keepFinalSymlink = options.keepFinalSymlink;
}
if (filePath === "") {
throw makeError("ENOENT", filePath, "no such file or directory");
}
let _this$_parsePath = this._parsePath(filePath),
drive = _this$_parsePath.drive,
entNames = _this$_parsePath.entNames;
if (drive == null) {
const _cwd = this._cwd;
if (_cwd == null) {
throw new Error(
`The path \`${filePath}\` cannot be resolved because no ` +
"current working directory function has been specified. Set the " +
"`cwd` option field to specify a current working directory."
);
}
const cwPath = this._parsePath(_cwd());
drive = cwPath.drive;
if (drive == null) {
throw new Error(
"On a win32 FS, the options' `cwd()` must return a valid win32 " +
"absolute path. This happened while trying to " +
`resolve: \`${filePath}\``
);
}
entNames = cwPath.entNames.concat(entNames);
}
checkPathLength(entNames, filePath);
const root = this._getRoot(drive, filePath);
const context = {
drive,
node: root,
nodePath: [["", root]],
entNames,
symlinkCount: 0,
keepFinalSymlink
};
while (context.entNames.length > 0) {
const entName = context.entNames.shift();
this._resolveEnt(context, filePath, entName);
}
const nodePath = context.nodePath;
return {
drive: context.drive,
realpath: context.drive + nodePath.map(x => x[0]).join(this._pathSep),
dirNode: (() => {
const dirNode =
nodePath.length >= 2
? nodePath[nodePath.length - 2][1]
: context.node;
if (dirNode == null || dirNode.type !== "directory") {
throw new Error("failed to resolve");
}
return dirNode;
})(),
node: context.node,
basename: nullthrows(nodePath[nodePath.length - 1][0]),
dirPath: nodePath
.slice(0, -1)
.map(nodePair => [nodePair[0], nullthrows(nodePair[1])])
};
}
_resolveEnt(context, filePath, entName) {
const node = context.node;
if (node == null) {
throw makeError("ENOENT", filePath, "no such file or directory");
}
if (node.type !== "directory") {
throw makeError("ENOTDIR", filePath, "not a directory");
}
const entries = node.entries;
if (entName === "" || entName === ".") {
return;
}
if (entName === "..") {
const nodePath = context.nodePath;
if (nodePath.length > 1) {
nodePath.pop();
context.node = nodePath[nodePath.length - 1][1];
}
return;
}
const childNode = entries.get(entName);
if (
childNode == null ||
childNode.type !== "symbolicLink" ||
(context.keepFinalSymlink && context.entNames.length === 0)
) {
context.node = childNode;
context.nodePath.push([entName, childNode]);
return;
}
if (context.symlinkCount >= 10) {
throw makeError("ELOOP", filePath, "too many levels of symbolic links");
}
const _this$_parsePath2 = this._parsePath(childNode.target),
entNames = _this$_parsePath2.entNames,
drive = _this$_parsePath2.drive;
if (drive != null) {
context.drive = drive;
context.node = this._getRoot(drive, filePath);
context.nodePath = [["", context.node]];
}
context.entNames = entNames.concat(context.entNames);
checkPathLength(context.entNames, filePath);
++context.symlinkCount;
}
_getRoot(drive, filePath) {
const root = this._roots.get(drive.toUpperCase());
if (root == null) {
throw makeError("ENOENT", filePath, `no such drive: \`${drive}\``);
}
return root;
}
_write(fd, buffer, offset, length, position) {
const desc = this._getDesc(fd);
if (!desc.writable) {
throw makeError("EBADF", null, "file descriptor cannot be written to");
}
if (position == null) {
position = desc.position;
}
const node = desc.node;
if (node.content.length < position + length) {
const newBuffer = Buffer.alloc(position + length);
node.content.copy(newBuffer, 0, 0, node.content.length);
node.content = newBuffer;
}
buffer.copy(node.content, position, offset, offset + length);
desc.position = position + length;
return buffer.length;
}
_getFd(filePath, desc) {
let fd = 3;
while (this._fds.has(fd)) {
++fd;
}
if (fd >= 256) {
throw makeError("EMFILE", filePath, "too many open files");
}
this._fds.set(fd, desc);
return fd;
}
_getDesc(fd) {
const desc = this._fds.get(fd);
if (desc == null) {
throw makeError("EBADF", null, "file descriptor is not open");
}
return desc;
}
_emitFileChange(nodePath, options) {
const fileNode = nodePath.pop();
let filePath = fileNode[0];
let recursive = false;
for (const watcher of fileNode[1].watchers) {
watcher.listener(options.eventType, filePath);
}
while (nodePath.length > 0) {
const dirNode = nodePath.pop();
for (const watcher of dirNode[1].watchers) {
if (recursive && !watcher.recursive) {
continue;
}
watcher.listener(options.eventType, filePath);
}
filePath = dirNode[0] + this._pathSep + filePath;
recursive = true;
}
}
}
class Stats {
/**
* Don't keep a reference to the node as it may get mutated over time.
*/
constructor(node) {
this._type = node.type;
this.dev = 1;
this.mode = node.mode;
this.nlink = 1;
this.uid = node.uid;
this.gid = node.gid;
this.rdev = 0;
this.blksize = 1024;
this.ino = node.id;
this.size =
node.type === "file"
? node.content.length
: node.type === "symbolicLink"
? node.target.length
: 0;
this.blocks = Math.ceil(this.size / 512);
this.atimeMs = 1;
this.mtimeMs = 1;
this.ctimeMs = 1;
this.birthtimeMs = 1;
this.atime = new Date(this.atimeMs);
this.mtime = new Date(this.mtimeMs);
this.ctime = new Date(this.ctimeMs);
this.birthtime = new Date(this.birthtimeMs);
}
isFile() {
return this._type === "file";
}
isDirectory() {
return this._type === "directory";
}
isBlockDevice() {
return false;
}
isCharacterDevice() {
return false;
}
isSymbolicLink() {
return this._type === "symbolicLink";
}
isFIFO() {
return false;
}
isSocket() {
return false;
}
}
class ReadFileSteam extends stream.Readable {
constructor(options) {
const highWaterMark = options.highWaterMark,
fd = options.fd; // eslint-disable-next-line lint/flow-no-fixme
// $FlowFixMe: Readable does accept null of undefined for that value.
super({
highWaterMark
});
this.bytesRead = 0;
this.path = options.filePath;
this._readSync = options.readSync;
this._fd = fd;
this._buffer = Buffer.alloc(1024);
const start = options.start,
end = options.end;
if (start != null) {
this._readSync(fd, Buffer.alloc(0), 0, 0, start);
}
if (end != null) {
this._positions = {
current: start || 0,
last: end + 1
};
}
}
_read(size) {
let bytesRead;
const _buffer = this._buffer;
do {
const length = this._getLengthToRead();
const position = this._positions && this._positions.current;
bytesRead = this._readSync(this._fd, _buffer, 0, length, position);
if (this._positions != null) {
this._positions.current += bytesRead;
}
this.bytesRead += bytesRead;
} while (this.push(bytesRead > 0 ? _buffer.slice(0, bytesRead) : null));
}
_getLengthToRead() {
const _positions = this._positions,
_buffer = this._buffer;
if (_positions == null) {
return _buffer.length;
}
const leftToRead = Math.max(0, _positions.last - _positions.current);
return Math.min(_buffer.length, leftToRead);
}
}
class WriteFileStream extends stream.Writable {
constructor(opts) {
super();
this.path = opts.filePath;
this.bytesWritten = 0;
this._fd = opts.fd;
this._writeSync = opts.writeSync;
if (opts.start != null) {
this._writeSync(opts.fd, Buffer.alloc(0), 0, 0, opts.start);
}
}
_write(buffer, encoding, callback) {
try {
const bytesWritten = this._writeSync(this._fd, buffer, 0, buffer.length);
this.bytesWritten += bytesWritten;
} catch (error) {
callback(error);
return;
}
callback();
}
}
class FSWatcher extends EventEmitter {
constructor(node, options) {
super();
_defineProperty(this, "_listener", (eventType, filePath) => {
const encFilePath =
this._encoding === "buffer" ? Buffer.from(filePath, "utf8") : filePath;
try {
this.emit("change", eventType, encFilePath);
} catch (error) {
this.close();
this.emit("error", error);
}
});
this._encoding = options.encoding;
this._nodeWatcher = {
recursive: options.recursive,
listener: this._listener
};
node.watchers.push(this._nodeWatcher);
this._node = node;
if (options.persistent) {
this._persistIntervalId = setInterval(() => {}, 60000);
}
}
close() {
this._node.watchers.splice(this._node.watchers.indexOf(this._nodeWatcher));
clearInterval(this._persistIntervalId);
}
}
function checkPathLength(entNames, filePath) {
if (entNames.length > 32) {
throw makeError(
"ENAMETOOLONG",
filePath,
"file path too long (or one of the intermediate " +
"symbolic link resolutions)"
);
}
}
function pathStr(filePath) {
if (typeof filePath === "string") {
return filePath;
}
return filePath.toString("utf8");
}
function makeError(code, filePath, message) {
const err = new Error(
filePath != null
? `${code}: \`${filePath}\`: ${message}`
: `${code}: ${message}`
);
err.code = code;
err.errno = constants[code];
err.path = filePath;
return err;
}
function nullthrows(x) {
if (x == null) {
throw new Error("item was null or undefined");
}
return x;
}
function getgid() {
return process.getgid != null ? process.getgid() : -1;
}
function getuid() {
return process.getuid != null ? process.getuid() : -1;
}
module.exports = MemoryFs;