Jofthomas's picture
Jofthomas HF staff
Upload 4781 files
5c2ed06 verified
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var fs_exports = {};
__export(fs_exports, {
FS: () => FS,
FSPath: () => FSPath
});
module.exports = __toCommonJS(fs_exports);
var fs = __toESM(require("fs"));
var pathModule = __toESM(require("path"));
var import_streams = require("./streams");
/**
* FS
* Pokemon Showdown - http://pokemonshowdown.com/
*
* An abstraction layer around Node's filesystem.
*
* Advantages:
* - write() etc do nothing in unit tests
* - paths are always relative to PS's base directory
* - Promises (seriously wtf Node Core what are you thinking)
* - PS-style API: FS("foo.txt").write("bar") for easier argument order
* - mkdirp
*
* FS is used nearly everywhere, but exceptions include:
* - crashlogger.js - in case the crash is in here
* - repl.js - which use Unix sockets out of this file's scope
* - launch script - happens before modules are loaded
* - sim/ - intended to be self-contained
*
* @author Guangcong Luo <guangcongluo@gmail.com>
* @license MIT
*/
const DIST = `${pathModule.sep}dist${pathModule.sep}`;
const ROOT_PATH = pathModule.resolve(__dirname, __dirname.includes(DIST) ? ".." : "", "..");
if (!global.__fsState) {
global.__fsState = {
pendingUpdates: /* @__PURE__ */ new Map()
};
}
class FSPath {
constructor(path) {
this.path = pathModule.resolve(ROOT_PATH, path);
}
parentDir() {
return new FSPath(pathModule.dirname(this.path));
}
read(options = "utf8") {
if (typeof options !== "string" && options.encoding === void 0) {
options.encoding = "utf8";
}
return new Promise((resolve, reject) => {
fs.readFile(this.path, options, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
readSync(options = "utf8") {
if (typeof options !== "string" && options.encoding === void 0) {
options.encoding = "utf8";
}
return fs.readFileSync(this.path, options);
}
readBuffer(options = {}) {
return new Promise((resolve, reject) => {
fs.readFile(this.path, options, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
readBufferSync(options = {}) {
return fs.readFileSync(this.path, options);
}
exists() {
return new Promise((resolve) => {
fs.exists(this.path, (exists) => {
resolve(exists);
});
});
}
existsSync() {
return fs.existsSync(this.path);
}
readIfExists() {
return new Promise((resolve, reject) => {
fs.readFile(this.path, "utf8", (err, data) => {
if (err && err.code === "ENOENT")
return resolve("");
err ? reject(err) : resolve(data);
});
});
}
readIfExistsSync() {
try {
return fs.readFileSync(this.path, "utf8");
} catch (err) {
if (err.code !== "ENOENT")
throw err;
}
return "";
}
write(data, options = {}) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.writeFile(this.path, data, options, (err) => {
err ? reject(err) : resolve();
});
});
}
writeSync(data, options = {}) {
if (global.Config?.nofswriting)
return;
return fs.writeFileSync(this.path, data, options);
}
/**
* Writes to a new file before renaming to replace an old file. If
* the process crashes while writing, the old file won't be lost.
* Does not protect against simultaneous writing; use writeUpdate
* for that.
*/
async safeWrite(data, options = {}) {
await FS(this.path + ".NEW").write(data, options);
await FS(this.path + ".NEW").rename(this.path);
}
safeWriteSync(data, options = {}) {
FS(this.path + ".NEW").writeSync(data, options);
FS(this.path + ".NEW").renameSync(this.path);
}
/**
* Safest way to update a file with in-memory state. Pass a callback
* that fetches the data to be written. It will write an update,
* avoiding race conditions. The callback may not necessarily be
* called, if `writeUpdate` is called many times in a short period.
*
* `options.throttle`, if it exists, will make sure updates are not
* written more than once every `options.throttle` milliseconds.
*
* No synchronous version because there's no risk of race conditions
* with synchronous code; just use `safeWriteSync`.
*/
writeUpdate(dataFetcher, options = {}) {
if (global.Config?.nofswriting)
return;
const pendingUpdate = __fsState.pendingUpdates.get(this.path);
const throttleTime = options.throttle ? Date.now() + options.throttle : 0;
if (pendingUpdate) {
pendingUpdate.pendingDataFetcher = dataFetcher;
pendingUpdate.pendingOptions = options;
if (pendingUpdate.throttleTimer && throttleTime < pendingUpdate.throttleTime) {
pendingUpdate.throttleTime = throttleTime;
clearTimeout(pendingUpdate.throttleTimer);
pendingUpdate.throttleTimer = setTimeout(() => this.checkNextUpdate(), throttleTime - Date.now());
}
return;
}
if (!throttleTime) {
this.writeUpdateNow(dataFetcher, options);
return;
}
const update = {
isWriting: false,
pendingDataFetcher: dataFetcher,
pendingOptions: options,
throttleTime,
throttleTimer: setTimeout(() => this.checkNextUpdate(), throttleTime - Date.now())
};
__fsState.pendingUpdates.set(this.path, update);
}
writeUpdateNow(dataFetcher, options) {
const throttleTime = options.throttle ? Date.now() + options.throttle : 0;
const update = {
isWriting: true,
pendingDataFetcher: null,
pendingOptions: null,
throttleTime,
throttleTimer: null
};
__fsState.pendingUpdates.set(this.path, update);
void this.safeWrite(dataFetcher(), options).then(() => this.finishUpdate());
}
checkNextUpdate() {
const pendingUpdate = __fsState.pendingUpdates.get(this.path);
if (!pendingUpdate)
throw new Error(`FS: Pending update not found`);
if (pendingUpdate.isWriting)
throw new Error(`FS: Conflicting update`);
const { pendingDataFetcher: dataFetcher, pendingOptions: options } = pendingUpdate;
if (!dataFetcher || !options) {
__fsState.pendingUpdates.delete(this.path);
return;
}
this.writeUpdateNow(dataFetcher, options);
}
finishUpdate() {
const pendingUpdate = __fsState.pendingUpdates.get(this.path);
if (!pendingUpdate)
throw new Error(`FS: Pending update not found`);
if (!pendingUpdate.isWriting)
throw new Error(`FS: Conflicting update`);
pendingUpdate.isWriting = false;
const throttleTime = pendingUpdate.throttleTime;
if (!throttleTime || throttleTime < Date.now()) {
this.checkNextUpdate();
return;
}
pendingUpdate.throttleTimer = setTimeout(() => this.checkNextUpdate(), throttleTime - Date.now());
}
append(data, options = {}) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.appendFile(this.path, data, options, (err) => {
err ? reject(err) : resolve();
});
});
}
appendSync(data, options = {}) {
if (global.Config?.nofswriting)
return;
return fs.appendFileSync(this.path, data, options);
}
symlinkTo(target) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.symlink(target, this.path, (err) => {
err ? reject(err) : resolve();
});
});
}
symlinkToSync(target) {
if (global.Config?.nofswriting)
return;
return fs.symlinkSync(target, this.path);
}
copyFile(dest) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.copyFile(this.path, dest, (err) => {
err ? reject(err) : resolve();
});
});
}
rename(target) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.rename(this.path, target, (err) => {
err ? reject(err) : resolve();
});
});
}
renameSync(target) {
if (global.Config?.nofswriting)
return;
return fs.renameSync(this.path, target);
}
readdir() {
return new Promise((resolve, reject) => {
fs.readdir(this.path, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
readdirSync() {
return fs.readdirSync(this.path);
}
async readdirIfExists() {
if (await this.exists())
return this.readdir();
return Promise.resolve([]);
}
readdirIfExistsSync() {
if (this.existsSync())
return this.readdirSync();
return [];
}
createReadStream() {
return new FileReadStream(this.path);
}
createWriteStream(options = {}) {
if (global.Config?.nofswriting) {
return new import_streams.WriteStream({ write() {
} });
}
return new import_streams.WriteStream(fs.createWriteStream(this.path, options));
}
createAppendStream(options = {}) {
if (global.Config?.nofswriting) {
return new import_streams.WriteStream({ write() {
} });
}
options.flags = options.flags || "a";
return new import_streams.WriteStream(fs.createWriteStream(this.path, options));
}
unlinkIfExists() {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.unlink(this.path, (err) => {
if (err && err.code === "ENOENT")
return resolve();
err ? reject(err) : resolve();
});
});
}
unlinkIfExistsSync() {
if (global.Config?.nofswriting)
return;
try {
fs.unlinkSync(this.path);
} catch (err) {
if (err.code !== "ENOENT")
throw err;
}
}
async rmdir(recursive) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.rmdir(this.path, { recursive }, (err) => {
err ? reject(err) : resolve();
});
});
}
rmdirSync(recursive) {
if (global.Config?.nofswriting)
return;
return fs.rmdirSync(this.path, { recursive });
}
mkdir(mode = 493) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.mkdir(this.path, mode, (err) => {
err ? reject(err) : resolve();
});
});
}
mkdirSync(mode = 493) {
if (global.Config?.nofswriting)
return;
return fs.mkdirSync(this.path, mode);
}
mkdirIfNonexistent(mode = 493) {
if (global.Config?.nofswriting)
return Promise.resolve();
return new Promise((resolve, reject) => {
fs.mkdir(this.path, mode, (err) => {
if (err && err.code === "EEXIST")
return resolve();
err ? reject(err) : resolve();
});
});
}
mkdirIfNonexistentSync(mode = 493) {
if (global.Config?.nofswriting)
return;
try {
fs.mkdirSync(this.path, mode);
} catch (err) {
if (err.code !== "EEXIST")
throw err;
}
}
/**
* Creates the directory (and any parent directories if necessary).
* Does not throw if the directory already exists.
*/
async mkdirp(mode = 493) {
try {
await this.mkdirIfNonexistent(mode);
} catch (err) {
if (err.code !== "ENOENT")
throw err;
await this.parentDir().mkdirp(mode);
await this.mkdirIfNonexistent(mode);
}
}
/**
* Creates the directory (and any parent directories if necessary).
* Does not throw if the directory already exists. Synchronous.
*/
mkdirpSync(mode = 493) {
try {
this.mkdirIfNonexistentSync(mode);
} catch (err) {
if (err.code !== "ENOENT")
throw err;
this.parentDir().mkdirpSync(mode);
this.mkdirIfNonexistentSync(mode);
}
}
/** Calls the callback if the file is modified. */
onModify(callback) {
fs.watchFile(this.path, (curr, prev) => {
if (curr.mtime > prev.mtime)
return callback();
});
}
/** Clears callbacks added with onModify(). */
unwatch() {
fs.unwatchFile(this.path);
}
async isFile() {
return new Promise((resolve, reject) => {
fs.stat(this.path, (err, stats) => {
err ? reject(err) : resolve(stats.isFile());
});
});
}
isFileSync() {
return fs.statSync(this.path).isFile();
}
async isDirectory() {
return new Promise((resolve, reject) => {
fs.stat(this.path, (err, stats) => {
err ? reject(err) : resolve(stats.isDirectory());
});
});
}
isDirectorySync() {
return fs.statSync(this.path).isDirectory();
}
async realpath() {
return new Promise((resolve, reject) => {
fs.realpath(this.path, (err, path) => {
err ? reject(err) : resolve(path);
});
});
}
realpathSync() {
return fs.realpathSync(this.path);
}
}
class FileReadStream extends import_streams.ReadStream {
constructor(file) {
super();
this.fd = new Promise((resolve, reject) => {
fs.open(file, "r", (err, fd) => err ? reject(err) : resolve(fd));
});
this.atEOF = false;
}
_read(size = 16384) {
return new Promise((resolve, reject) => {
if (this.atEOF)
return void resolve();
this.ensureCapacity(size);
void this.fd.then((fd) => {
fs.read(fd, this.buf, this.bufEnd, size, null, (err, bytesRead, buf) => {
if (err)
return reject(err);
if (!bytesRead) {
this.atEOF = true;
this.resolvePush();
return resolve();
}
this.bufEnd += bytesRead;
this.resolvePush();
resolve();
});
});
});
}
_destroy() {
return new Promise((resolve) => {
void this.fd.then((fd) => {
fs.close(fd, () => resolve());
});
});
}
}
function getFs(path) {
return new FSPath(path);
}
const FS = Object.assign(getFs, {
FileReadStream,
FSPath,
ROOT_PATH
});
//# sourceMappingURL=fs.js.map