minigame-std
Version:
Cross-platform standard library for WeChat minigame and web browsers with unified APIs for crypto, fs, fetch, storage, and more.
1,690 lines (1,689 loc) • 56.5 kB
JavaScript
import * as opfs from "happy-opfs";
import { NOTHING_TO_ZIP_ERROR, NOT_FOUND_ERROR, ROOT_DIR, appendFile as appendFile$1, appendFileSync as appendFileSync$1, copy as copy$1, copySync as copySync$1, downloadFile as downloadFile$1, emptyDir as emptyDir$1, emptyDirSync as emptyDirSync$1, exists as exists$1, existsSync as existsSync$1, isFileHandle, isFileHandleLike, mkdir as mkdir$1, mkdirSync as mkdirSync$1, move as move$1, moveSync as moveSync$1, readDir as readDir$1, readDirSync as readDirSync$1, readFile as readFile$1, readFileSync as readFileSync$1, readJsonFile as readJsonFile$1, readJsonFileSync as readJsonFileSync$1, readTextFile as readTextFile$1, readTextFileSync as readTextFileSync$1, remove as remove$1, removeSync as removeSync$1, stat as stat$1, statSync as statSync$1, unzip as unzip$1, unzipFromUrl as unzipFromUrl$1, unzipSync as unzipSync$1, uploadFile as uploadFile$1, writeFile as writeFile$1, writeFileSync as writeFileSync$1, writeJsonFile as writeJsonFile$1, writeJsonFileSync as writeJsonFileSync$1, zip as zip$1, zipFromUrl as zipFromUrl$1, zipSync as zipSync$1 } from "happy-opfs";
import { Err, Lazy, Ok, RESULT_FALSE, RESULT_VOID, tryAsyncResult, tryResult } from "happy-rusty";
import { ABORT_ERROR, FetchError } from "@happy-ts/fetch-t";
import { unzipSync as unzipSync$2, zipSync as zipSync$2 } from "fflate/browser";
import { Future } from "tiny-future";
import { bufferSourceToAb, createFailedFetchTask, miniGameFailureToError, validateSafeUrl } from "./_internal.mjs";
import { SEPARATOR, basename, dirname, normalize } from "./path.mjs";
import { asyncResultify } from "./utils.mjs";
//#region src/macros/env.ts
/**
* 如果在小游戏环境中返回 true,否则返回 false。
*/
const IS_MINA = __MINIGAME_STD_MINA__;
//#endregion
//#region src/std/fs/mina_fs_shared.ts
/**
* @internal
* 同步/异步的公共代码。
*/
/**
* 小游戏文件系统管理器实例。
*
*/
const fs = /*#__PURE__*/ Lazy(() => wx.getFileSystemManager());
/**
* 用户数据根目录,`wxfile://usr` 或 `http://usr`。
*
*/
const usrPath = /*#__PURE__*/ Lazy(() => wx.env.USER_DATA_PATH);
/**
* 根路径,`wxfile://` 或 `http://`。
*
*/
const rootPath = /*#__PURE__*/ Lazy(() => {
return `${usrPath.force().split("://")[0]}://`;
});
const EMPTY_BYTES = /*#__PURE__*/ new Uint8Array(0);
/**
* 获取小游戏文件系统管理器实例。
* @returns 文件系统管理器实例。
*/
function getFs() {
return fs.force();
}
/**
* 获取文件系统的根路径。
* @returns 文件系统的根路径。
*/
function getUsrPath() {
return usrPath.force();
}
/**
* 验证并标准化路径,返回绝对路径。
*
* 支持两种输入格式:
* 1. 完整路径:以 `wxfile://` 或 `http://` 开头(如 `wxfile://usr/test`)
* 2. 相对路径:以 `/` 开头(如 `/test`),会自动拼接 `wx.env.USER_DATA_PATH`
*
* @param path - 待验证的路径。
* @returns 验证成功返回标准化后的绝对路径,失败返回错误信息。
*/
function validateAbsolutePath(path) {
const typeError = validatePathType(path);
if (typeError) return typeError;
let isFullPath = false;
if (path.startsWith(rootPath.force())) {
isFullPath = true;
path = path.slice(rootPath.force().length);
if (!path) return Err(/* @__PURE__ */ new Error("Path must not be root directory"));
}
const normalized = normalize(path);
path = normalized.length > 1 && normalized[normalized.length - 1] === ROOT_DIR ? normalized.slice(0, -1) : normalized;
if (isFullPath) {
if (path === ROOT_DIR) return Err(/* @__PURE__ */ new Error("Path must not be root directory"));
if (path[0] === ROOT_DIR) path = path.slice(1);
return Ok(rootPath.force() + path);
}
if (path[0] !== ROOT_DIR) return Err(/* @__PURE__ */ new Error(`Path must be absolute (start with '/'): '${path}'`));
return Ok(usrPath.force() + path);
}
/**
* 验证可读路径(用于只读操作:readFile、stat、readDir)。
*
* 支持三种输入格式:
* 1. 完整路径:以 `wxfile://` 或 `http://` 开头(如 `wxfile://usr/test`)
* 2. 用户数据相对路径:以 `/` 开头(如 `/test`),会自动拼接 `wx.env.USER_DATA_PATH`
* 3. 代码包路径:不以 `./`、`../` 开头的相对路径(如 `images/logo.png`),直接返回原路径
*
* @param path - 待验证的路径。
* @returns 验证成功返回标准化后的路径,失败返回错误信息。
*/
function validateReadablePath(path) {
const typeError = validatePathType(path);
if (typeError) return typeError;
if (path.startsWith(rootPath.force()) || path.startsWith(ROOT_DIR)) return validateAbsolutePath(path);
if (path.startsWith("./") || path.startsWith("../")) return Err(/* @__PURE__ */ new Error(`Invalid path: '${path}'. Code package paths must not start with './' or '../'`));
return Ok(normalize(path));
}
/**
* 验证提供的 ExistsOptions 是否有效。
* `isDirectory` 和 `isFile` 不能同时为 `true`。
*
* @param options - 要验证的 ExistsOptions。
* @returns 表示成功或错误的 VoidIOResult。
*/
function validateExistsOptions(options) {
const { isDirectory = false, isFile = false } = options ?? {};
return isDirectory && isFile ? Err(/* @__PURE__ */ new Error("isDirectory and isFile cannot both be true")) : RESULT_VOID;
}
/**
* 判断错误是否为 `NotFoundError`。
* @param error - 要检查的错误。
* @returns 如果是 `NotFoundError` 返回 `true`,否则返回 `false`。
*/
function isNotFoundError(error) {
return error.name === NOT_FOUND_ERROR;
}
/**
* 将错误对象转换为 IOResult 类型。
* @typeParam T - Result 的 Ok 类型。
* @param error - IO 操作的错误对象, 可以是同步(Error)或者异步(WechatMinigame.FileError)的。
* @returns 转换后的 IOResult 对象。
*/
function fileErrorToResult(error) {
const err = miniGameFailureToError(error);
if (isNotFoundFileError(err)) err.name = NOT_FOUND_ERROR;
return Err(err);
}
/**
* 处理 `mkdir` 的错误。
*/
function fileErrorToMkdirResult(error) {
return isAlreadyExistsFileError(error) ? RESULT_VOID : fileErrorToResult(error);
}
/**
* 处理 `remove` 的错误。
*/
function fileErrorToRemoveResult(error) {
return isNotFoundFileError(error) ? RESULT_VOID : fileErrorToResult(error);
}
function createNothingToZipError() {
const error = /* @__PURE__ */ new Error("Nothing to zip");
error.name = NOTHING_TO_ZIP_ERROR;
return Err(error);
}
function createFileNotExistsError(filePath) {
return Err(/* @__PURE__ */ new Error(`Cannot append to non-existent file: ${filePath}`));
}
function createDirIsFileError(dirPath) {
return Err(/* @__PURE__ */ new Error(`Path already exists but is a file: ${dirPath}`));
}
/**
* 获取读取文件的编码。
* @returns 返回 `'utf8'` 或 `undefined`(读取二进制时不传 encoding)。
*/
function getReadFileEncoding(options) {
return options?.encoding === "utf8" ? "utf8" : void 0;
}
/**
* 获取写入文件的参数。
*/
function getWriteFileContents(contents) {
const isBin = typeof contents !== "string";
let data;
if (isBin) {
const result = tryResult(() => bufferSourceToAb(contents));
if (result.isErr()) return result.asErr();
data = result.unwrap();
} else data = contents;
return Ok({
data,
encoding: isBin ? void 0 : "utf8"
});
}
/**
* 获取 `exists` 的结果。
*
* 注意:Windows 和 Mac 平台 `stat` 代码包内不存在的文件时, 平台会误报成功并返回 `stats.size = -1`。
* 此处通过 `stats.size < 0` 判断并视为不存在, 以兼容该平台 bug。
*
* TODO(platform-wx): 待微信 Windows/Mac 平台修复该 stat 误报 bug 后, 移除 `stats.size < 0` 判断。
* 跟踪点: `mina_fs_shared.ts` getExistsResult。
*/
function getExistsResult(statResult, options) {
return statResult.map((stats) => {
const { isDirectory = false, isFile = false } = options ?? {};
return !(stats.size < 0 || isDirectory && stats.isFile() || isFile && stats.isDirectory());
}).orElse((err) => {
return isNotFoundError(err) ? RESULT_FALSE : statResult.asErr();
});
}
/**
* 判断文件/目录是否存在(不区分类型)。
*
* 相比 `stat` + `getExistsResult`,使用底层 `access` 接口开销更小,不会构造 `Stats` 对象。
* 当 `ExistsOptions` 不需要检查 `isDirectory`/`isFile` 时使用此实现。
*
* @param path - 文件或目录路径。
* @returns 存在返回 `true`,不存在返回 `false`,其他错误透传。
*/
async function accessExists(path) {
const pathRes = validateReadablePath(path);
if (pathRes.isErr()) return pathRes.asErr();
path = pathRes.unwrap();
return accessResultToExists(await asyncResultify(getFs().access)({ path }));
}
/**
* `accessExists` 的同步版本。
*
* @param path - 文件或目录路径。
* @returns 存在返回 `true`,不存在返回 `false`,其他错误透传。
*/
function accessExistsSync(path) {
const pathRes = validateReadablePath(path);
if (pathRes.isErr()) return pathRes.asErr();
path = pathRes.unwrap();
return accessResultToExists(tryResult(() => getFs().accessSync(path)));
}
/**
* 根据 `recursive` 不同标准化 `stat` 的结果(recursive=true 的时候开发者工具对于文件和空文件夹会返回单个 Stats)。
* - `recursive=false`: 返回单个 `Stats` 或 `FileStats[]`
* - `recursive=true`: 始终返回 `FileStats[]`,即使是单个文件或空目录
* - 如果是单个 `Stats`,包装成数组,path 设为 '' 表示当前项目
*/
function normalizeStats(statsOrFileStats, recursive) {
if (Array.isArray(statsOrFileStats)) return statsOrFileStats.map(({ path, stats }) => ({
path: path.replace(/^\/+/, ""),
stats
})).sort((a, b) => a.path.localeCompare(b.path));
return recursive ? [{
path: "",
stats: statsOrFileStats
}] : statsOrFileStats;
}
/**
* 标准化同步或异步的文件错误对象。
* @param error - IO 操作的错误对象, 可以是同步(Error)或者异步(WechatMinigame.FileError)的。
*/
function normalizeFileError(error) {
return error instanceof Error ? {
errCode: error.errno ?? 0,
errMsg: error.message
} : error;
}
/**
* 判断是否文件或者文件夹不存在。
* @param error - IO 操作的错误对象, 可以是同步(Error)或者异步(WechatMinigame.FileError)的。
*/
function isNotFoundFileError(error) {
const { errCode, errMsg } = normalizeFileError(error);
return errCode === 1300002 || errMsg.includes("no such file or directory");
}
/**
* 判断是否文件或者文件夹已存在。
* @param error - IO 操作的错误对象, 可以是同步(Error)或者异步(WechatMinigame.FileError)的。
*/
function isAlreadyExistsFileError(error) {
const { errCode, errMsg } = normalizeFileError(error);
return errCode === 1301005 || errMsg.includes("already exists");
}
/**
* 验证 path 是否为字符串类型。
* @param path - 待验证的路径。
* @returns 如果不是字符串返回错误,否则返回 undefined。
*/
function validatePathType(path) {
if (typeof path !== "string") return Err(/* @__PURE__ */ new TypeError(`Path must be a string but received ${typeof path}`));
}
/**
* 将 `access`/`accessSync` 调用的结果转换为存在性结果。
* - 成功 → `true`
* - NotFound → `false`
* - 其他错误 → 透传
*/
function accessResultToExists(accessRes) {
return accessRes.map(() => true).orElse((err) => {
const normalized = fileErrorToResult(err).unwrapErr();
return isNotFoundError(normalized) ? RESULT_FALSE : Err(normalized);
});
}
//#endregion
//#region src/std/fs/mina_fs_async.ts
/**
* @internal
* 小游戏平台的异步文件系统操作实现。
*/
/**
* 递归创建文件夹,相当于`mkdir -p`。
* @param dirPath - 需要创建的目录路径。
* @returns 创建操作的异步结果。
*/
async function mkdir$2(dirPath) {
const dirPathRes = validateAbsolutePath(dirPath);
if (dirPathRes.isErr()) return dirPathRes.asErr();
dirPath = dirPathRes.unwrap();
if (dirPath === getUsrPath()) return RESULT_VOID;
const statRes = await stat$2(dirPath);
if (statRes.isOk()) {
if (statRes.unwrap().isFile()) return createDirIsFileError(dirPath);
return RESULT_VOID;
}
return (await asyncResultify(getFs().mkdir)({
dirPath,
recursive: true
})).and(RESULT_VOID).orElse(fileErrorToMkdirResult);
}
/**
* 移动或重命名文件或目录。
* @param srcPath - 原路径。
* @param destPath - 新路径。
* @returns 移动操作的异步结果。
*/
async function move$2(srcPath, destPath) {
const srcPathRes = validateAbsolutePath(srcPath);
if (srcPathRes.isErr()) return srcPathRes.asErr();
srcPath = srcPathRes.unwrap();
const destPathRes = validateAbsolutePath(destPath);
if (destPathRes.isErr()) return destPathRes.asErr();
destPath = destPathRes.unwrap();
return (await asyncResultify(getFs().rename)({
oldPath: srcPath,
newPath: destPath
})).and(RESULT_VOID).orElse(fileErrorToResult);
}
/**
* 读取目录下的所有文件和子目录。
* @param dirPath - 目录路径。
* @returns 包含目录内容的字符串数组的异步结果。
*/
async function readDir$2(dirPath) {
const dirPathRes = validateReadablePath(dirPath);
if (dirPathRes.isErr()) return dirPathRes.asErr();
dirPath = dirPathRes.unwrap();
return (await asyncResultify(getFs().readdir)({ dirPath })).map((x) => x.files).orElse(fileErrorToResult);
}
/**
* 读取文件内容,可选地指定编码和返回类型。
* @template T - 返回内容的类型。
* @param filePath - 文件路径。
* @param options - 可选的读取选项。
* @returns 包含文件内容的异步结果。
*/
async function readFile$2(filePath, options) {
const filePathRes = validateReadablePath(filePath);
if (filePathRes.isErr()) return filePathRes.asErr();
filePath = filePathRes.unwrap();
const encoding = getReadFileEncoding(options);
return (await asyncResultify(getFs().readFile)({
filePath,
encoding
})).map((x) => {
const { data } = x;
return typeof data === "string" ? data : new Uint8Array(data);
}).orElse(fileErrorToResult);
}
/**
* 删除指定路径的文件或目录。
* @param path - 需要删除的文件或目录的路径。
* @returns 删除操作的异步结果。
*/
async function remove$2(path) {
const statRes = await stat$2(path);
if (statRes.isErr()) return isNotFoundError(statRes.unwrapErr()) ? RESULT_VOID : statRes.asErr();
path = validateAbsolutePath(path).unwrap();
return (await (statRes.unwrap().isDirectory() ? asyncResultify(getFs().rmdir)({
dirPath: path,
recursive: true
}) : asyncResultify(getFs().unlink)({ filePath: path }))).and(RESULT_VOID).orElse(fileErrorToRemoveResult);
}
async function stat$2(path, options) {
const pathRes = validateReadablePath(path);
if (pathRes.isErr()) return pathRes.asErr();
path = pathRes.unwrap();
const { recursive = false } = options ?? {};
return (await asyncResultify(getFs().stat)({
path,
recursive
})).map((x) => normalizeStats(x.stats, recursive)).orElse(fileErrorToResult);
}
/**
* 将内容写入文件。
* @param filePath - 文件路径。
* @param contents - 要写入的内容。
* @param options - 可选的写入选项。
* @returns 写入操作的异步结果。
*/
async function writeFile$2(filePath, contents, options) {
const { append = false, create = true } = options ?? {};
const fs = getFs();
let writeMethod = fs.writeFile;
if (append) {
const existsRes = await exists$2(filePath);
if (existsRes.isErr()) return existsRes.asErr();
if (existsRes.unwrap()) writeMethod = fs.appendFile;
else {
if (!create) return createFileNotExistsError(filePath);
writeMethod = fs.writeFile;
}
}
const filePathRes = validateAbsolutePath(filePath);
if (filePathRes.isErr()) return filePathRes.asErr();
filePath = filePathRes.unwrap();
if (create && writeMethod === fs.writeFile) {
const mkdirRes = await mkdir$2(dirname(filePath));
if (mkdirRes.isErr()) return mkdirRes;
}
const contentsRes = getWriteFileContents(contents);
if (contentsRes.isErr()) return contentsRes.asErr();
const { data, encoding } = contentsRes.unwrap();
return (await asyncResultify(writeMethod)({
filePath,
data,
encoding
})).and(RESULT_VOID).orElse(fileErrorToResult);
}
/**
* 向文件追加内容。
* @param filePath - 文件路径。
* @param contents - 要追加的内容。
* @param options - 可选的追加选项。
* @returns 追加操作的异步结果。
*/
function appendFile$2(filePath, contents, options) {
return writeFile$2(filePath, contents, {
append: true,
create: options?.create ?? true
});
}
/**
* 复制文件或文件夹。
*
* @param srcPath - 源文件或文件夹路径。
* @param destPath - 目标文件或文件夹路径。
* @returns 操作的异步结果。
*/
async function copy$2(srcPath, destPath) {
const destPathRes = validateAbsolutePath(destPath);
if (destPathRes.isErr()) return destPathRes.asErr();
destPath = destPathRes.unwrap();
const statRes = await stat$2(srcPath, { recursive: true });
if (statRes.isErr()) return statRes.asErr();
srcPath = validateAbsolutePath(srcPath).unwrap();
for (const { path, stats } of statRes.unwrap()) {
let copyRes;
if (!path) if (stats.isDirectory()) copyRes = await mkdir$2(destPath);
else copyRes = await (await mkdir$2(dirname(destPath))).andThenAsync(() => {
return copyFile(srcPath, destPath);
});
else {
const srcEntryPath = srcPath + SEPARATOR + path;
const destEntryPath = destPath + SEPARATOR + path;
copyRes = await (stats.isDirectory() ? mkdir$2(destEntryPath) : copyFile(srcEntryPath, destEntryPath));
}
if (copyRes.isErr()) return copyRes;
}
return RESULT_VOID;
}
/**
* 检查指定路径的文件或目录是否存在。
* @param path - 文件或目录的路径。
* @param options - 可选的检查选项。
* @returns 检查存在性的异步结果,存在时返回 true。
*/
async function exists$2(path, options) {
const optionsRes = validateExistsOptions(options);
if (optionsRes.isErr()) return optionsRes.asErr();
const { isDirectory = false, isFile = false } = options ?? {};
if (!isDirectory && !isFile) return accessExists(path);
return getExistsResult(await stat$2(path), options);
}
/**
* 清空目录中的所有文件和子目录。
* @param dirPath - 目录路径。
* @returns 清空操作的异步结果。
*/
async function emptyDir$2(dirPath) {
const readDirRes = await readDir$2(dirPath);
if (readDirRes.isErr()) return isNotFoundError(readDirRes.unwrapErr()) ? mkdir$2(dirPath) : readDirRes.asErr();
dirPath = validateAbsolutePath(dirPath).unwrap();
const tasks = readDirRes.unwrap().map((name) => remove$2(dirPath + SEPARATOR + name));
return (await Promise.all(tasks)).find((x) => x.isErr()) ?? RESULT_VOID;
}
/**
* 读取文本文件的内容。
* @param filePath - 文件路径。
* @returns 包含文件文本内容的异步结果。
*/
function readTextFile$2(filePath) {
return readFile$2(filePath, { encoding: "utf8" });
}
/**
* 读取文件并解析为 JSON。
* @param filePath - 文件路径。
* @returns 读取结果。
*/
async function readJsonFile$2(filePath) {
return (await readTextFile$2(filePath)).andThen((contents) => {
return tryResult(JSON.parse, contents);
});
}
/**
* 将数据序列化为 JSON 并写入文件。
* @param filePath - 文件路径。
* @param data - 要写入的数据。
* @returns 写入结果。
*/
async function writeJsonFile$2(filePath, data) {
return tryResult(JSON.stringify, data).andThenAsync((text) => writeFile$2(filePath, text));
}
function downloadFile$2(fileUrl, filePath, options) {
const fileUrlRes = validateSafeUrl(fileUrl);
if (fileUrlRes.isErr()) return createFailedFetchTask(fileUrlRes);
if (typeof filePath === "string") {
const filePathRes = validateAbsolutePath(filePath);
if (filePathRes.isErr()) return createFailedFetchTask(filePathRes);
filePath = filePathRes.unwrap();
} else {
options = filePath;
filePath = void 0;
}
const { onProgress, headers, ...rest } = options ?? {};
let aborted = false;
const future = new Future();
let task;
const download = () => {
task = wx.downloadFile({
...rest,
url: fileUrl,
filePath,
header: headers,
async success(response) {
if (aborted) {
future.resolve(Err(createAbortError()));
return;
}
const { statusCode } = response;
if (statusCode >= 200 && statusCode < 300) {
future.resolve(Ok(response));
return;
}
if (response.filePath) await remove$2(response.filePath);
future.resolve(Err(new FetchError(response.errMsg, statusCode)));
},
fail(err) {
future.resolve(aborted ? Err(createAbortError()) : miniGameFailureToResult(err));
}
});
if (typeof onProgress === "function") task.onProgressUpdate((progress) => {
const { totalBytesExpectedToWrite, totalBytesWritten } = progress;
onProgress(typeof totalBytesExpectedToWrite === "number" && typeof totalBytesWritten === "number" ? Ok({
totalByteLength: totalBytesExpectedToWrite,
completedByteLength: totalBytesWritten
}) : Err(/* @__PURE__ */ new Error(`Unknown download progress ${totalBytesWritten}/${totalBytesExpectedToWrite}`)));
});
};
if (typeof filePath === "string") mkdir$2(dirname(filePath)).then((mkdirRes) => {
if (aborted) {
future.resolve(Err(createAbortError()));
return;
}
if (mkdirRes.isErr()) {
future.resolve(mkdirRes.asErr());
return;
}
download();
});
else download();
return {
abort() {
aborted = true;
task?.abort();
},
get aborted() {
return aborted;
},
get result() {
return future.promise;
}
};
}
/**
* 文件上传。
* @param filePath - 需要上传的文件路径。
* @param fileUrl - 目标网络 URL。
* @param options - 可选参数。
* @returns 上传操作的异步结果。
*/
function uploadFile$2(filePath, fileUrl, options) {
const fileUrlRes = validateSafeUrl(fileUrl);
if (fileUrlRes.isErr()) return createFailedFetchTask(fileUrlRes);
const filePathRes = validateAbsolutePath(filePath);
if (filePathRes.isErr()) return createFailedFetchTask(filePathRes);
filePath = filePathRes.unwrap();
const { headers, ...rest } = options ?? {};
let aborted = false;
const future = new Future();
const task = wx.uploadFile({
name: basename(filePath),
...rest,
url: fileUrl,
filePath,
header: headers,
success(res) {
future.resolve(Ok(res));
},
fail(err) {
future.resolve(miniGameFailureToResult(err));
}
});
return {
abort() {
aborted = true;
task?.abort();
},
get aborted() {
return aborted;
},
get result() {
return future.promise;
}
};
}
/**
* 解压 zip 文件。
* @param zipFilePath - 要解压的 zip 文件路径。
* @param destDir - 要解压到的目标文件夹路径。
* @returns 解压操作的异步结果。
*/
async function unzip$2(zipFilePath, destDir) {
const zipFilePathRes = validateAbsolutePath(zipFilePath);
if (zipFilePathRes.isErr()) return zipFilePathRes.asErr();
zipFilePath = zipFilePathRes.unwrap();
const destDirRes = validateAbsolutePath(destDir);
if (destDirRes.isErr()) return destDirRes.asErr();
destDir = destDirRes.unwrap();
return (await asyncResultify(getFs().unzip)({
zipFilePath,
targetPath: destDir
})).and(RESULT_VOID).orElse(fileErrorToResult);
}
/**
* 从网络下载 zip 文件并解压。
* @param zipFileUrl - Zip 文件的网络地址。
* @param destDir - 要解压到的目标文件夹路径。
* @param options - 可选的下载参数。
* @returns 下载并解压操作的异步结果。
*/
async function unzipFromUrl$2(zipFileUrl, destDir, options) {
const destDirRes = validateAbsolutePath(destDir);
if (destDirRes.isErr()) return destDirRes.asErr();
destDir = destDirRes.unwrap();
return (await downloadFile$2(zipFileUrl, options).result).andThenAsync(({ tempFilePath }) => {
return unzip$2(tempFilePath, destDir);
});
}
async function zip$2(sourcePath, zipFilePath, options) {
if (typeof zipFilePath === "string") {
const zipFilePathRes = validateAbsolutePath(zipFilePath);
if (zipFilePathRes.isErr()) return zipFilePathRes.asErr();
zipFilePath = zipFilePathRes.unwrap();
} else {
options = zipFilePath;
zipFilePath = void 0;
}
const statRes = await stat$2(sourcePath, { recursive: true });
if (statRes.isErr()) return statRes.asErr();
const statsArray = statRes.unwrap();
sourcePath = validateAbsolutePath(sourcePath).unwrap();
const sourceName = basename(sourcePath);
const zippable = {};
if (statsArray.length === 1 && statsArray[0].stats.isFile()) {
const readFileRes = await readFile$2(sourcePath);
if (readFileRes.isErr()) return readFileRes;
zippable[sourceName] = readFileRes.unwrap();
} else {
const preserveRoot = options?.preserveRoot ?? true;
if (preserveRoot) zippable[sourceName + SEPARATOR] = EMPTY_BYTES;
const tasks = [];
for (const { path, stats } of statRes.unwrap()) {
if (!path) continue;
const entryName = preserveRoot ? sourceName + SEPARATOR + path : path;
if (stats.isFile()) tasks.push((async () => {
return (await readFile$2(sourcePath + SEPARATOR + path)).map((data) => ({
entryName,
data
}));
})());
else zippable[entryName + SEPARATOR] = EMPTY_BYTES;
}
if (tasks.length > 0) {
const results = await Promise.all(tasks);
for (const result of results) {
if (result.isErr()) return result.asErr();
const { entryName, data } = result.unwrap();
zippable[entryName] = data;
}
}
}
if (Object.keys(zippable).length === 0) return createNothingToZipError();
return zipTo(zippable, zipFilePath);
}
async function zipFromUrl$2(sourceUrl, zipFilePath, options) {
if (typeof zipFilePath === "string") {
const zipFilePathRes = validateAbsolutePath(zipFilePath);
if (zipFilePathRes.isErr()) return zipFilePathRes.asErr();
zipFilePath = zipFilePathRes.unwrap();
} else {
options = zipFilePath;
zipFilePath = void 0;
}
return (await downloadFile$2(sourceUrl, options).result).andThenAsync(async ({ tempFilePath }) => {
const readFileRes = await readFile$2(tempFilePath);
if (readFileRes.isErr()) return readFileRes;
return zipTo({ [basename(tempFilePath)]: readFileRes.unwrap() }, zipFilePath);
});
}
/**
* 将错误对象转换为 IOResult 类型。
*/
function miniGameFailureToResult(error) {
return Err(miniGameFailureToError(error));
}
/**
* 创建一个 `AbortError` 错误。
*/
function createAbortError() {
const error = /* @__PURE__ */ new Error();
error.name = ABORT_ERROR;
return error;
}
/**
* 复制文件。
*/
async function copyFile(srcPath, destPath) {
return (await asyncResultify(getFs().copyFile)({
srcPath,
destPath
})).and(RESULT_VOID).orElse(fileErrorToResult);
}
/**
* 将 zippable 对象压缩为 zip 文件。
*/
function zipTo(zippable, zipFilePath) {
return tryResult(() => zipSync$2(zippable)).andThenAsync((bytesLike) => {
const bytes = bytesLike;
return zipFilePath ? writeFile$2(zipFilePath, bytes) : Promise.resolve(Ok(bytes));
});
}
//#endregion
//#region src/std/fs/web_fs_helpers.ts
/**
* @internal
* 文件系统辅助函数。
*/
/**
* 将 `FileSystemHandleLike` 转换为小游戏 `Stats`。
* @param handleLike - 要转换的 `FileSystemHandleLike` 对象。
* @returns 小游戏的 `Stats` 对象。
*/
function convertFileSystemHandleLikeToStats(handleLike) {
const isFile = isFileHandleLike(handleLike);
return {
isFile: () => isFile,
isDirectory: () => !isFile,
size: isFile ? handleLike.size : 0,
lastModifiedTime: isFile ? handleLike.lastModified : 0,
lastAccessedTime: 0,
mode: 0
};
}
/**
* 将 `FileSystemHandle` 转换为小游戏 `Stats`。
* @param handle - 要转换的 `FileSystemHandle` 对象。
* @returns 小游戏的 `Stats` 对象。
*/
async function convertFileSystemHandleToStats(handle) {
const isFile = isFileHandle(handle);
let size = 0;
let lastModified = 0;
if (isFile) {
const file = await handle.getFile();
({size, lastModified} = file);
}
return {
isFile: () => isFile,
isDirectory: () => !isFile,
size,
lastModifiedTime: lastModified,
lastAccessedTime: 0,
mode: 0
};
}
/**
* 将 Web 端的读取目录结果转换为小游戏端的读取目录结果。
* @param dirPath - 要读取的目录路径。
* @returns 目录内容路径数组。
*/
async function webToMinaReadDir(dirPath) {
return (await readDir$1(dirPath)).andTryAsync(async (entries) => {
if (typeof Array.fromAsync === "function") return Array.fromAsync(entries, ({ path }) => path);
const items = [];
for await (const { path } of entries) items.push(path);
return items;
});
}
/**
* 将 Web 端的 stat 结果转换为小游戏端的 stat 结果。
* @param path - 要获取状态的路径。
* @param options - 可选的 stat 选项。
* @returns 文件或目录的状态信息。
*/
async function webToMinaStat(path, options) {
const statRes = await stat$1(path);
if (statRes.isErr()) return statRes.asErr();
const entryStatsRes = await tryAsyncResult(convertFileSystemHandleToStats(statRes.unwrap()));
if (entryStatsRes.isErr()) return entryStatsRes;
if (!options?.recursive) return entryStatsRes;
const entryStats = entryStatsRes.unwrap();
if (entryStats.isFile()) return Ok([{
path: "",
stats: entryStats
}]);
return (await readDir$1(path)).andTryAsync(async (entries) => {
const tasks = [Promise.resolve({
path: "",
stats: entryStats
})];
for await (const { path, handle } of entries) tasks.push((async () => {
const stats = await convertFileSystemHandleToStats(handle);
return {
path,
stats
};
})());
return Promise.all(tasks);
});
}
/**
* 将 Web 端的读取目录结果转换为小游戏端的读取目录结果。
* @param dirPath - 要读取的目录路径。
* @returns 目录内容路径数组。
*/
function webToMinaReadDirSync(dirPath) {
return readDirSync$1(dirPath).map((entries) => {
return entries.map(({ path }) => path);
});
}
/**
* 将 Web 端的 stat 结果转换为小游戏端的 stat 结果。
* @param path - 要获取状态的路径。
* @param options - 可选的 stat 选项。
* @returns 文件或目录的状态信息。
*/
function webToMinaStatSync(path, options) {
const statRes = statSync$1(path);
if (statRes.isErr()) return statRes.asErr();
const entryStats = convertFileSystemHandleLikeToStats(statRes.unwrap());
if (!options?.recursive) return Ok(entryStats);
if (entryStats.isFile()) return Ok([{
path: "",
stats: entryStats
}]);
return readDirSync$1(path).map((entries) => {
const statsArr = entries.map(({ path, handle }) => ({
path,
stats: convertFileSystemHandleLikeToStats(handle)
}));
statsArr.unshift({
path: "",
stats: entryStats
});
return statsArr;
});
}
//#endregion
//#region src/std/fs/fs_async.ts
/**
* 递归创建文件夹,相当于 `mkdir -p`。
* @param dirPath - 将要创建的目录的路径。
* @returns 创建成功返回的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await mkdir('/path/to/dir');
* if (result.isOk()) {
* console.log('目录创建成功');
* }
* ```
*/
function mkdir(dirPath) {
return (IS_MINA ? mkdir$2 : mkdir$1)(dirPath);
}
/**
* 移动或重命名文件或目录。
* @param srcPath - 原始路径。
* @param destPath - 新路径。
* @returns 操作成功返回的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await move('/old/path', '/new/path');
* if (result.isOk()) {
* console.log('移动成功');
* }
* ```
*/
function move(srcPath, destPath) {
return (IS_MINA ? move$2 : move$1)(srcPath, destPath);
}
/**
* 异步读取指定目录下的所有文件和子目录。
* @param dirPath - 需要读取的目录路径。
* @returns 包含目录内容的字符串数组的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await readDir('/path/to/dir');
* if (result.isOk()) {
* console.log(result.unwrap()); // ['file1.txt', 'file2.txt', 'subdir']
* }
* ```
*/
async function readDir(dirPath) {
return (IS_MINA ? readDir$2 : webToMinaReadDir)(dirPath);
}
/**
* 读取文件内容,可选地指定编码和返回类型。
* @template T - 返回内容的类型。
* @param filePath - 文件路径。
* @param options - 可选的读取选项。
* @returns 包含文件内容的异步操作结果。
*/
function readFile(filePath, options) {
return IS_MINA ? readFile$2(filePath, options) : readFile$1(filePath, options);
}
/**
* 删除文件或目录。
* @param path - 要删除的文件或目录的路径。
* @returns 删除成功返回的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await remove('/path/to/file.txt');
* if (result.isOk()) {
* console.log('删除成功');
* }
* ```
*/
function remove(path) {
return (IS_MINA ? remove$2 : remove$1)(path);
}
/**
* 获取文件或目录的状态信息。
* @param path - 文件或目录的路径。
* @param options - 可选选项,包含 recursive 可递归获取目录下所有文件状态。
* @returns 包含状态信息的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await stat('/path/to/file.txt');
* if (result.isOk()) {
* const stats = result.unwrap();
* console.log(stats.isFile()); // true
* }
* ```
*/
async function stat(path, options) {
return (IS_MINA ? stat$2 : webToMinaStat)(path, options);
}
/**
* 写入文件,文件不存在则创建,同时创建对应目录。
* @param filePath - 文件路径。
* @param contents - 要写入的内容,支持 ArrayBuffer 和 string(需确保是 UTF-8 编码)。
* @param options - 可选写入选项。
* @returns 写入成功返回的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await writeFile('/path/to/file.txt', 'Hello, World!');
* if (result.isOk()) {
* console.log('写入成功');
* }
* ```
*/
function writeFile(filePath, contents, options) {
return (IS_MINA ? writeFile$2 : writeFile$1)(filePath, contents, options);
}
/**
* 向文件追加内容。
* @param filePath - 文件路径。
* @param contents - 要追加的内容。
* @param options - 可选的追加选项。
* @returns 追加成功返回的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await appendFile('/path/to/file.txt', '\nNew content');
* if (result.isOk()) {
* console.log('追加成功');
* }
* ```
*/
function appendFile(filePath, contents, options) {
return (IS_MINA ? appendFile$2 : appendFile$1)(filePath, contents, options);
}
/**
* 复制文件或文件夹。
* @param srcPath - 源文件或文件夹路径。
* @param destPath - 目标文件或文件夹路径。
* @returns 操作的异步结果。
* @since 1.0.0
* @example
* ```ts
* const result = await copy('/src/file.txt', '/dest/file.txt');
* if (result.isOk()) {
* console.log('复制成功');
* }
* ```
*/
function copy(srcPath, destPath) {
return (IS_MINA ? copy$2 : copy$1)(srcPath, destPath);
}
/**
* 检查指定路径的文件或目录是否存在。
* @param path - 文件或目录的路径。
* @param options - 可选的检查选项。
* @returns 存在返回 true 的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await exists('/path/to/file.txt');
* if (result.isOk() && result.unwrap()) {
* console.log('文件存在');
* }
* ```
*/
function exists(path, options) {
return (IS_MINA ? exists$2 : exists$1)(path, options);
}
/**
* 清空指定目录下的所有文件和子目录。
* @param dirPath - 目录路径。
* @returns 清空成功返回的异步操作结果。
* @since 1.0.1
* @example
* ```ts
* const result = await emptyDir('/path/to/dir');
* if (result.isOk()) {
* console.log('目录已清空');
* }
* ```
*/
function emptyDir(dirPath) {
return (IS_MINA ? emptyDir$2 : emptyDir$1)(dirPath);
}
/**
* 读取 JSON 文件并解析为对象。
* @typeParam T - JSON 解析后的类型。
* @param filePath - 文件路径。
* @returns 解析后的对象。
* @since 1.6.0
* @example
* ```ts
* const result = await readJsonFile<{ name: string }>('/path/to/config.json');
* if (result.isOk()) {
* console.log(result.unwrap().name);
* }
* ```
*/
function readJsonFile(filePath) {
return (IS_MINA ? readJsonFile$2 : readJsonFile$1)(filePath);
}
/**
* 读取文本文件的内容。
* @param filePath - 文件路径。
* @returns 包含文件文本内容的异步操作结果。
* @since 1.0.0
* @example
* ```ts
* const result = await readTextFile('/path/to/file.txt');
* if (result.isOk()) {
* console.log(result.unwrap());
* }
* ```
*/
function readTextFile(filePath) {
return (IS_MINA ? readTextFile$2 : readTextFile$1)(filePath);
}
/**
* 将数据序列化为 JSON 并写入文件。
* @typeParam T - 要写入数据的类型。
* @param filePath - 文件路径。
* @param data - 要写入的数据。
* @returns 写入操作的异步结果。
* @since 2.0.0
* @example
* ```ts
* const result = await writeJsonFile('/path/to/config.json', { name: 'test' });
* if (result.isOk()) {
* console.log('写入成功');
* }
* ```
*/
function writeJsonFile(filePath, data) {
return (IS_MINA ? writeJsonFile$2 : writeJsonFile$1)(filePath, data);
}
function downloadFile(fileUrl, filePath, options) {
if (typeof filePath === "string") return IS_MINA ? downloadFile$2(fileUrl, filePath, options) : downloadFile$1(fileUrl, filePath, options);
else return IS_MINA ? downloadFile$2(fileUrl, filePath) : downloadFile$1(fileUrl, filePath);
}
/**
* 上传本地文件到服务器。
* @param filePath - 需要上传的文件路径。
* @param fileUrl - 目标服务器的 URL。
* @param options - 可选的请求初始化参数。
* @returns 上传操作的 FetchTask。
* @since 1.0.0
* @example
* ```ts
* const task = uploadFile('/path/to/file.txt', 'https://example.com/upload');
* const result = await task.result;
* if (result.isOk()) {
* console.log('上传成功');
* }
* ```
*/
function uploadFile(filePath, fileUrl, options) {
return IS_MINA ? uploadFile$2(filePath, fileUrl, options) : uploadFile$1(filePath, fileUrl, options);
}
/**
* 解压 zip 文件。
* @param zipFilePath - 要解压的 zip 文件路径。
* @param targetPath - 要解压到的目标文件夹路径。
* @returns 解压操作的异步结果。
* @since 1.3.0
* @example
* ```ts
* const result = await unzip('/path/to/archive.zip', '/path/to/output');
* if (result.isOk()) {
* console.log('解压成功');
* }
* ```
*/
function unzip(zipFilePath, targetPath) {
return (IS_MINA ? unzip$2 : unzip$1)(zipFilePath, targetPath);
}
/**
* 从网络下载 zip 文件并解压。
* @param zipFileUrl - Zip 文件的网络地址。
* @param targetPath - 要解压到的目标文件夹路径。
* @param options - 可选的下载参数。
* @returns 下载并解压操作的异步结果。
* @since 1.4.0
* @example
* ```ts
* const result = await unzipFromUrl('https://example.com/archive.zip', '/path/to/output');
* if (result.isOk()) {
* console.log('下载并解压成功');
* }
* ```
*/
function unzipFromUrl(zipFileUrl, targetPath, options) {
return (IS_MINA ? unzipFromUrl$2 : unzipFromUrl$1)(zipFileUrl, targetPath, options);
}
function zip(sourcePath, zipFilePath, options) {
if (typeof zipFilePath === "string") return (IS_MINA ? zip$2 : zip$1)(sourcePath, zipFilePath, options);
else return (IS_MINA ? zip$2 : zip$1)(sourcePath, zipFilePath);
}
function zipFromUrl(sourceUrl, zipFilePath, options) {
if (typeof zipFilePath === "string") return IS_MINA ? zipFromUrl$2(sourceUrl, zipFilePath, options) : zipFromUrl$1(sourceUrl, zipFilePath, options);
else return IS_MINA ? zipFromUrl$2(sourceUrl, zipFilePath) : zipFromUrl$1(sourceUrl, zipFilePath);
}
//#endregion
//#region src/std/fs/mina_fs_sync.ts
/**
* @internal
* 小游戏平台的同步文件系统操作实现。
*/
/**
* `mkdir` 的同步版本。
* @param dirPath - 要创建的目录路径。
* @returns 操作结果。
*/
function mkdirSync$2(dirPath) {
const dirPathRes = validateAbsolutePath(dirPath);
if (dirPathRes.isErr()) return dirPathRes.asErr();
dirPath = dirPathRes.unwrap();
if (dirPath === getUsrPath()) return RESULT_VOID;
const statRes = statSync$2(dirPath);
if (statRes.isOk()) {
if (statRes.unwrap().isFile()) return createDirIsFileError(dirPath);
return RESULT_VOID;
}
return trySyncOp(() => getFs().mkdirSync(dirPath, true), fileErrorToMkdirResult);
}
/**
* `move` 的同步版本。
* @param srcPath - 源路径。
* @param destPath - 目标路径。
* @returns 操作结果。
*/
function moveSync$2(srcPath, destPath) {
const srcPathRes = validateAbsolutePath(srcPath);
if (srcPathRes.isErr()) return srcPathRes.asErr();
srcPath = srcPathRes.unwrap();
const destPathRes = validateAbsolutePath(destPath);
if (destPathRes.isErr()) return destPathRes.asErr();
destPath = destPathRes.unwrap();
return trySyncOp(() => getFs().renameSync(srcPath, destPath));
}
/**
* `readDir` 的同步版本。
* @param dirPath - 要读取的目录路径。
* @returns 目录中的文件和子目录名称数组。
*/
function readDirSync$2(dirPath) {
const dirPathRes = validateReadablePath(dirPath);
if (dirPathRes.isErr()) return dirPathRes.asErr();
dirPath = dirPathRes.unwrap();
return trySyncOp(() => getFs().readdirSync(dirPath));
}
function readFileSync$2(filePath, options) {
const filePathRes = validateReadablePath(filePath);
if (filePathRes.isErr()) return filePathRes.asErr();
filePath = filePathRes.unwrap();
const encoding = getReadFileEncoding(options);
return trySyncOp(() => {
const data = getFs().readFileSync(filePath, encoding);
return typeof data === "string" ? data : new Uint8Array(data);
});
}
/**
* `remove` 的同步版本。
* @param path - 要删除的文件或目录路径。
* @returns 操作结果。
*/
function removeSync$2(path) {
const statRes = statSync$2(path);
if (statRes.isErr()) return isNotFoundError(statRes.unwrapErr()) ? RESULT_VOID : statRes.asErr();
path = validateAbsolutePath(path).unwrap();
return trySyncOp(() => {
if (statRes.unwrap().isDirectory()) getFs().rmdirSync(path, true);
else getFs().unlinkSync(path);
}, fileErrorToRemoveResult);
}
function statSync$2(path, options) {
const pathRes = validateReadablePath(path);
if (pathRes.isErr()) return pathRes.asErr();
path = pathRes.unwrap();
const { recursive = false } = options ?? {};
return trySyncOp(() => getFs().statSync(path, recursive)).map((x) => normalizeStats(x, recursive));
}
/**
* `writeFile` 的同步版本。
* @param filePath - 要写入的文件路径。
* @param contents - 要写入的内容。
* @param options - 写入选项。
* @returns 操作结果。
*/
function writeFileSync$2(filePath, contents, options) {
const { append = false, create = true } = options ?? {};
const fs = getFs();
let writeMethod = fs.writeFileSync;
if (append) {
const existsRes = existsSync$2(filePath);
if (existsRes.isErr()) return existsRes.asErr();
if (existsRes.unwrap()) writeMethod = fs.appendFileSync;
else {
if (!create) return createFileNotExistsError(filePath);
writeMethod = fs.writeFileSync;
}
}
const filePathRes = validateAbsolutePath(filePath);
if (filePathRes.isErr()) return filePathRes.asErr();
filePath = filePathRes.unwrap();
if (create && writeMethod === fs.writeFileSync) {
const result = mkdirSync$2(dirname(filePath));
if (result.isErr()) return result;
}
const contentsRes = getWriteFileContents(contents);
if (contentsRes.isErr()) return contentsRes.asErr();
const { data, encoding } = contentsRes.unwrap();
return trySyncOp(() => writeMethod(filePath, data, encoding));
}
/**
* `appendFile` 的同步版本。
* @param filePath - 文件路径。
* @param contents - 要追加的内容。
* @param options - 可选的追加选项。
* @returns 操作结果。
*/
function appendFileSync$2(filePath, contents, options) {
return writeFileSync$2(filePath, contents, {
append: true,
create: options?.create ?? true
});
}
/**
* `copy` 的同步版本。
* @param srcPath - 源路径。
* @param destPath - 目标路径。
* @returns 操作结果。
*/
function copySync$2(srcPath, destPath) {
const destPathRes = validateAbsolutePath(destPath);
if (destPathRes.isErr()) return destPathRes.asErr();
destPath = destPathRes.unwrap();
const statRes = statSync$2(srcPath, { recursive: true });
if (statRes.isErr()) return statRes.asErr();
srcPath = validateAbsolutePath(srcPath).unwrap();
for (const { path, stats } of statRes.unwrap()) {
let copyRes;
if (!path) if (stats.isDirectory()) copyRes = mkdirSync$2(destPath);
else copyRes = mkdirSync$2(dirname(destPath)).andThen(() => {
return copyFileSync(srcPath, destPath);
});
else {
const srcEntryPath = srcPath + SEPARATOR + path;
const destEntryPath = destPath + SEPARATOR + path;
copyRes = stats.isDirectory() ? mkdirSync$2(destEntryPath) : copyFileSync(srcEntryPath, destEntryPath);
}
if (copyRes.isErr()) return copyRes;
}
return RESULT_VOID;
}
/**
* `exists` 的同步版本。
* @param path - 文件或目录路径。
* @param options - 检查选项。
* @returns 是否存在。
*/
function existsSync$2(path, options) {
const optionsRes = validateExistsOptions(options);
if (optionsRes.isErr()) return optionsRes.asErr();
const { isDirectory = false, isFile = false } = options ?? {};
if (!isDirectory && !isFile) return accessExistsSync(path);
return getExistsResult(statSync$2(path), options);
}
/**
* `emptyDir` 的同步版本。
* @param dirPath - 要清空的目录路径。
* @returns 操作结果。
*/
function emptyDirSync$2(dirPath) {
const readDirRes = readDirSync$2(dirPath);
if (readDirRes.isErr()) return isNotFoundError(readDirRes.unwrapErr()) ? mkdirSync$2(dirPath) : readDirRes.asErr();
dirPath = validateAbsolutePath(dirPath).unwrap();
for (const name of readDirRes.unwrap()) {
const removeRes = removeSync$2(dirPath + SEPARATOR + name);
if (removeRes.isErr()) return removeRes.asErr();
}
return RESULT_VOID;
}
/**
* `readTextFile` 的同步版本。
* @param filePath - 要读取的文件路径。
* @returns 文件的文本内容。
*/
function readTextFileSync$2(filePath) {
return readFileSync$2(filePath, { encoding: "utf8" });
}
/**
* `readJsonFile` 的同步版本。
* @param filePath - 要读取的 JSON 文件路径。
* @returns 解析后的 JSON 对象。
*/
function readJsonFileSync$2(filePath) {
return readTextFileSync$2(filePath).andThen((contents) => {
return tryResult(JSON.parse, contents);
});
}
/**
* `writeJsonFile` 的同步版本。
* @param filePath - 要写入的文件路径。
* @param data - 要序列化并写入的数据。
* @returns 操作结果。
*/
function writeJsonFileSync$2(filePath, data) {
return tryResult(JSON.stringify, data).andThen((text) => writeFileSync$2(filePath, text));
}
/**
* `unzip` 的同步版本。
* @param zipFilePath - 要解压的 ZIP 文件路径。
* @param destDir - 解压目标目录。
* @returns 操作结果。
*/
function unzipSync$3(zipFilePath, destDir) {
const destDirRes = validateAbsolutePath(destDir);
if (destDirRes.isErr()) return destDirRes.asErr();
destDir = destDirRes.unwrap();
const readFileRes = readFileSync$2(zipFilePath);
if (readFileRes.isErr()) return readFileRes.asErr();
const bytes = readFileRes.unwrap();
const unzippedRes = tryResult(() => unzipSync$2(bytes));
if (unzippedRes.isErr()) return unzippedRes.asErr();
const unzipped = unzippedRes.unwrap();
for (const path in unzipped) if (path.at(-1) === SEPARATOR) {
const mkdirRes = mkdirSync$2(destDir + SEPARATOR + path.slice(1));
if (mkdirRes.isErr()) return mkdirRes.asErr();
} else {
const writeFileRes = writeFileSync$2(destDir + SEPARATOR + path, unzipped[path]);
if (writeFileRes.isErr()) return writeFileRes.asErr();
}
return RESULT_VOID;
}
function zipSync$3(sourcePath, zipFilePath, options) {
if (typeof zipFilePath === "string") {
const zipFilePathRes = validateAbsolutePath(zipFilePath);
if (zipFilePathRes.isErr()) return zipFilePathRes.asErr();
zipFilePath = zipFilePathRes.unwrap();
} else {
options = zipFilePath;
zipFilePath = void 0;
}
const statRes = statSync$2(sourcePath, { recursive: true });
if (statRes.isErr()) return statRes.asErr();
const statsArray = statRes.unwrap();
sourcePath = validateAbsolutePath(sourcePath).unwrap();
const sourceName = basename(sourcePath);
const zippable = {};
if (statsArray.length === 1 && statsArray[0].stats.isFile()) {
const readFileRes = readFileSync$2(sourcePath);
if (readFileRes.isErr()) return readFileRes;
zippable[sourceName] = readFileRes.unwrap();
} else {
const preserveRoot = options?.preserveRoot ?? true;
if (preserveRoot) zippable[sourceName + SEPARATOR] = EMPTY_BYTES;
for (const { path, stats } of statsArray) {
if (!path) continue;
const entryName = preserveRoot ? sourceName + SEPARATOR + path : path;
if (stats.isFile()) {
const readFileRes = readFileSync$2(sourcePath + SEPARATOR + path);
if (readFileRes.isErr()) return readFileRes;
zippable[entryName] = readFileRes.unwrap();
} else zippable[entryName + SEPARATOR] = EMPTY_BYTES;
}
}
if (Object.keys(zippable).length === 0) return createNothingToZipError();
return tryResult(() => zipSync$2(zippable)).andThen((bytesLike) => {
const bytes = bytesLike;
return zipFilePath ? writeFileSync$2(zipFilePath, bytes) : Ok(bytes);
});
}
/**
* 安全地调用同步接口。
*/
function trySyncOp(op, errToResult = fileErrorToResult) {
return tryResult(op).orElse(errToResult);
}
/**
* 复制单个文件。
*/
function copyFileSync(srcPath, destPath) {
return trySyncOp(() => getFs().copyFileSync(srcPath, destPath));
}
//#endregion
//#region src/std/fs/fs_sync.ts
/**
* `mkdir` 的同步版本,递归创建文件夹。
* @param dirPath - 将要创建的目录的路径。
* @returns 创建成功返回的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = mkdirSync('/path/to/dir');
* if (result.isOk()) {
* console.log('目录创建成功');
* }
* ```
*/
function mkdirSync(dirPath) {
return (IS_MINA ? mkdirSync$2 : mkdirSync$1)(dirPath);
}
/**
* `move` 的同步版本,移动或重命名文件或目录。
* @param srcPath - 原始路径。
* @param destPath - 新路径。
* @returns 操作成功返回的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = moveSync('/old/path', '/new/path');
* if (result.isOk()) {
* console.log('移动成功');
* }
* ```
*/
function moveSync(srcPath, destPath) {
return (IS_MINA ? moveSync$2 : moveSync$1)(srcPath, destPath);
}
/**
* `readDir` 的同步版本,读取指定目录下的所有文件和子目录。
* @param dirPath - 需要读取的目录路径。
* @returns 包含目录内容的字符串数组的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = readDirSync('/path/to/dir');
* if (result.isOk()) {
* console.log(result.unwrap()); // ['file1.txt', 'file2.txt']
* }
* ```
*/
function readDirSync(dirPath) {
return (IS_MINA ? readDirSync$2 : webToMinaReadDirSync)(dirPath);
}
function readFileSync(filePath, options) {
return IS_MINA ? readFileSync$2(filePath, options) : readFileSync$1(filePath, options);
}
/**
* `remove` 的同步版本,删除文件或目录。
* @param path - 要删除的文件或目录的路径。
* @returns 删除成功返回的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = removeSync('/path/to/file.txt');
* if (result.isOk()) {
* console.log('删除成功');
* }
* ```
*/
function removeSync(path) {
return (IS_MINA ? removeSync$2 : removeSync$1)(path);
}
function statSync(path, options) {
return (IS_MINA ? statSync$2 : webToMinaStatSync)(path, options);
}
/**
* `writeFile` 的同步版本,写入文件。
* @param filePath - 文件路径。
* @param contents - 要写入的内容。
* @returns 写入成功返回的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = writeFileSync('/path/to/file.txt', 'Hello, World!');
* if (result.isOk()) {
* console.log('写入成功');
* }
* ```
*/
function writeFileSync(filePath, contents) {
return (IS_MINA ? writeFileSync$2 : writeFileSync$1)(filePath, contents);
}
/**
* `appendFile` 的同步版本,向文件追加内容。
* @param filePath - 文件路径。
* @param contents - 要追加的内容。
* @param options - 可选的追加选项。
* @returns 追加成功返回的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = appendFileSync('/path/to/file.txt', '\nNew content');
* if (result.isOk()) {
* console.log('追加成功');
* }
* ```
*/
function appendFileSync(filePath, contents, options) {
return (IS_MINA ? appendFileSync$2 : appendFileSync$1)(filePath, contents, options);
}
/**
* `copy` 的同步版本,复制文件或文件夹。
* @param srcPath - 源文件或文件夹路径。
* @param destPath - 目标文件或文件夹路径。
* @returns 操作的结果。
* @since 1.1.0
* @example
* ```ts
* const result = copySync('/src/file.txt', '/dest/file.txt');
* if (result.isOk()) {
* console.log('复制成功');
* }
* ```
*/
function copySync(srcPath, destPath) {
return (IS_MINA ? copySync$2 : copySync$1)(srcPath, destPath);
}
/**
* `exists` 的同步版本,检查指定路径的文件或目录是否存在。
* @param path - 文件或目录的路径。
* @param options - 可选的检查选项。
* @returns 存在返回 true 的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = existsSync('/path/to/file.txt');
* if (result.isOk() && result.unwrap()) {
* console.log('文件存在');
* }
* ```
*/
function existsSync(path, options) {
return (IS_MINA ? existsSync$2 : existsSync$1)(path, options);
}
/**
* `emptyDir` 的同步版本,清空指定目录下的所有文件和子目录。
* @param dirPath - 目录路径。
* @returns 清空成功返回的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = emptyDirSync('/path/to/dir');
* if (result.isOk()) {
* console.log('目录已清空');
* }
* ```
*/
function emptyDirSync(dirPath) {
return (IS_MINA ? emptyDirSync$2 : emptyDirSync$1)(dirPath);
}
/**
* `readJsonFile` 的同步版本,读取 JSON 文件并解析为对象。
* @typeParam T - JSON 解析后的类型。
* @param filePath - 文件路径。
* @returns 解析后的对象。
* @since 1.6.0
* @example
* ```ts
* const result = readJsonFileSync<{ name: string }>('/path/to/config.json');
* if (result.isOk()) {
* console.log(result.unwrap().name);
* }
* ```
*/
function readJsonFileSync(filePath) {
return (IS_MINA ? readJsonFileSync$2 : readJsonFileSync$1)(filePath);
}
/**
* `readTextFile` 的同步版本,读取文本文件的内容。
* @param filePath - 文件路径。
* @returns 包含文件文本内容的操作结果。
* @since 1.1.0
* @example
* ```ts
* const result = readTextFileSync('/path/to/file.txt');
* if (result.isOk()) {
* console.log(result.unwrap());
* }
* ```
*/
function readTextFileSync(filePath) {
return (IS_MINA ? readTextFileSync$2 : readTextFileSync$1)(filePath);
}
/**
* `writeJsonFile` 的同步版本,将数据序列化为 JSON 并写入文件。
* @typeParam T - 要写入数据的类型。
* @param filePath - 文件路径。
* @param data - 要写入的数据。
* @returns 写入操作的结果。
* @since 2.0.0
* @example
* ```ts
* const result = writeJsonFileSync('/path/to/config.json', { name: 'test' });
* if (result.isOk()) {
* console.log('写入成功');
* }
* ```
*/
function writeJsonFileSync(filePath, data) {
return (IS_MINA ? writeJsonFileSync$2 : writeJsonFileSync$1)(filePath, data);
}
/**
* `unzip` 的同步版本,解压 zip 文件。
* @param zipFilePath - 要解压的 zip 文件路径。
* @param targetPath - 要解压到的目标文件夹路径。
* @returns 解压操作的结果。
* @since 1.3.0
* @example
* ```ts
* const result = unzipSync('/path/to/archive.zip', '/path/to/output');
* if (result.isOk()) {
* console.log('解压成功');
* }
* ```
*/
function unzipSync(zipFilePath, targetPath) {
return (IS_MINA ? unzipSync$3 : unzipSync$1)(zipFilePath, targetPath);
}
function zipSync(sourcePath, zipFilePath, options) {
if (typeof zipFilePath === "string") return (IS_MINA ? zipSync$3 : zipSync$1)(sourcePath, zipFilePath, options);
else return (IS_MINA ? zipSync$3 : zipSync$1)(sourcePath, zipFilePath);
}
//#endregion
export { appendFile, appendFileSync, copy, copySync, downloadFile, emptyDir, emptyDirSync, exists, existsSync, mkdir, mkdirSync, move, moveSync, opfs, readDir, readDirSync, readFile, readFileSync, readJsonFile, readJsonFileSync, readTextFile, readTextFileSync, remove, removeSync, stat, statSync, unzip, unzipFromUrl, unzipSync, uploadFile, writeFile, writeFileSync, writeJsonFile, writeJsonFileSync, zip, zipFromUrl, zipSync };
//# sourceMappingURL=fs.mjs.map