nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
3,403 lines • 87.8 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/fs.js
import * as __hoisted_internal_fs_read_context__ from "nstdlib/lib/internal/fs/read/context";
import * as __hoisted_internal_fs_cp_cp__ from "nstdlib/lib/internal/fs/cp/cp";
import * as __hoisted_util__ from "nstdlib/lib/util";
import * as __hoisted_internal_fs_cp_cp_sync__ from "nstdlib/lib/internal/fs/cp/cp-sync";
import * as __hoisted_internal_fs_rimraf__ from "nstdlib/lib/internal/fs/rimraf";
import * as __hoisted_internal_fs_watchers__ from "nstdlib/lib/internal/fs/watchers";
import * as __hoisted_internal_fs_recursive_watch__ from "nstdlib/lib/internal/fs/recursive_watch";
import * as __hoisted_internal_event_target__ from "nstdlib/lib/internal/event_target";
import * as __hoisted_internal_fs_streams__ from "nstdlib/lib/internal/fs/streams";
import * as __hoisted_internal_fs_glob__ from "nstdlib/lib/internal/fs/glob";
import * as __hoisted_internal_fs_promises__ from "nstdlib/lib/internal/fs/promises";
import { fs as constants } from "nstdlib/stub/binding/constants";
import * as pathModule from "nstdlib/lib/path";
import { isArrayBufferView } from "nstdlib/lib/internal/util/types";
import * as binding from "nstdlib/stub/binding/fs";
import { createBlobFromFilePath } from "nstdlib/lib/internal/blob";
import { Buffer } from "nstdlib/lib/buffer";
import {
AbortError,
aggregateTwoErrors,
codes as __codes__,
} from "nstdlib/lib/internal/errors";
import { toPathIfFileURL } from "nstdlib/lib/internal/url";
import {
customPromisifyArgs as kCustomPromisifyArgsSymbol,
emitExperimentalWarning,
getLazy,
kEmptyObject,
promisify as __promisify__,
SideEffectFreeRegExpPrototypeExec,
defineLazyProperties,
isWindows,
isMacOS,
} from "nstdlib/lib/internal/util";
import {
constants as __constants__,
copyObject,
Dirent,
emitRecursiveRmdirWarning,
getDirent,
getDirents,
getOptions,
getValidatedFd,
getValidatedPath,
handleErrorFromBinding,
preprocessSymlinkDestination,
Stats,
getStatFsFromBinding,
getStatsFromBinding,
realpathCacheKey,
stringToFlags,
stringToSymlinkType,
toUnixTimestamp,
validateBufferArray,
validateCpOptions,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePath,
validatePosition,
validateRmOptions,
validateRmOptionsSync,
validateRmdirOptions,
validateStringAfterArrayBufferView,
warnOnNonPortableTemplate,
} from "nstdlib/lib/internal/fs/utils";
import {
CHAR_FORWARD_SLASH,
CHAR_BACKWARD_SLASH,
} from "nstdlib/lib/internal/constants";
import {
isInt32,
parseFileMode,
validateBoolean,
validateBuffer,
validateEncoding,
validateFunction,
validateInteger,
validateObject,
validateString,
kValidateObjectAllowNullable,
} from "nstdlib/lib/internal/validators";
import * as permission from "nstdlib/lib/internal/process/permission";
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// Maintainers, keep in mind that ES1-style octal literals (`0666`) are not
// allowed in strict mode. Use ES6-style octal literals instead (`0o666`).
const {
S_IFIFO,
S_IFLNK,
S_IFMT,
S_IFREG,
S_IFSOCK,
F_OK,
R_OK,
W_OK,
X_OK,
O_WRONLY,
O_SYMLINK,
} = constants;
const { isAbsolute } = pathModule;
const { isBuffer: BufferIsBuffer } = Buffer;
const BufferToString = uncurryThis(Buffer.prototype.toString);
const { ERR_ACCESS_DENIED, ERR_FS_FILE_TOO_LARGE, ERR_INVALID_ARG_VALUE } =
__codes__;
const { FSReqCallback, statValues } = binding;
const { custom: kCustomPromisifiedSymbol } = __promisify__;
const { kIoMaxLength, kMaxUserId } = __constants__;
let truncateWarn = true;
let fs;
// Lazy loaded
let cpFn;
let cpSyncFn;
let promises = null;
let ReadStream;
let WriteStream;
let rimraf;
let rimrafSync;
let kResistStopPropagation;
// These have to be separate because of how graceful-fs happens to do it's
// monkeypatching.
let FileReadStream;
let FileWriteStream;
function showTruncateDeprecation() {
if (truncateWarn) {
process.emitWarning(
"Using fs.truncate with a file descriptor is deprecated. Please use " +
"fs.ftruncate with a file descriptor instead.",
"DeprecationWarning",
"DEP0081",
);
truncateWarn = false;
}
}
// Ensure that callbacks run in the global context. Only use this function
// for callbacks that are passed to the binding layer, callbacks that are
// invoked from JS already run in the proper scope.
function makeCallback(cb) {
validateFunction(cb, "cb");
return (...args) => ReflectApply(cb, this, args);
}
// Special case of `makeCallback()` that is specific to async `*stat()` calls as
// an optimization, since the data passed back to the callback needs to be
// transformed anyway.
function makeStatsCallback(cb) {
validateFunction(cb, "cb");
return (err, stats) => {
if (err) return cb(err);
cb(err, getStatsFromBinding(stats));
};
}
const isFd = isInt32;
function isFileType(stats, fileType) {
// Use stats array directly to avoid creating an fs.Stats instance just for
// our internal use.
let mode = stats[1];
if (typeof mode === "bigint") mode = Number(mode);
return (mode & S_IFMT) === fileType;
}
/**
* Tests a user's permissions for the file or directory
* specified by `path`.
* @param {string | Buffer | URL} path
* @param {number} [mode]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function access(path, mode, callback) {
if (typeof mode === "function") {
callback = mode;
mode = F_OK;
}
path = getValidatedPath(path);
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.access(path, mode, req);
}
/**
* Synchronously tests a user's permissions for the file or
* directory specified by `path`.
* @param {string | Buffer | URL} path
* @param {number} [mode]
* @returns {void}
*/
function accessSync(path, mode) {
binding.access(getValidatedPath(path), mode);
}
/**
* Tests whether or not the given path exists.
* @param {string | Buffer | URL} path
* @param {(exists?: boolean) => any} callback
* @returns {void}
*/
function exists(path, callback) {
validateFunction(callback, "cb");
function suppressedCallback(err) {
callback(!err);
}
try {
fs.access(path, F_OK, suppressedCallback);
} catch {
return callback(false);
}
}
Object.defineProperty(exists, kCustomPromisifiedSymbol, {
__proto__: null,
value: function exists(path) {
// eslint-disable-line func-name-matching
return new Promise((resolve) => fs.exists(path, resolve));
},
});
// fs.existsSync never throws, it only returns true or false.
// Since fs.existsSync never throws, users have established
// the expectation that passing invalid arguments to it, even like
// fs.existsSync(), would only get a false in return, so we cannot signal
// validation errors to users properly out of compatibility concerns.
// TODO(joyeecheung): deprecate the never-throw-on-invalid-arguments behavior
/**
* Synchronously tests whether or not the given path exists.
* @param {string | Buffer | URL} path
* @returns {boolean}
*/
function existsSync(path) {
try {
path = getValidatedPath(path);
} catch {
return false;
}
return binding.existsSync(path);
}
function readFileAfterOpen(err, fd) {
const context = this.context;
if (err) {
context.callback(err);
return;
}
context.fd = fd;
const req = new FSReqCallback();
req.oncomplete = readFileAfterStat;
req.context = context;
binding.fstat(fd, false, req);
}
function readFileAfterStat(err, stats) {
const context = this.context;
if (err) return context.close(err);
// TODO(BridgeAR): Check if allocating a smaller chunk is better performance
// wise, similar to the promise based version (less peak memory and chunked
// stringify operations vs multiple C++/JS boundary crossings).
const size = (context.size = isFileType(stats, S_IFREG) ? stats[8] : 0);
if (size > kIoMaxLength) {
err = new ERR_FS_FILE_TOO_LARGE(size);
return context.close(err);
}
try {
if (size === 0) {
// TODO(BridgeAR): If an encoding is set, use the StringDecoder to concat
// the result and reuse the buffer instead of allocating a new one.
context.buffers = [];
} else {
context.buffer = Buffer.allocUnsafeSlow(size);
}
} catch (err) {
return context.close(err);
}
context.read();
}
function checkAborted(signal, callback) {
if (signal?.aborted) {
callback(new AbortError(undefined, { cause: signal?.reason }));
return true;
}
return false;
}
/**
* Asynchronously reads the entire contents of a file.
* @param {string | Buffer | URL | number} path
* @param {{
* encoding?: string | null;
* flag?: string;
* signal?: AbortSignal;
* } | string} [options]
* @param {(
* err?: Error,
* data?: string | Buffer
* ) => any} callback
* @returns {void}
*/
function readFile(path, options, callback) {
callback ||= options;
validateFunction(callback, "cb");
options = getOptions(options, { flag: "r" });
const ReadFileContext = __hoisted_internal_fs_read_context__;
const context = new ReadFileContext(callback, options.encoding);
context.isUserFd = isFd(path); // File descriptor ownership
if (options.signal) {
context.signal = options.signal;
}
if (context.isUserFd) {
process.nextTick(function tick(context) {
ReflectApply(readFileAfterOpen, { context }, [null, path]);
}, context);
return;
}
if (checkAborted(options.signal, callback)) return;
const flagsNumber = stringToFlags(options.flag, "options.flag");
const req = new FSReqCallback();
req.context = context;
req.oncomplete = readFileAfterOpen;
binding.open(getValidatedPath(path), flagsNumber, 0o666, req);
}
function tryStatSync(fd, isUserFd) {
const stats = binding.fstat(fd, false, undefined, true /* shouldNotThrow */);
if (stats === undefined && !isUserFd) {
fs.closeSync(fd);
}
return stats;
}
function tryCreateBuffer(size, fd, isUserFd) {
let threw = true;
let buffer;
try {
if (size > kIoMaxLength) {
throw new ERR_FS_FILE_TOO_LARGE(size);
}
buffer = Buffer.allocUnsafe(size);
threw = false;
} finally {
if (threw && !isUserFd) fs.closeSync(fd);
}
return buffer;
}
function tryReadSync(fd, isUserFd, buffer, pos, len) {
let threw = true;
let bytesRead;
try {
bytesRead = fs.readSync(fd, buffer, pos, len);
threw = false;
} finally {
if (threw && !isUserFd) fs.closeSync(fd);
}
return bytesRead;
}
/**
* Synchronously reads the entire contents of a file.
* @param {string | Buffer | URL | number} path
* @param {{
* encoding?: string | null;
* flag?: string;
* }} [options]
* @returns {string | Buffer}
*/
function readFileSync(path, options) {
options = getOptions(options, { flag: "r" });
if (options.encoding === "utf8" || options.encoding === "utf-8") {
if (!isInt32(path)) {
path = getValidatedPath(path);
}
return binding.readFileUtf8(path, stringToFlags(options.flag));
}
const isUserFd = isFd(path); // File descriptor ownership
const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
const stats = tryStatSync(fd, isUserFd);
const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
let pos = 0;
let buffer; // Single buffer with file data
let buffers; // List for when size is unknown
if (size === 0) {
buffers = [];
} else {
buffer = tryCreateBuffer(size, fd, isUserFd);
}
let bytesRead;
if (size !== 0) {
do {
bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
pos += bytesRead;
} while (bytesRead !== 0 && pos < size);
} else {
do {
// The kernel lies about many files.
// Go ahead and try to read some bytes.
buffer = Buffer.allocUnsafe(8192);
bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
if (bytesRead !== 0) {
Array.prototype.push.call(buffers, buffer.slice(0, bytesRead));
}
pos += bytesRead;
} while (bytesRead !== 0);
}
if (!isUserFd) fs.closeSync(fd);
if (size === 0) {
// Data was collected into the buffers list.
buffer = Buffer.concat(buffers, pos);
} else if (pos < size) {
buffer = buffer.slice(0, pos);
}
if (options.encoding) buffer = buffer.toString(options.encoding);
return buffer;
}
function defaultCloseCallback(err) {
if (err != null) throw err;
}
/**
* Closes the file descriptor.
* @param {number} fd
* @param {(err?: Error) => any} [callback]
* @returns {void}
*/
function close(fd, callback = defaultCloseCallback) {
if (callback !== defaultCloseCallback) callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.close(fd, req);
}
/**
* Synchronously closes the file descriptor.
* @param {number} fd
* @returns {void}
*/
function closeSync(fd) {
binding.close(fd);
}
/**
* Asynchronously opens a file.
* @param {string | Buffer | URL} path
* @param {string | number} [flags]
* @param {string | number} [mode]
* @param {(
* err?: Error,
* fd?: number
* ) => any} callback
* @returns {void}
*/
function open(path, flags, mode, callback) {
path = getValidatedPath(path);
if (arguments.length < 3) {
callback = flags;
flags = "r";
mode = 0o666;
} else if (typeof mode === "function") {
callback = mode;
mode = 0o666;
} else {
mode = parseFileMode(mode, "mode", 0o666);
}
const flagsNumber = stringToFlags(flags);
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.open(path, flagsNumber, mode, req);
}
/**
* Synchronously opens a file.
* @param {string | Buffer | URL} path
* @param {string | number} [flags]
* @param {string | number} [mode]
* @returns {number}
*/
function openSync(path, flags, mode) {
return binding.open(
getValidatedPath(path),
stringToFlags(flags),
parseFileMode(mode, "mode", 0o666),
);
}
/**
* @param {string | Buffer | URL } path
* @param {{
* type?: string;
* }} [options]
* @returns {Promise<Blob>}
*/
function openAsBlob(path, options = kEmptyObject) {
validateObject(options, "options");
const type = options.type || "";
validateString(type, "options.type");
// The underlying implementation here returns the Blob synchronously for now.
// To give ourselves flexibility to maybe return the Blob asynchronously,
// this API returns a Promise.
path = getValidatedPath(path);
return Promise.resolve(createBlobFromFilePath(path, { type }));
}
/**
* Reads file from the specified `fd` (file descriptor).
* @param {number} fd
* @param {Buffer | TypedArray | DataView} buffer
* @param {number | {
* offset?: number;
* length?: number;
* position?: number | bigint | null;
* }} [offsetOrOptions]
* @param {number} length
* @param {number | bigint | null} position
* @param {(
* err?: Error,
* bytesRead?: number,
* buffer?: Buffer
* ) => any} callback
* @returns {void}
*/
function read(fd, buffer, offsetOrOptions, length, position, callback) {
fd = getValidatedFd(fd);
let offset = offsetOrOptions;
let params = null;
if (arguments.length <= 4) {
if (arguments.length === 4) {
// This is fs.read(fd, buffer, options, callback)
validateObject(offsetOrOptions, "options", kValidateObjectAllowNullable);
callback = length;
params = offsetOrOptions;
} else if (arguments.length === 3) {
// This is fs.read(fd, bufferOrParams, callback)
if (!isArrayBufferView(buffer)) {
// This is fs.read(fd, params, callback)
params = buffer;
({ buffer = Buffer.alloc(16384) } = params ?? kEmptyObject);
}
callback = offsetOrOptions;
} else {
// This is fs.read(fd, callback)
callback = buffer;
buffer = Buffer.alloc(16384);
}
if (params !== undefined) {
validateObject(params, "options", kValidateObjectAllowNullable);
}
({
offset = 0,
length = buffer?.byteLength - offset,
position = null,
} = params ?? kEmptyObject);
}
validateBuffer(buffer);
validateFunction(callback, "cb");
if (offset == null) {
offset = 0;
} else {
validateInteger(offset, "offset", 0);
}
length |= 0;
if (length === 0) {
return process.nextTick(function tick() {
callback(null, 0, buffer);
});
}
if (buffer.byteLength === 0) {
throw new ERR_INVALID_ARG_VALUE(
"buffer",
buffer,
"is empty and cannot be written",
);
}
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (position == null) {
position = -1;
} else {
validatePosition(position, "position", length);
}
function wrapper(err, bytesRead) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, bytesRead || 0, buffer);
}
const req = new FSReqCallback();
req.oncomplete = wrapper;
binding.read(fd, buffer, offset, length, position, req);
}
Object.defineProperty(read, kCustomPromisifyArgsSymbol, {
__proto__: null,
value: ["bytesRead", "buffer"],
enumerable: false,
});
/**
* Synchronously reads the file from the
* specified `fd` (file descriptor).
* @param {number} fd
* @param {Buffer | TypedArray | DataView} buffer
* @param {number | {
* offset?: number;
* length?: number;
* position?: number | bigint | null;
* }} [offsetOrOptions]
* @param {number} [length]
* @param {number} [position]
* @returns {number}
*/
function readSync(fd, buffer, offsetOrOptions, length, position) {
fd = getValidatedFd(fd);
validateBuffer(buffer);
let offset = offsetOrOptions;
if (arguments.length <= 3 || typeof offsetOrOptions === "object") {
if (offsetOrOptions !== undefined) {
validateObject(offsetOrOptions, "options", kValidateObjectAllowNullable);
}
({
offset = 0,
length = buffer.byteLength - offset,
position = null,
} = offsetOrOptions ?? kEmptyObject);
}
if (offset === undefined) {
offset = 0;
} else {
validateInteger(offset, "offset", 0);
}
length |= 0;
if (length === 0) {
return 0;
}
if (buffer.byteLength === 0) {
throw new ERR_INVALID_ARG_VALUE(
"buffer",
buffer,
"is empty and cannot be written",
);
}
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (position == null) {
position = -1;
} else {
validatePosition(position, "position", length);
}
return binding.read(fd, buffer, offset, length, position);
}
/**
* Reads file from the specified `fd` (file descriptor)
* and writes to an array of `ArrayBufferView`s.
* @param {number} fd
* @param {ArrayBufferView[]} buffers
* @param {number | null} [position]
* @param {(
* err?: Error,
* bytesRead?: number,
* buffers?: ArrayBufferView[]
* ) => any} callback
* @returns {void}
*/
function readv(fd, buffers, position, callback) {
function wrapper(err, read) {
callback(err, read || 0, buffers);
}
fd = getValidatedFd(fd);
validateBufferArray(buffers);
callback ||= position;
validateFunction(callback, "cb");
const req = new FSReqCallback();
req.oncomplete = wrapper;
if (typeof position !== "number") position = null;
binding.readBuffers(fd, buffers, position, req);
}
Object.defineProperty(readv, kCustomPromisifyArgsSymbol, {
__proto__: null,
value: ["bytesRead", "buffers"],
enumerable: false,
});
/**
* Synchronously reads file from the
* specified `fd` (file descriptor) and writes to an array
* of `ArrayBufferView`s.
* @param {number} fd
* @param {ArrayBufferView[]} buffers
* @param {number | null} [position]
* @returns {number}
*/
function readvSync(fd, buffers, position) {
fd = getValidatedFd(fd);
validateBufferArray(buffers);
if (typeof position !== "number") position = null;
return binding.readBuffers(fd, buffers, position);
}
/**
* Writes `buffer` to the specified `fd` (file descriptor).
* @param {number} fd
* @param {Buffer | TypedArray | DataView | string} buffer
* @param {number | object} [offsetOrOptions]
* @param {number} [length]
* @param {number | null} [position]
* @param {(
* err?: Error,
* bytesWritten?: number,
* buffer?: Buffer | TypedArray | DataView
* ) => any} callback
* @returns {void}
*/
function write(fd, buffer, offsetOrOptions, length, position, callback) {
function wrapper(err, written) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, written || 0, buffer);
}
fd = getValidatedFd(fd);
let offset = offsetOrOptions;
if (isArrayBufferView(buffer)) {
callback ||= position || length || offset;
validateFunction(callback, "cb");
if (typeof offset === "object") {
({
offset = 0,
length = buffer.byteLength - offset,
position = null,
} = offsetOrOptions ?? kEmptyObject);
}
if (offset == null || typeof offset === "function") {
offset = 0;
} else {
validateInteger(offset, "offset", 0);
}
if (typeof length !== "number") length = buffer.byteLength - offset;
if (typeof position !== "number") position = null;
validateOffsetLengthWrite(offset, length, buffer.byteLength);
const req = new FSReqCallback();
req.oncomplete = wrapper;
binding.writeBuffer(fd, buffer, offset, length, position, req);
return;
}
validateStringAfterArrayBufferView(buffer, "buffer");
if (typeof position !== "function") {
if (typeof offset === "function") {
position = offset;
offset = null;
} else {
position = length;
}
length = "utf8";
}
const str = buffer;
validateEncoding(str, length);
callback = position;
validateFunction(callback, "cb");
const req = new FSReqCallback();
req.oncomplete = wrapper;
binding.writeString(fd, str, offset, length, req);
}
Object.defineProperty(write, kCustomPromisifyArgsSymbol, {
__proto__: null,
value: ["bytesWritten", "buffer"],
enumerable: false,
});
/**
* Synchronously writes `buffer` to the
* specified `fd` (file descriptor).
* @param {number} fd
* @param {Buffer | TypedArray | DataView | string} buffer
* @param {{
* offset?: number;
* length?: number;
* position?: number | null;
* }} [offsetOrOptions]
* @param {number} [length]
* @param {number} [position]
* @returns {number}
*/
function writeSync(fd, buffer, offsetOrOptions, length, position) {
fd = getValidatedFd(fd);
const ctx = {};
let result;
let offset = offsetOrOptions;
if (isArrayBufferView(buffer)) {
if (typeof offset === "object") {
({
offset = 0,
length = buffer.byteLength - offset,
position = null,
} = offsetOrOptions ?? kEmptyObject);
}
if (position === undefined) position = null;
if (offset == null) {
offset = 0;
} else {
validateInteger(offset, "offset", 0);
}
if (typeof length !== "number") length = buffer.byteLength - offset;
validateOffsetLengthWrite(offset, length, buffer.byteLength);
result = binding.writeBuffer(
fd,
buffer,
offset,
length,
position,
undefined,
ctx,
);
} else {
validateStringAfterArrayBufferView(buffer, "buffer");
validateEncoding(buffer, length);
if (offset === undefined) offset = null;
result = binding.writeString(fd, buffer, offset, length, undefined, ctx);
}
handleErrorFromBinding(ctx);
return result;
}
/**
* Writes an array of `ArrayBufferView`s to the
* specified `fd` (file descriptor).
* @param {number} fd
* @param {ArrayBufferView[]} buffers
* @param {number | null} [position]
* @param {(
* err?: Error,
* bytesWritten?: number,
* buffers?: ArrayBufferView[]
* ) => any} callback
* @returns {void}
*/
function writev(fd, buffers, position, callback) {
function wrapper(err, written) {
callback(err, written || 0, buffers);
}
fd = getValidatedFd(fd);
validateBufferArray(buffers);
callback ||= position;
validateFunction(callback, "cb");
if (buffers.length === 0) {
process.nextTick(callback, null, 0, buffers);
return;
}
const req = new FSReqCallback();
req.oncomplete = wrapper;
if (typeof position !== "number") position = null;
binding.writeBuffers(fd, buffers, position, req);
}
Object.defineProperty(writev, kCustomPromisifyArgsSymbol, {
__proto__: null,
value: ["bytesWritten", "buffer"],
enumerable: false,
});
/**
* Synchronously writes an array of `ArrayBufferView`s
* to the specified `fd` (file descriptor).
* @param {number} fd
* @param {ArrayBufferView[]} buffers
* @param {number | null} [position]
* @returns {number}
*/
function writevSync(fd, buffers, position) {
fd = getValidatedFd(fd);
validateBufferArray(buffers);
if (buffers.length === 0) {
return 0;
}
if (typeof position !== "number") position = null;
return binding.writeBuffers(fd, buffers, position);
}
/**
* Asynchronously renames file at `oldPath` to
* the pathname provided as `newPath`.
* @param {string | Buffer | URL} oldPath
* @param {string | Buffer | URL} newPath
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function rename(oldPath, newPath, callback) {
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.rename(
getValidatedPath(oldPath, "oldPath"),
getValidatedPath(newPath, "newPath"),
req,
);
}
/**
* Synchronously renames file at `oldPath` to
* the pathname provided as `newPath`.
* @param {string | Buffer | URL} oldPath
* @param {string | Buffer | URL} newPath
* @returns {void}
*/
function renameSync(oldPath, newPath) {
binding.rename(
getValidatedPath(oldPath, "oldPath"),
getValidatedPath(newPath, "newPath"),
);
}
/**
* Truncates the file.
* @param {string | Buffer | URL} path
* @param {number} [len]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function truncate(path, len, callback) {
if (typeof path === "number") {
showTruncateDeprecation();
return fs.ftruncate(path, len, callback);
}
if (typeof len === "function") {
callback = len;
len = 0;
} else if (len === undefined) {
len = 0;
}
validateInteger(len, "len");
len = Math.max(0, len);
validateFunction(callback, "cb");
fs.open(path, "r+", (er, fd) => {
if (er) return callback(er);
const req = new FSReqCallback();
req.oncomplete = function oncomplete(er) {
fs.close(fd, (er2) => {
callback(aggregateTwoErrors(er2, er));
});
};
binding.ftruncate(fd, len, req);
});
}
/**
* Synchronously truncates the file.
* @param {string | Buffer | URL} path
* @param {number} [len]
* @returns {void}
*/
function truncateSync(path, len) {
if (typeof path === "number") {
// legacy
showTruncateDeprecation();
return fs.ftruncateSync(path, len);
}
if (len === undefined) {
len = 0;
}
// Allow error to be thrown, but still close fd.
const fd = fs.openSync(path, "r+");
try {
fs.ftruncateSync(fd, len);
} finally {
fs.closeSync(fd);
}
}
/**
* Truncates the file descriptor.
* @param {number} fd
* @param {number} [len]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function ftruncate(fd, len = 0, callback) {
if (typeof len === "function") {
callback = len;
len = 0;
}
validateInteger(len, "len");
len = Math.max(0, len);
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.ftruncate(fd, len, req);
}
/**
* Synchronously truncates the file descriptor.
* @param {number} fd
* @param {number} [len]
* @returns {void}
*/
function ftruncateSync(fd, len = 0) {
validateInteger(len, "len");
binding.ftruncate(fd, len < 0 ? 0 : len);
}
function lazyLoadCp() {
if (cpFn === undefined) {
({ cpFn } = __hoisted_internal_fs_cp_cp__);
cpFn = __hoisted_util__.callbackify(cpFn);
({ cpSyncFn } = __hoisted_internal_fs_cp_cp_sync__);
}
}
function lazyLoadRimraf() {
if (rimraf === undefined)
({ rimraf, rimrafSync } = __hoisted_internal_fs_rimraf__);
}
/**
* Asynchronously removes a directory.
* @param {string | Buffer | URL} path
* @param {{
* maxRetries?: number;
* recursive?: boolean;
* retryDelay?: number;
* }} [options]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function rmdir(path, options, callback) {
if (typeof options === "function") {
callback = options;
options = undefined;
}
callback = makeCallback(callback);
path = getValidatedPath(path);
if (options?.recursive) {
emitRecursiveRmdirWarning();
validateRmOptions(
path,
{ ...options, force: false },
true,
(err, options) => {
if (err === false) {
const req = new FSReqCallback();
req.oncomplete = callback;
binding.rmdir(path, req);
return;
}
if (err) {
return callback(err);
}
lazyLoadRimraf();
rimraf(path, options, callback);
},
);
} else {
validateRmdirOptions(options);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.rmdir(path, req);
}
}
/**
* Synchronously removes a directory.
* @param {string | Buffer | URL} path
* @param {{
* maxRetries?: number;
* recursive?: boolean;
* retryDelay?: number;
* }} [options]
* @returns {void}
*/
function rmdirSync(path, options) {
path = getValidatedPath(path);
if (options?.recursive) {
emitRecursiveRmdirWarning();
options = validateRmOptionsSync(path, { ...options, force: false }, true);
if (options !== false) {
lazyLoadRimraf();
return rimrafSync(path, options);
}
} else {
validateRmdirOptions(options);
}
binding.rmdir(path);
}
/**
* Asynchronously removes files and
* directories (modeled on the standard POSIX `rm` utility).
* @param {string | Buffer | URL} path
* @param {{
* force?: boolean;
* maxRetries?: number;
* recursive?: boolean;
* retryDelay?: number;
* }} [options]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function rm(path, options, callback) {
if (typeof options === "function") {
callback = options;
options = undefined;
}
path = getValidatedPath(path);
validateRmOptions(path, options, false, (err, options) => {
if (err) {
return callback(err);
}
lazyLoadRimraf();
return rimraf(path, options, callback);
});
}
/**
* Synchronously removes files and
* directories (modeled on the standard POSIX `rm` utility).
* @param {string | Buffer | URL} path
* @param {{
* force?: boolean;
* maxRetries?: number;
* recursive?: boolean;
* retryDelay?: number;
* }} [options]
* @returns {void}
*/
function rmSync(path, options) {
lazyLoadRimraf();
return rimrafSync(
getValidatedPath(path),
validateRmOptionsSync(path, options, false),
);
}
/**
* Forces all currently queued I/O operations associated
* with the file to the operating system's synchronized
* I/O completion state.
* @param {number} fd
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function fdatasync(fd, callback) {
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.fdatasync(fd, req);
}
/**
* Synchronously forces all currently queued I/O operations
* associated with the file to the operating
* system's synchronized I/O completion state.
* @param {number} fd
* @returns {void}
*/
function fdatasyncSync(fd) {
binding.fdatasync(fd);
}
/**
* Requests for all data for the open file descriptor
* to be flushed to the storage device.
* @param {number} fd
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function fsync(fd, callback) {
const req = new FSReqCallback();
req.oncomplete = makeCallback(callback);
binding.fsync(fd, req);
}
/**
* Synchronously requests for all data for the open
* file descriptor to be flushed to the storage device.
* @param {number} fd
* @returns {void}
*/
function fsyncSync(fd) {
binding.fsync(fd);
}
/**
* Asynchronously creates a directory.
* @param {string | Buffer | URL} path
* @param {{
* recursive?: boolean;
* mode?: string | number;
* } | number} [options]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function mkdir(path, options, callback) {
let mode = 0o777;
let recursive = false;
if (typeof options === "function") {
callback = options;
} else if (typeof options === "number" || typeof options === "string") {
mode = parseFileMode(options, "mode");
} else if (options) {
if (options.recursive !== undefined) {
recursive = options.recursive;
validateBoolean(recursive, "options.recursive");
}
if (options.mode !== undefined) {
mode = parseFileMode(options.mode, "options.mode");
}
}
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.mkdir(getValidatedPath(path), mode, recursive, req);
}
/**
* Synchronously creates a directory.
* @param {string | Buffer | URL} path
* @param {{
* recursive?: boolean;
* mode?: string | number;
* } | number} [options]
* @returns {string | void}
*/
function mkdirSync(path, options) {
let mode = 0o777;
let recursive = false;
if (typeof options === "number" || typeof options === "string") {
mode = parseFileMode(options, "mode");
} else if (options) {
if (options.recursive !== undefined) {
recursive = options.recursive;
validateBoolean(recursive, "options.recursive");
}
if (options.mode !== undefined) {
mode = parseFileMode(options.mode, "options.mode");
}
}
const result = binding.mkdir(getValidatedPath(path), mode, recursive);
if (recursive) {
return result;
}
}
/**
* An iterative algorithm for reading the entire contents of the `basePath` directory.
* This function does not validate `basePath` as a directory. It is passed directly to
* `binding.readdir`.
* @param {string} basePath
* @param {{ encoding: string, withFileTypes: boolean }} options
* @returns {string[] | Dirent[]}
*/
function readdirSyncRecursive(basePath, options) {
const withFileTypes = Boolean(options.withFileTypes);
const encoding = options.encoding;
const readdirResults = [];
const pathsQueue = [basePath];
function read(path) {
const readdirResult = binding.readdir(path, encoding, withFileTypes);
if (readdirResult === undefined) {
return;
}
if (withFileTypes) {
// Calling `readdir` with `withFileTypes=true`, the result is an array of arrays.
// The first array is the names, and the second array is the types.
// They are guaranteed to be the same length; hence, setting `length` to the length
// of the first array within the result.
const length = readdirResult[0].length;
for (let i = 0; i < length; i++) {
const dirent = getDirent(
path,
readdirResult[0][i],
readdirResult[1][i],
);
Array.prototype.push.call(readdirResults, dirent);
if (dirent.isDirectory()) {
Array.prototype.push.call(
pathsQueue,
pathModule.join(dirent.parentPath, dirent.name),
);
}
}
} else {
for (let i = 0; i < readdirResult.length; i++) {
const resultPath = pathModule.join(path, readdirResult[i]);
const relativeResultPath = pathModule.relative(basePath, resultPath);
const stat = binding.internalModuleStat(resultPath);
Array.prototype.push.call(readdirResults, relativeResultPath);
// 1 indicates directory
if (stat === 1) {
Array.prototype.push.call(pathsQueue, resultPath);
}
}
}
}
for (let i = 0; i < pathsQueue.length; i++) {
read(pathsQueue[i]);
}
return readdirResults;
}
/**
* Reads the contents of a directory.
* @param {string | Buffer | URL} path
* @param {string | {
* encoding?: string;
* withFileTypes?: boolean;
* recursive?: boolean;
* }} [options]
* @param {(
* err?: Error,
* files?: string[] | Buffer[] | Dirent[]
* ) => any} callback
* @returns {void}
*/
function readdir(path, options, callback) {
callback = makeCallback(typeof options === "function" ? options : callback);
options = getOptions(options);
path = getValidatedPath(path);
if (options.recursive != null) {
validateBoolean(options.recursive, "options.recursive");
}
if (options.recursive) {
callback(null, readdirSyncRecursive(path, options));
return;
}
const req = new FSReqCallback();
if (!options.withFileTypes) {
req.oncomplete = callback;
} else {
req.oncomplete = (err, result) => {
if (err) {
callback(err);
return;
}
getDirents(path, result, callback);
};
}
binding.readdir(path, options.encoding, !!options.withFileTypes, req);
}
/**
* Synchronously reads the contents of a directory.
* @param {string | Buffer | URL} path
* @param {string | {
* encoding?: string;
* withFileTypes?: boolean;
* recursive?: boolean;
* }} [options]
* @returns {string | Buffer[] | Dirent[]}
*/
function readdirSync(path, options) {
options = getOptions(options);
path = getValidatedPath(path);
if (options.recursive != null) {
validateBoolean(options.recursive, "options.recursive");
}
if (options.recursive) {
return readdirSyncRecursive(path, options);
}
const result = binding.readdir(
path,
options.encoding,
!!options.withFileTypes,
);
return result !== undefined && options.withFileTypes
? getDirents(path, result)
: result;
}
/**
* Invokes the callback with the `fs.Stats`
* for the file descriptor.
* @param {number} fd
* @param {{ bigint?: boolean; }} [options]
* @param {(
* err?: Error,
* stats?: Stats
* ) => any} [callback]
* @returns {void}
*/
function fstat(fd, options = { bigint: false }, callback) {
if (typeof options === "function") {
callback = options;
options = kEmptyObject;
}
callback = makeStatsCallback(callback);
const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
binding.fstat(fd, options.bigint, req);
}
/**
* Retrieves the `fs.Stats` for the symbolic link
* referred to by the `path`.
* @param {string | Buffer | URL} path
* @param {{ bigint?: boolean; }} [options]
* @param {(
* err?: Error,
* stats?: Stats
* ) => any} callback
* @returns {void}
*/
function lstat(path, options = { bigint: false }, callback) {
if (typeof options === "function") {
callback = options;
options = kEmptyObject;
}
callback = makeStatsCallback(callback);
path = getValidatedPath(path);
if (permission.isEnabled() && !permission.has("fs.read", path)) {
const resource = BufferIsBuffer(path) ? BufferToString(path) : path;
callback(
new ERR_ACCESS_DENIED(
"Access to this API has been restricted",
"FileSystemRead",
resource,
),
);
return;
}
const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
binding.lstat(path, options.bigint, req);
}
/**
* Asynchronously gets the stats of a file.
* @param {string | Buffer | URL} path
* @param {{ bigint?: boolean; }} [options]
* @param {(
* err?: Error,
* stats?: Stats
* ) => any} callback
* @returns {void}
*/
function stat(path, options = { bigint: false }, callback) {
if (typeof options === "function") {
callback = options;
options = kEmptyObject;
}
callback = makeStatsCallback(callback);
const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
binding.stat(getValidatedPath(path), options.bigint, req);
}
function statfs(path, options = { bigint: false }, callback) {
if (typeof options === "function") {
callback = options;
options = kEmptyObject;
}
validateFunction(callback, "cb");
path = getValidatedPath(path);
const req = new FSReqCallback(options.bigint);
req.oncomplete = (err, stats) => {
if (err) {
return callback(err);
}
callback(err, getStatFsFromBinding(stats));
};
binding.statfs(getValidatedPath(path), options.bigint, req);
}
/**
* Synchronously retrieves the `fs.Stats` for
* the file descriptor.
* @param {number} fd
* @param {{ bigint?: boolean; }} [options]
* @returns {Stats | undefined}
*/
function fstatSync(fd, options = { bigint: false }) {
const stats = binding.fstat(fd, options.bigint, undefined, false);
if (stats === undefined) {
return;
}
return getStatsFromBinding(stats);
}
/**
* Synchronously retrieves the `fs.Stats` for
* the symbolic link referred to by the `path`.
* @param {string | Buffer | URL} path
* @param {{
* bigint?: boolean;
* throwIfNoEntry?: boolean;
* }} [options]
* @returns {Stats | undefined}
*/
function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
path = getValidatedPath(path);
if (permission.isEnabled() && !permission.has("fs.read", path)) {
const resource = BufferIsBuffer(path) ? BufferToString(path) : path;
throw new ERR_ACCESS_DENIED(
"Access to this API has been restricted",
"FileSystemRead",
resource,
);
}
const stats = binding.lstat(
getValidatedPath(path),
options.bigint,
undefined,
options.throwIfNoEntry,
);
if (stats === undefined) {
return;
}
return getStatsFromBinding(stats);
}
/**
* Synchronously retrieves the `fs.Stats`
* for the `path`.
* @param {string | Buffer | URL} path
* @param {{
* bigint?: boolean;
* throwIfNoEntry?: boolean;
* }} [options]
* @returns {Stats}
*/
function statSync(path, options = { bigint: false, throwIfNoEntry: true }) {
const stats = binding.stat(
getValidatedPath(path),
options.bigint,
undefined,
options.throwIfNoEntry,
);
if (stats === undefined) {
return undefined;
}
return getStatsFromBinding(stats);
}
function statfsSync(path, options = { bigint: false }) {
const stats = binding.statfs(getValidatedPath(path), options.bigint);
return getStatFsFromBinding(stats);
}
/**
* Reads the contents of a symbolic link
* referred to by `path`.
* @param {string | Buffer | URL} path
* @param {{ encoding?: string; } | string} [options]
* @param {(
* err?: Error,
* linkString?: string | Buffer
* ) => any} callback
* @returns {void}
*/
function readlink(path, options, callback) {
callback = makeCallback(typeof options === "function" ? options : callback);
options = getOptions(options);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.readlink(getValidatedPath(path), options.encoding, req);
}
/**
* Synchronously reads the contents of a symbolic link
* referred to by `path`.
* @param {string | Buffer | URL} path
* @param {{ encoding?: string; } | string} [options]
* @returns {string | Buffer}
*/
function readlinkSync(path, options) {
options = getOptions(options);
return binding.readlink(getValidatedPath(path), options.encoding);
}
/**
* Creates the link called `path` pointing to `target`.
* @param {string | Buffer | URL} target
* @param {string | Buffer | URL} path
* @param {string | null} [type_]
* @param {(err?: Error) => any} callback_
* @returns {void}
*/
function symlink(target, path, type_, callback_) {
const type = typeof type_ === "string" ? type_ : null;
const callback = makeCallback(arguments[arguments.length - 1]);
if (permission.isEnabled()) {
// The permission model's security guarantees fall apart in the presence of
// relative symbolic links. Thus, we have to prevent their creation.
if (BufferIsBuffer(target)) {
if (!isAbsolute(BufferToString(target))) {
callback(new ERR_ACCESS_DENIED("relative symbolic link target"));
return;
}
} else if (
typeof target !== "string" ||
!isAbsolute(toPathIfFileURL(target))
) {
callback(new ERR_ACCESS_DENIED("relative symbolic link target"));
return;
}
}
target = getValidatedPath(target, "target");
path = getValidatedPath(path);
if (isWindows && type === null) {
let absoluteTarget;
try {
// Symlinks targets can be relative to the newly created path.
// Calculate absolute file name of the symlink target, and check
// if it is a directory. Ignore resolve error to keep symlink
// errors consistent between platforms if invalid path is
// provided.
absoluteTarget = pathModule.resolve(path, "..", target);
} catch {
// Continue regardless of error.
}
if (absoluteTarget !== undefined) {
stat(absoluteTarget, (err, stat) => {
const resolvedType = !err && stat.isDirectory() ? "dir" : "file";
const resolvedFlags = stringToSymlinkType(resolvedType);
const destination = preprocessSymlinkDestination(
target,
resolvedType,
path,
);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.symlink(destination, path, resolvedFlags, req);
});
return;
}
}
const destination = preprocessSymlinkDestination(target, type, path);
const flags = stringToSymlinkType(type);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.symlink(destination, path, flags, req);
}
/**
* Synchronously creates the link called `path`
* pointing to `target`.
* @param {string | Buffer | URL} target
* @param {string | Buffer | URL} path
* @param {string | null} [type]
* @returns {void}
*/
function symlinkSync(target, path, type) {
type = typeof type === "string" ? type : null;
if (isWindows && type === null) {
const absoluteTarget = pathModule.resolve(`${path}`, "..", `${target}`);
if (statSync(absoluteTarget, { throwIfNoEntry: false })?.isDirectory()) {
type = "dir";
}
}
if (permission.isEnabled()) {
// The permission model's security guarantees fall apart in the presence of
// relative symbolic links. Thus, we have to prevent their creation.
if (BufferIsBuffer(target)) {
if (!isAbsolute(BufferToString(target))) {
throw new ERR_ACCESS_DENIED("relative symbolic link target");
}
} else if (
typeof target !== "string" ||
!isAbsolute(toPathIfFileURL(target))
) {
throw new ERR_ACCESS_DENIED("relative symbolic link target");
}
}
target = getValidatedPath(target, "target");
path = getValidatedPath(path);
binding.symlink(
preprocessSymlinkDestination(target, type, path),
path,
stringToSymlinkType(type),
);
}
/**
* Creates a new link from the `existingPath`
* to the `newPath`.
* @param {string | Buffer | URL} existingPath
* @param {string | Buffer | URL} newPath
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function link(existingPath, newPath, callback) {
callback = makeCallback(callback);
existingPath = getValidatedPath(existingPath, "existingPath");
newPath = getValidatedPath(newPath, "newPath");
const req = new FSReqCallback();
req.oncomplete = callback;
binding.link(existingPath, newPath, req);
}
/**
* Synchronously creates a new link from the `existingPath`
* to the `newPath`.
* @param {string | Buffer | URL} existingPath
* @param {string | Buffer | URL} newPath
* @returns {void}
*/
function linkSync(existingPath, newPath) {
existingPath = getValidatedPath(existingPath, "existingPath");
newPath = getValidatedPath(newPath, "newPath");
binding.link(existingPath, newPath);
}
/**
* Asynchronously removes a file or symbolic link.
* @param {string | Buffer | URL} path
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function unlink(path, callback) {
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.unlink(getValidatedPath(path), req);
}
/**
* Synchronously removes a file or symbolic link.
* @param {string | Buffer | URL} path
* @returns {void}
*/
function unlinkSync(path) {
binding.unlink(getValidatedPath(path));
}
/**
* Sets the permissions on the file.
* @param {number} fd
* @param {string | number} mode
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function fchmod(fd, mode, callback) {
mode = parseFileMode(mode, "mode");
callback = makeCallback(callback);
if (permission.isEnabled()) {
callback(
new ERR_ACCESS_DENIED(
"fchmod API is disabled when Permission Model is enabled.",
),
);
return;
}
const req = new FSReqCallback();
req.oncomplete = callback;
binding.fchmod(fd, mode, req);
}
/**
* Synchronously sets the permissions on the file.
* @param {number} fd
* @param {string | number} mode
* @returns {void}
*/
function fchmodSync(fd, mode) {
if (permission.isEnabled()) {
throw new ERR_ACCESS_DENIED(
"fchmod API is disabled when Permission Model is enabled.",
);
}
binding.fchmod(fd, parseFileMode(mode, "mode"));
}
/**
* Changes the permissions on a symbolic link.
* @param {string | Buffer | URL} path
* @param {number} mode
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function lchmod(path, mode, callback) {
validateFunction(callback, "cb");
mode = parseFileMode(mode, "mode");
fs.open(path, O_WRONLY | O_SYMLINK, (err, fd) => {
if (err) {
callback(err);
return;
}
// Prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchmod(fd, mode, (err) => {
fs.close(fd, (err2) => {
callback(aggregateTwoErrors(err2, err));
});
});
});
}
/**
* Synchronously changes the permissions on a symbolic link.
* @param {string | Buffer | URL} path
* @param {number} mode
* @returns {void}
*/
function lchmodSync(path, mode) {
const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
// Prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
try {
fs.fchmodSync(fd, mode);
} finally {
fs.closeSync(fd);
}
}
/**
* Asynchronously changes the permissions of a file.
* @param {string | Buffer | URL} path
* @param {string | number} mode
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function chmod(path, mode, callback) {
path = getValidatedPath(path);
mode = parseFileMode(mode, "mode");
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.chmod(path, mode, req);
}
/**
* Synchronously changes the permissions of a file.
* @param {string | Buffer | URL} path
* @param {string | number} mode
* @returns {void}
*/
function chmodSync(path, mode) {
path = getValidatedPath(path);
mode = parseFileMode(mode, "mode");
binding.chmod(path, mode);
}
/**
* Sets the owner of the symbolic link.
* @param {string | Buffer | URL} path
* @param {number} uid
* @param {number} gid
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function lchown(path, uid, gid, callback) {
callback = makeCallback(callback);
path = getValidatedPath(path);
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.lchown(path, uid, gid, req);
}
/**
* Synchronously sets the owner of the symbolic link.
* @param {string | Buffer | URL} path
* @param {number} uid
* @param {number} gid
* @returns {void}
*/
function lchownSync(path, uid, gid) {
path = getValidatedPath(path);
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
binding.lchown(path, uid, gid);
}
/**
* Sets the owner of the file.
* @param {number} fd
* @param {number} uid
* @param {number} gid
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function fchown(fd, uid, gid, callback) {
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
callback = makeCallback(callback);
if (permission.isEnabled()) {
callback(
new ERR_ACCESS_DENIED(
"fchown API is disabled when Permission Model is enabled.",
),
);
return;
}
const req = new FSReqCallback();
req.oncomplete = callback;
binding.fchown(fd, uid, gid, req);
}
/**
* Synchronously sets the owner of the file.
* @param {number} fd
* @param {number} uid
* @param {number} gid
* @returns {void}
*/
function fchownSync(fd, uid, gid) {
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
if (permission.isEnabled()) {
throw new ERR_ACCESS_DENIED(
"fchown API is disabled when Permission Model is enabled.",
);
}
binding.fchown(fd, uid, gid);
}
/**
* Asynchronously changes the owner and group
* of a file.
* @param {string | Buffer | URL} path
* @param {number} uid
* @param {number} gid
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function chown(path, uid, gid, callback) {
callback = makeCallback(callback);
path = getValidatedPath(path);
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.chown(path, uid, gid, req);
}
/**
* Synchronously changes the owner and group
* of a file.
* @param {string | Buffer | URL} path
* @param {number} uid
* @param {number} gid
* @returns {void}
*/
function chownSync(path, uid, gid) {
path = getValidatedPath(path);
validateInteger(uid, "uid", -1, kMaxUserId);
validateInteger(gid, "gid", -1, kMaxUserId);
binding.chown(path, uid, gid);
}
/**
* Changes the file system timestamps of the object
* referenced by `path`.
* @param {string | Buffer | URL} path
* @param {number | string | Date} atime
* @param {number | string | Date} mtime
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function utimes(path, atime, mtime, callback) {
callback = makeCallback(callback);
path = getValidatedPath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.utimes(path, toUnixTimestamp(atime), toUnixTimestamp(mtime), req);
}
/**
* Synchronously changes the file system timestamps
* of the object referenced by `path`.
* @param {string | Buffer | URL} path
* @param {number | string | Date} atime
* @param {number | string | Date} mtime
* @returns {void}
*/
function utimesSync(path, atime, mtime) {
binding.utimes(
getValidatedPath(path),
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
);
}
/**
* Changes the file system timestamps of the object
* referenced by the supplied `fd` (file descriptor).
* @param {number} fd
* @param {number | string | Date} atime
* @param {number | string | Date} mtime
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function futimes(fd, atime, mtime, callback) {
atime = toUnixTimestamp(atime, "atime");
mtime = toUnixTimestamp(mtime, "mtime");
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.futimes(fd, atime, mtime, req);
}
/**
* Synchronously changes the file system timestamps
* of the object referenced by the
* supplied `fd` (file descriptor).
* @param {number} fd
* @param {number | string | Date} atime
* @param {number | string | Date} mtime
* @returns {void}
*/
function futimesSync(fd, atime, mtime) {
binding.futimes(
fd,
toUnixTimestamp(atime, "atime"),
toUnixTimestamp(mtime, "mtime"),
);
}
/**
* Changes the access and modification times of
* a file in the same way as `fs.utimes()`.
* @param {string | Buffer | URL} path
* @param {number | string | Date} atime
* @param {number | string | Date} mtime
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function lutimes(path, atime, mtime, callback) {
callback = makeCallback(callback);
path = getValidatedPath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.lutimes(path, toUnixTimestamp(atime), toUnixTimestamp(mtime), req);
}
/**
* Synchronously changes the access and modification
* times of a file in the same way as `fs.utimesSync()`.
* @param {string | Buffer | URL} path
* @param {number | string | Date} atime
* @param {number | string | Date} mtime
* @returns {void}
*/
function lutimesSync(path, atime, mtime) {
binding.lutimes(
getValidatedPath(path),
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
);
}
function writeAll(
fd,
isUserFd,
buffer,
offset,
length,
signal,
flush,
callback,
) {
if (signal?.aborted) {
const abortError = new AbortError(undefined, { cause: signal?.reason });
if (isUserFd) {
callback(abortError);
} else {
fs.close(fd, (err) => {
callback(aggregateTwoErrors(err, abortError));
});
}
return;
}
// write(fd, buffer, offset, length, position, callback)
fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
if (writeErr) {
if (isUserFd) {
callback(writeErr);
} else {
fs.close(fd, (err) => {
callback(aggregateTwoErrors(err, writeErr));
});
}
} else if (written === length) {
if (!flush) {
if (isUserFd) {
callback(null);
} else {
fs.close(fd, callback);
}
} else {
fs.fsync(fd, (syncErr) => {
if (syncErr) {
if (isUserFd) {
callback(syncErr);
} else {
fs.close(fd, (err) => {
callback(aggregateTwoErrors(err, syncErr));
});
}
} else if (isUserFd) {
callback(null);
} else {
fs.close(fd, callback);
}
});
}
} else {
offset += written;
length -= written;
writeAll(fd, isUserFd, buffer, offset, length, signal, flush, callback);
}
});
}
/**
* Asynchronously writes data to the file.
* @param {string | Buffer | URL | number} path
* @param {string | Buffer | TypedArray | DataView} data
* @param {{
* encoding?: string | null;
* mode?: number;
* flag?: string;
* signal?: AbortSignal;
* flush?: boolean;
* } | string} [options]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function writeFile(path, data, options, callback) {
callback ||= options;
validateFunction(callback, "cb");
options = getOptions(options, {
encoding: "utf8",
mode: 0o666,
flag: "w",
flush: false,
});
const flag = options.flag || "w";
const flush = options.flush ?? false;
validateBoolean(flush, "options.flush");
if (!isArrayBufferView(data)) {
validateStringAfterArrayBufferView(data, "data");
data = Buffer.from(data, options.encoding || "utf8");
}
if (isFd(path)) {
const isUserFd = true;
const signal = options.signal;
writeAll(path, isUserFd, data, 0, data.byteLength, signal, flush, callback);
return;
}
if (checkAborted(options.signal, callback)) return;
fs.open(path, flag, options.mode, (openErr, fd) => {
if (openErr) {
callback(openErr);
} else {
const isUserFd = false;
const signal = options.signal;
writeAll(fd, isUserFd, data, 0, data.byteLength, signal, flush, callback);
}
});
}
/**
* Synchronously writes data to the file.
* @param {string | Buffer | URL | number} path
* @param {string | Buffer | TypedArray | DataView} data
* @param {{
* encoding?: string | null;
* mode?: number;
* flag?: string;
* flush?: boolean;
* } | string} [options]
* @returns {void}
*/
function writeFileSync(path, data, options) {
options = getOptions(options, {
encoding: "utf8",
mode: 0o666,
flag: "w",
flush: false,
});
const flush = options.flush ?? false;
validateBoolean(flush, "options.flush");
const flag = options.flag || "w";
// C++ fast path for string data and UTF8 encoding
if (
typeof data === "string" &&
(options.encoding === "utf8" || options.encoding === "utf-8")
) {
if (!isInt32(path)) {
path = getValidatedPath(path);
}
return binding.writeFileUtf8(
path,
data,
stringToFlags(flag),
parseFileMode(options.mode, "mode", 0o666),
);
}
if (!isArrayBufferView(data)) {
validateStringAfterArrayBufferView(data, "data");
data = Buffer.from(data, options.encoding || "utf8");
}
const isUserFd = isFd(path); // File descriptor ownership
const fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
let offset = 0;
let length = data.byteLength;
try {
while (length > 0) {
const written = fs.writeSync(fd, data, offset, length);
offset += written;
length -= written;
}
if (flush) {
fs.fsyncSync(fd);
}
} finally {
if (!isUserFd) fs.closeSync(fd);
}
}
/**
* Asynchronously appends data to a file.
* @param {string | Buffer | URL | number} path
* @param {string | Buffer} data
* @param {{
* encoding?: string | null;
* mode?: number;
* flag?: string;
* flush?: boolean;
* } | string} [options]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function appendFile(path, data, options, callback) {
callback ||= options;
validateFunction(callback, "cb");
options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" });
// Don't make changes directly on options object
options = copyObject(options);
// Force append behavior when using a supplied file descriptor
if (!options.flag || isFd(path)) options.flag = "a";
fs.writeFile(path, data, options, callback);
}
/**
* Synchronously appends data to a file.
* @param {string | Buffer | URL | number} path
* @param {string | Buffer} data
* @param {{
* encoding?: string | null;
* mode?: number;
* flag?: string;
* } | string} [options]
* @returns {void}
*/
function appendFileSync(path, data, options) {
options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" });
// Don't make changes directly on options object
options = copyObject(options);
// Force append behavior when using a supplied file descriptor
if (!options.flag || isFd(path)) options.flag = "a";
fs.writeFileSync(path, data, options);
}
/**
* Watches for the changes on `filename`.
* @param {string | Buffer | URL} filename
* @param {string | {
* persistent?: boolean;
* recursive?: boolean;
* encoding?: string;
* signal?: AbortSignal;
* }} [options]
* @param {(
* eventType?: string,
* filename?: string | Buffer
* ) => any} [listener]
* @returns {watchers.FSWatcher}
*/
function watch(filename, options, listener) {
if (typeof options === "function") {
listener = options;
}
options = getOptions(options);
// Don't make changes directly on options object
options = copyObject(options);
if (options.persistent === undefined) options.persistent = true;
if (options.recursive === undefined) options.recursive = false;
let watcher;
const watchers = __hoisted_internal_fs_watchers__;
const path = getValidatedPath(filename);
// TODO(anonrig): Remove non-native watcher when/if libuv supports recursive.
// As of November 2022, libuv does not support recursive file watch on all platforms,
// e.g. Linux due to the limitations of inotify.
if (options.recursive && !isMacOS && !isWindows) {
const nonNativeWatcher = __hoisted_internal_fs_recursive_watch__;
watcher = new nonNativeWatcher.FSWatcher(options);
watcher[watchers.kFSWatchStart](path);
} else {
watcher = new watchers.FSWatcher();
watcher[watchers.kFSWatchStart](
path,
options.persistent,
options.recursive,
options.encoding,
);
}
if (listener) {
watcher.addListener("change", listener);
}
if (options.signal) {
if (options.signal.aborted) {
process.nextTick(() => watcher.close());
} else {
const listener = () => watcher.close();
kResistStopPropagation ??=
__hoisted_internal_event_target__.kResistStopPropagation;
options.signal.addEventListener("abort", listener, {
__proto__: null,
[kResistStopPropagation]: true,
});
watcher.once("close", () => {
options.signal.removeEventListener("abort", listener);
});
}
}
return watcher;
}
const statWatchers = new Map();
/**
* Watches for changes on `filename`.
* @param {string | Buffer | URL} filename
* @param {{
* bigint?: boolean;
* persistent?: boolean;
* interval?: number;
* }} [options]
* @param {(
* current?: Stats,
* previous?: Stats
* ) => any} listener
* @returns {watchers.StatWatcher}
*/
function watchFile(filename, options, listener) {
filename = getValidatedPath(filename);
filename = pathModule.resolve(filename);
let stat;
if (options === null || typeof options !== "object") {
listener = options;
options = null;
}
options = {
// Poll interval in milliseconds. 5007 is what libev used to use. It's
// a little on the slow side but let's stick with it for now to keep
// behavioral changes to a minimum.
interval: 5007,
persistent: true,
...options,
};
validateFunction(listener, "listener");
stat = statWatchers.get(filename);
const watchers = __hoisted_internal_fs_watchers__;
if (stat === undefined) {
stat = new watchers.StatWatcher(options.bigint);
stat[watchers.kFSStatWatcherStart](
filename,
options.persistent,
options.interval,
);
statWatchers.set(filename, stat);
} else {
stat[watchers.kFSStatWatcherAddOrCleanRef]("add");
}
stat.addListener("change", listener);
return stat;
}
/**
* Stops watching for changes on `filename`.
* @param {string | Buffer | URL} filename
* @param {() => any} [listener]
* @returns {void}
*/
function unwatchFile(filename, listener) {
filename = getValidatedPath(filename);
filename = pathModule.resolve(filename);
const stat = statWatchers.get(filename);
if (stat === undefined) return;
const watchers = __hoisted_internal_fs_watchers__;
if (typeof listener === "function") {
const beforeListenerCount = stat.listenerCount("change");
stat.removeListener("change", listener);
if (stat.listenerCount("change") < beforeListenerCount)
stat[watchers.kFSStatWatcherAddOrCleanRef]("clean");
} else {
stat.removeAllListeners("change");
stat[watchers.kFSStatWatcherAddOrCleanRef]("cleanAll");
}
if (stat.listenerCount("change") === 0) {
stat.stop();
statWatchers.delete(filename);
}
}
let splitRoot;
if (isWindows) {
// Regex to find the device root on Windows (e.g. 'c:\\'), including trailing
// slash.
const splitRootRe = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/][^\\/]+)?[\\/]*/;
splitRoot = function splitRoot(str) {
return SideEffectFreeRegExpPrototypeExec(splitRootRe, str)[0];
};
} else {
splitRoot = function splitRoot(str) {
for (let i = 0; i < str.length; ++i) {
if (String.prototype.charCodeAt.call(str, i) !== CHAR_FORWARD_SLASH)
return String.prototype.slice.call(str, 0, i);
}
return str;
};
}
function encodeRealpathResult(result, options) {
if (!options || !options.encoding || options.encoding === "utf8")
return result;
const asBuffer = Buffer.from(result);
if (options.encoding === "buffer") {
return asBuffer;
}
return asBuffer.toString(options.encoding);
}
// Finds the next portion of a (partial) path, up to the next path delimiter
let nextPart;
if (isWindows) {
nextPart = function nextPart(p, i) {
for (; i < p.length; ++i) {
const ch = String.prototype.charCodeAt.call(p, i);
// Check for a separator character
if (ch === CHAR_BACKWARD_SLASH || ch === CHAR_FORWARD_SLASH) return i;
}
return -1;
};
} else {
nextPart = function nextPart(p, i) {
return String.prototype.indexOf.call(p, "/", i);
};
}
/**
* Returns the resolved pathname.
* @param {string | Buffer | URL} p
* @param {string | { encoding?: string | null; }} [options]
* @returns {string | Buffer}
*/
function realpathSync(p, options) {
options = getOptions(options);
p = toPathIfFileURL(p);
if (typeof p !== "string") {
p += "";
}
validatePath(p);
p = pathModule.resolve(p);
const cache = options[realpathCacheKey];
const maybeCachedResult = cache?.get(p);
if (maybeCachedResult) {
return maybeCachedResult;
}
const seenLinks = new Map();
const knownHard = new Set();
const original = p;
// Current character position in p
let pos;
// The partial path so far, including a trailing slash if any
let current;
// The partial path without a trailing slash (except when pointing at a root)
let base;
// The partial path scanned in the previous round, with slash
let previous;
// Skip over roots
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows) {
const out = binding.lstat(
base,
false,
undefined,
true /* throwIfNoEntry */,
);
if (out === undefined) {
return;
}
knownHard.add(base);
}
// Walk down the path, swapping out linked path parts for their real
// values
// NB: p.length changes.
while (pos < p.length) {
// find the next part
const result = nextPart(p, pos);
previous = current;
if (result === -1) {
const last = String.prototype.slice.call(p, pos);
current += last;
base = previous + last;
pos = p.length;
} else {
current += String.prototype.slice.call(p, pos, result + 1);
base = previous + String.prototype.slice.call(p, pos, result);
pos = result + 1;
}
// Continue if not a symlink, break if a pipe/socket
if (knownHard.has(base) || cache?.get(base) === base) {
if (isFileType(statValues, S_IFIFO) || isFileType(statValues, S_IFSOCK)) {
break;
}
continue;
}
let resolvedLink;
const maybeCachedResolved = cache?.get(base);
if (maybeCachedResolved) {
resolvedLink = maybeCachedResolved;
} else {
// Use stats array directly to avoid creating an fs.Stats instance just
// for our internal use.
const stats = binding.lstat(
base,
true,
undefined,
true /* throwIfNoEntry */,
);
if (stats === undefined) {
return;
}
if (!isFileType(stats, S_IFLNK)) {
knownHard.add(base);
cache?.set(base, base);
continue;
}
// Read the link if it wasn't read before
// dev/ino always return 0 on windows, so skip the check.
let linkTarget = null;
let id;
if (!isWindows) {
const dev = BigInt.prototype.toString.call(stats[0], 32);
const ino = BigInt.prototype.toString.call(stats[7], 32);
id = `${dev}:${ino}`;
if (seenLinks.has(id)) {
linkTarget = seenLinks.get(id);
}
}
if (linkTarget === null) {
binding.stat(base, false, undefined, true);
linkTarget = binding.readlink(base, undefined);
}
resolvedLink = pathModule.resolve(previous, linkTarget);
cache?.set(base, resolvedLink);
if (!isWindows) seenLinks.set(id, linkTarget);
}
// Resolve the link, then start over
p = pathModule.resolve(resolvedLink, String.prototype.slice.call(p, pos));
// Skip over roots
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard.has(base)) {
const out = binding.lstat(
base,
false,
undefined,
true /* throwIfNoEntry */,
);
if (out === undefined) {
return;
}
knownHard.add(base);
}
}
cache?.set(original, p);
return encodeRealpathResult(p, options);
}
/**
* Returns the resolved pathname.
* @param {string | Buffer | URL} path
* @param {string | { encoding?: string; }} [options]
* @returns {string | Buffer}
*/
realpathSync.native = (path, options) => {
options = getOptions(options);
return binding.realpath(getValidatedPath(path), options.encoding);
};
/**
* Asynchronously computes the canonical pathname by
* resolving `.`, `..` and symbolic links.
* @param {string | Buffer | URL} p
* @param {string | { encoding?: string; }} [options]
* @param {(
* err?: Error,
* resolvedPath?: string | Buffer
* ) => any} callback
* @returns {void}
*/
function realpath(p, options, callback) {
if (typeof options === "function") {
callback = options;
} else {
validateFunction(callback, "cb");
}
options = getOptions(options);
p = toPathIfFileURL(p);
if (typeof p !== "string") {
p += "";
}
validatePath(p);
p = pathModule.resolve(p);
const seenLinks = new Map();
const knownHard = new Set();
// Current character position in p
let pos;
// The partial path so far, including a trailing slash if any
let current;
// The partial path without a trailing slash (except when pointing at a root)
let base;
// The partial path scanned in the previous round, with slash
let previous;
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard.has(base)) {
fs.lstat(base, (err) => {
if (err) return callback(err);
knownHard.add(base);
LOOP();
});
} else {
process.nextTick(LOOP);
}
// Walk down the path, swapping out linked path parts for their real
// values
function LOOP() {
// Stop if scanned past end of path
if (pos >= p.length) {
return callback(null, encodeRealpathResult(p, options));
}
// find the next part
const result = nextPart(p, pos);
previous = current;
if (result === -1) {
const last = String.prototype.slice.call(p, pos);
current += last;
base = previous + last;
pos = p.length;
} else {
current += String.prototype.slice.call(p, pos, result + 1);
base = previous + String.prototype.slice.call(p, pos, result);
pos = result + 1;
}
// Continue if not a symlink, break if a pipe/socket
if (knownHard.has(base)) {
if (isFileType(statValues, S_IFIFO) || isFileType(statValues, S_IFSOCK)) {
return callback(null, encodeRealpathResult(p, options));
}
return process.nextTick(LOOP);
}
return fs.lstat(base, { bigint: true }, gotStat);
}
function gotStat(err, stats) {
if (err) return callback(err);
// If not a symlink, skip to the next path part
if (!stats.isSymbolicLink()) {
knownHard.add(base);
return process.nextTick(LOOP);
}
// Stat & read the link if not read before.
// Call `gotTarget()` as soon as the link target is known.
// `dev`/`ino` always return 0 on windows, so skip the check.
let id;
if (!isWindows) {
const dev = BigInt.prototype.toString.call(stats.dev, 32);
const ino = BigInt.prototype.toString.call(stats.ino, 32);
id = `${dev}:${ino}`;
if (seenLinks.has(id)) {
return gotTarget(null, seenLinks.get(id));
}
}
fs.stat(base, (err) => {
if (err) return callback(err);
fs.readlink(base, (err, target) => {
if (!isWindows) seenLinks.set(id, target);
gotTarget(err, target);
});
});
}
function gotTarget(err, target) {
if (err) return callback(err);
gotResolvedLink(pathModule.resolve(previous, target));
}
function gotResolvedLink(resolvedLink) {
// Resolve the link, then start over
p = pathModule.resolve(resolvedLink, String.prototype.slice.call(p, pos));
current = base = splitRoot(p);
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard.has(base)) {
fs.lstat(base, (err) => {
if (err) return callback(err);
knownHard.add(base);
LOOP();
});
} else {
process.nextTick(LOOP);
}
}
}
/**
* Asynchronously computes the canonical pathname by
* resolving `.`, `..` and symbolic links.
* @param {string | Buffer | URL} path
* @param {string | { encoding?: string; }} [options]
* @param {(
* err?: Error,
* resolvedPath?: string | Buffer
* ) => any} callback
* @returns {void}
*/
realpath.native = (path, options, callback) => {
callback = makeCallback(callback || options);
options = getOptions(options);
path = getValidatedPath(path);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.realpath(path, options.encoding, req);
};
/**
* Creates a unique temporary directory.
* @param {string | Buffer | URL} prefix
* @param {string | { encoding?: string; }} [options]
* @param {(
* err?: Error,
* directory?: string
* ) => any} callback
* @returns {void}
*/
function mkdtemp(prefix, options, callback) {
callback = makeCallback(typeof options === "function" ? options : callback);
options = getOptions(options);
prefix = getValidatedPath(prefix, "prefix");
warnOnNonPortableTemplate(prefix);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.mkdtemp(prefix, options.encoding, req);
}
/**
* Synchronously creates a unique temporary directory.
* @param {string | Buffer | URL} prefix
* @param {string | { encoding?: string; }} [options]
* @returns {string}
*/
function mkdtempSync(prefix, options) {
options = getOptions(options);
prefix = getValidatedPath(prefix, "prefix");
warnOnNonPortableTemplate(prefix);
return binding.mkdtemp(prefix, options.encoding);
}
/**
* Asynchronously copies `src` to `dest`. By
* default, `dest` is overwritten if it already exists.
* @param {string | Buffer | URL} src
* @param {string | Buffer | URL} dest
* @param {number} [mode]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function copyFile(src, dest, mode, callback) {
if (typeof mode === "function") {
callback = mode;
mode = 0;
}
src = getValidatedPath(src, "src");
dest = getValidatedPath(dest, "dest");
callback = makeCallback(callback);
const req = new FSReqCallback();
req.oncomplete = callback;
binding.copyFile(src, dest, mode, req);
}
/**
* Synchronously copies `src` to `dest`. By
* default, `dest` is overwritten if it already exists.
* @param {string | Buffer | URL} src
* @param {string | Buffer | URL} dest
* @param {number} [mode]
* @returns {void}
*/
function copyFileSync(src, dest, mode) {
binding.copyFile(
getValidatedPath(src, "src"),
getValidatedPath(dest, "dest"),
mode,
);
}
/**
* Asynchronously copies `src` to `dest`. `src` can be a file, directory, or
* symlink. The contents of directories will be copied recursively.
* @param {string | URL} src
* @param {string | URL} dest
* @param {object} [options]
* @param {(err?: Error) => any} callback
* @returns {void}
*/
function cp(src, dest, options, callback) {
if (typeof options === "function") {
callback = options;
options = undefined;
}
callback = makeCallback(callback);
options = validateCpOptions(options);
src = getValidatedPath(src, "src");
dest = getValidatedPath(dest, "dest");
lazyLoadCp();
cpFn(src, dest, options, callback);
}
/**
* Synchronously copies `src` to `dest`. `src` can be a file, directory, or
* symlink. The contents of directories will be copied recursively.
* @param {string | URL} src
* @param {string | URL} dest
* @param {object} [options]
* @returns {void}
*/
function cpSync(src, dest, options) {
options = validateCpOptions(options);
src = getValidatedPath(src, "src");
dest = getValidatedPath(dest, "dest");
lazyLoadCp();
cpSyncFn(src, dest, options);
}
function lazyLoadStreams() {
if (!ReadStream) {
({ ReadStream, WriteStream } = __hoisted_internal_fs_streams__);
FileReadStream = ReadStream;
FileWriteStream = WriteStream;
}
}
/**
* Creates a readable stream with a default `highWaterMark`
* of 64 KiB.
* @param {string | Buffer | URL} path
* @param {string | {
* flags?: string;
* encoding?: string;
* fd?: number | FileHandle;
* mode?: number;
* autoClose?: boolean;
* emitClose?: boolean;
* start: number;
* end?: number;
* highWaterMark?: number;
* fs?: object | null;
* signal?: AbortSignal | null;
* }} [options]
* @returns {ReadStream}
*/
function createReadStream(path, options) {
lazyLoadStreams();
return new ReadStream(path, options);
}
/**
* Creates a write stream.
* @param {string | Buffer | URL} path
* @param {string | {
* flags?: string;
* encoding?: string;
* fd?: number | FileHandle;
* mode?: number;
* autoClose?: boolean;
* emitClose?: boolean;
* start: number;
* fs?: object | null;
* signal?: AbortSignal | null;
* highWaterMark?: number;
* flush?: boolean;
* }} [options]
* @returns {WriteStream}
*/
function createWriteStream(path, options) {
lazyLoadStreams();
return new WriteStream(path, options);
}
const lazyGlob = getLazy(() => __hoisted_internal_fs_glob__.Glob);
function glob(pattern, options, callback) {
emitExperimentalWarning("glob");
if (typeof options === "function") {
callback = options;
options = undefined;
}
callback = makeCallback(callback);
const Glob = lazyGlob();
// TODO: Use iterator helpers when available
(async () => {
try {
const res = [];
for await (const entry of new Glob(pattern, options).glob()) {
Array.prototype.push.call(res, entry);
}
callback(null, res);
} catch (err) {
callback(err);
}
})();
}
function globSync(pattern, options) {
emitExperimentalWarning("globSync");
const Glob = lazyGlob();
return new Glob(pattern, options).globSync();
}
export default fs = {
appendFile,
appendFileSync,
access,
accessSync,
chown,
chownSync,
chmod,
chmodSync,
close,
closeSync,
copyFile,
copyFileSync,
cp,
cpSync,
createReadStream,
createWriteStream,
exists,
existsSync,
fchown,
fchownSync,
fchmod,
fchmodSync,
fdatasync,
fdatasyncSync,
fstat,
fstatSync,
fsync,
fsyncSync,
ftruncate,
ftruncateSync,
futimes,
futimesSync,
glob,
globSync,
lchown,
lchownSync,
lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
link,
linkSync,
lstat,
lstatSync,
lutimes,
lutimesSync,
mkdir,
mkdirSync,
mkdtemp,
mkdtempSync,
open,
openSync,
openAsBlob,
readdir,
readdirSync,
read,
readSync,
readv,
readvSync,
readFile,
readFileSync,
readlink,
readlinkSync,
realpath,
realpathSync,
rename,
renameSync,
rm,
rmSync,
rmdir,
rmdirSync,
stat,
statfs,
statSync,
statfsSync,
symlink,
symlinkSync,
truncate,
truncateSync,
unwatchFile,
unlink,
unlinkSync,
utimes,
utimesSync,
watch,
watchFile,
writeFile,
writeFileSync,
write,
writeSync,
writev,
writevSync,
Dirent,
Stats,
get ReadStream() {
lazyLoadStreams();
return ReadStream;
},
set ReadStream(val) {
ReadStream = val;
},
get WriteStream() {
lazyLoadStreams();
return WriteStream;
},
set WriteStream(val) {
WriteStream = val;
},
// Legacy names... these have to be separate because of how graceful-fs
// (and possibly other) modules monkey patch the values.
get FileReadStream() {
lazyLoadStreams();
return FileReadStream;
},
set FileReadStream(val) {
FileReadStream = val;
},
get FileWriteStream() {
lazyLoadStreams();
return FileWriteStream;
},
set FileWriteStream(val) {
FileWriteStream = val;
},
// For tests
_toUnixTimestamp: toUnixTimestamp,
};
defineLazyProperties(fs, "internal/fs/dir", ["Dir", "opendir", "opendirSync"]);
Object.defineProperties(fs, {
F_OK: { __proto__: null, enumerable: true, value: F_OK || 0 },
R_OK: { __proto__: null, enumerable: true, value: R_OK || 0 },
W_OK: { __proto__: null, enumerable: true, value: W_OK || 0 },
X_OK: { __proto__: null, enumerable: true, value: X_OK || 0 },
constants: {
__proto__: null,
configurable: false,
enumerable: true,
value: constants,
},
promises: {
__proto__: null,
configurable: true,
enumerable: true,
get() {
promises ??= __hoisted_internal_fs_promises__.exports;
return promises;
},
},
});
export { appendFile };
export { appendFileSync };
export { access };
export { accessSync };
export { chown };
export { chownSync };
export { chmod };
export { chmodSync };
export { close };
export { closeSync };
export { copyFile };
export { copyFileSync };
export { cp };
export { cpSync };
export { createReadStream };
export { createWriteStream };
export { exists };
export { existsSync };
export { fchown };
export { fchownSync };
export { fchmod };
export { fchmodSync };
export { fdatasync };
export { fdatasyncSync };
export { fstat };
export { fstatSync };
export { fsync };
export { fsyncSync };
export { ftruncate };
export { ftruncateSync };
export { futimes };
export { futimesSync };
export { glob };
export { globSync };
export { lchown };
export { lchownSync };
export { link };
export { linkSync };
export { lstat };
export { lstatSync };
export { lutimes };
export { lutimesSync };
export { mkdir };
export { mkdirSync };
export { mkdtemp };
export { mkdtempSync };
export { open };
export { openSync };
export { openAsBlob };
export { readdir };
export { readdirSync };
export { read };
export { readSync };
export { readv };
export { readvSync };
export { readFile };
export { readFileSync };
export { readlink };
export { readlinkSync };
export { realpath };
export { realpathSync };
export { rename };
export { renameSync };
export { rm };
export { rmSync };
export { rmdir };
export { rmdirSync };
export { stat };
export { statfs };
export { statSync };
export { statfsSync };
export { symlink };
export { symlinkSync };
export { truncate };
export { truncateSync };
export { unwatchFile };
export { unlink };
export { unlinkSync };
export { utimes };
export { utimesSync };
export { watch };
export { watchFile };
export { writeFile };
export { writeFileSync };
export { write };
export { writeSync };
export { writev };
export { writevSync };
export const _toUnixTimestamp = toUnixTimestamp;