mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00

Some checks are pending
ci / build libs (push) Blocked by required conditions
ci / test debug windows-x86_64 (push) Blocked by required conditions
ci / test release windows-x86_64 (push) Blocked by required conditions
ci / pre-build (push) Waiting to run
ci / test debug linux-aarch64 (push) Blocked by required conditions
ci / test release linux-aarch64 (push) Blocked by required conditions
ci / test debug macos-aarch64 (push) Blocked by required conditions
ci / test release macos-aarch64 (push) Blocked by required conditions
ci / bench release linux-x86_64 (push) Blocked by required conditions
ci / lint debug linux-x86_64 (push) Blocked by required conditions
ci / lint debug macos-x86_64 (push) Blocked by required conditions
ci / lint debug windows-x86_64 (push) Blocked by required conditions
ci / test debug linux-x86_64 (push) Blocked by required conditions
ci / test release linux-x86_64 (push) Blocked by required conditions
ci / test debug macos-x86_64 (push) Blocked by required conditions
ci / test release macos-x86_64 (push) Blocked by required conditions
ci / publish canary (push) Blocked by required conditions
Closes #30570 Changes in this PR: - Implement `ino`, `nlink`, and `blocks` properties of `Deno.FileInfo` on Windows. These changes are automatically reflected to the corresponding node stat function. In order to do so, I had to tinker with the [createByteStruct](a3a904da14/ext/fs/30_fs.js (L297)
) function to create another optional int type, apart from `?u64`. It's common for small sized files on Windows (particularly NTFS file system) to have a `Stats.blocks` property of 0, and currently all 0 values with type `?u64` will be coerced into `null` by `createByteStruct`. - Refactor the `BigIntStats` and `Stats` class, to use the same class with Node.js that are provided from [utils.mjs](7f8e488c36/ext/node/polyfills/internal/fs/utils.mjs (L577)
). Also ensures that all properties are not `null` or `undefined`. - Addresses the `prefer-primordials` lint rule.
256 lines
6.9 KiB
TypeScript
256 lines
6.9 KiB
TypeScript
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
import { denoErrorToNodeError } from "ext:deno_node/internal/errors.ts";
|
|
import { promisify } from "ext:deno_node/internal/util.mjs";
|
|
import { primordials } from "ext:core/mod.js";
|
|
import {
|
|
BigIntStats,
|
|
getValidatedPathToString,
|
|
Stats,
|
|
} from "ext:deno_node/internal/fs/utils.mjs";
|
|
import { makeCallback } from "ext:deno_node/_fs/_fs_common.ts";
|
|
import { isWindows } from "ext:deno_node/_util/os.ts";
|
|
import { Buffer } from "node:buffer";
|
|
|
|
export { BigIntStats, Stats };
|
|
|
|
const {
|
|
BigInt,
|
|
Date,
|
|
DatePrototypeGetTime,
|
|
ErrorPrototype,
|
|
ObjectDefineProperties,
|
|
ObjectPrototypeIsPrototypeOf,
|
|
PromisePrototypeThen,
|
|
} = primordials;
|
|
|
|
export type statOptions = {
|
|
bigint: boolean;
|
|
throwIfNoEntry?: boolean;
|
|
};
|
|
|
|
export function convertFileInfoToStats(origin: Deno.FileInfo): Stats {
|
|
const atime = origin.atime ?? new Date(0);
|
|
const birthtime = origin.birthtime ?? new Date(0);
|
|
const ctime = origin.ctime ?? new Date(0);
|
|
const mtime = origin.mtime ?? new Date(0);
|
|
|
|
const stats = new Stats(
|
|
origin.dev,
|
|
origin.mode || 0,
|
|
origin.nlink || 0,
|
|
isWindows ? 0 : origin.uid,
|
|
isWindows ? 0 : origin.gid,
|
|
isWindows ? 0 : origin.rdev,
|
|
// https://github.com/nodejs/node/blob/591ba692bfe30408e6a67397e7d18bfa1b9c3561/deps/uv/src/win/fs.c#L1929-L1930
|
|
isWindows ? 4096 : origin.blksize,
|
|
origin.ino || 0,
|
|
origin.size,
|
|
origin.blocks || 0,
|
|
DatePrototypeGetTime(atime),
|
|
DatePrototypeGetTime(mtime),
|
|
DatePrototypeGetTime(ctime),
|
|
DatePrototypeGetTime(birthtime),
|
|
);
|
|
|
|
return defineExtraProps(stats, origin);
|
|
}
|
|
|
|
function toBigInt(number?: number | null): bigint {
|
|
if (number === null || number === undefined) return 0n;
|
|
return BigInt(number);
|
|
}
|
|
|
|
export function convertFileInfoToBigIntStats(
|
|
origin: Deno.FileInfo,
|
|
): BigIntStats {
|
|
const atime = origin.atime ?? new Date(0);
|
|
const birthtime = origin.birthtime ?? new Date(0);
|
|
const ctime = origin.ctime ?? new Date(0);
|
|
const mtime = origin.mtime ?? new Date(0);
|
|
|
|
const bigIntStats = new BigIntStats(
|
|
toBigInt(origin.dev),
|
|
toBigInt(origin.mode),
|
|
toBigInt(origin.nlink),
|
|
toBigInt(origin.uid),
|
|
toBigInt(origin.gid),
|
|
toBigInt(origin.rdev),
|
|
// https://github.com/nodejs/node/blob/591ba692bfe30408e6a67397e7d18bfa1b9c3561/deps/uv/src/win/fs.c#L1929-L1930
|
|
isWindows ? 4096n : toBigInt(origin.blksize),
|
|
toBigInt(origin.ino),
|
|
toBigInt(origin.size),
|
|
toBigInt(origin.blocks),
|
|
BigInt(DatePrototypeGetTime(atime)) * 1000000n,
|
|
BigInt(DatePrototypeGetTime(mtime)) * 1000000n,
|
|
BigInt(DatePrototypeGetTime(ctime)) * 1000000n,
|
|
BigInt(DatePrototypeGetTime(birthtime)) * 1000000n,
|
|
);
|
|
|
|
return defineExtraProps(bigIntStats, origin);
|
|
}
|
|
|
|
const defineExtraProps = <T extends Stats | BigIntStats>(
|
|
stats: T,
|
|
origin: Deno.FileInfo,
|
|
): T => {
|
|
ObjectDefineProperties(stats, {
|
|
isDirectory: {
|
|
__proto__: null,
|
|
value: () => origin.isDirectory,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
isFile: {
|
|
__proto__: null,
|
|
value: () => origin.isFile,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
isSymbolicLink: {
|
|
__proto__: null,
|
|
value: () => origin.isSymlink,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
isBlockDevice: {
|
|
__proto__: null,
|
|
value: () => isWindows ? false : origin.isBlockDevice,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
isFIFO: {
|
|
__proto__: null,
|
|
value: () => isWindows ? false : origin.isFifo,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
isCharacterDevice: {
|
|
__proto__: null,
|
|
value: () => isWindows ? false : origin.isCharDevice,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
isSocket: {
|
|
__proto__: null,
|
|
value: () => isWindows ? false : origin.isSocket,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
});
|
|
return stats;
|
|
};
|
|
|
|
// shortcut for Convert File Info to Stats or BigIntStats
|
|
export function CFISBIS(fileInfo: Deno.FileInfo, bigInt: false): Stats;
|
|
export function CFISBIS(fileInfo: Deno.FileInfo, bigInt: true): BigIntStats;
|
|
export function CFISBIS(
|
|
fileInfo: Deno.FileInfo,
|
|
bigInt: boolean,
|
|
): Stats | BigIntStats {
|
|
if (bigInt) return convertFileInfoToBigIntStats(fileInfo);
|
|
return convertFileInfoToStats(fileInfo);
|
|
}
|
|
|
|
export type statCallbackBigInt = (
|
|
err: Error | null,
|
|
stat?: BigIntStats,
|
|
) => void;
|
|
|
|
export type statCallback = (err: Error | null, stat?: Stats) => void;
|
|
|
|
const defaultOptions = { __proto__: null, bigint: false };
|
|
const defaultSyncOptions = {
|
|
__proto__: null,
|
|
bigint: false,
|
|
throwIfNoEntry: true,
|
|
};
|
|
|
|
export function stat(path: string | Buffer | URL, callback: statCallback): void;
|
|
export function stat(
|
|
path: string | Buffer | URL,
|
|
options: { bigint: false },
|
|
callback: statCallback,
|
|
): void;
|
|
export function stat(
|
|
path: string | Buffer | URL,
|
|
options: { bigint: true },
|
|
callback: statCallbackBigInt,
|
|
): void;
|
|
export function stat(
|
|
path: string | Buffer | URL,
|
|
options: statCallback | statCallbackBigInt | statOptions = defaultOptions,
|
|
callback?: statCallback | statCallbackBigInt,
|
|
) {
|
|
if (typeof options === "function") {
|
|
callback = options;
|
|
options = defaultOptions;
|
|
}
|
|
callback = makeCallback(callback);
|
|
path = getValidatedPathToString(path);
|
|
|
|
PromisePrototypeThen(
|
|
Deno.stat(path),
|
|
(stat) => callback(null, CFISBIS(stat, options.bigint)),
|
|
(err) =>
|
|
callback(
|
|
denoErrorToNodeError(err, { syscall: "stat", path }),
|
|
),
|
|
);
|
|
}
|
|
|
|
export const statPromise = promisify(stat) as (
|
|
& ((path: string | Buffer | URL) => Promise<Stats>)
|
|
& ((
|
|
path: string | Buffer | URL,
|
|
options: { bigint: false },
|
|
) => Promise<Stats>)
|
|
& ((
|
|
path: string | Buffer | URL,
|
|
options: { bigint: true },
|
|
) => Promise<BigIntStats>)
|
|
);
|
|
|
|
export function statSync(path: string | Buffer | URL): Stats;
|
|
export function statSync(
|
|
path: string | Buffer | URL,
|
|
options: { bigint: false; throwIfNoEntry: true },
|
|
): Stats;
|
|
export function statSync(
|
|
path: string | Buffer | URL,
|
|
options: { bigint: false; throwIfNoEntry: false },
|
|
): Stats | undefined;
|
|
export function statSync(
|
|
path: string | Buffer | URL,
|
|
options: { bigint: true; throwIfNoEntry: true },
|
|
): BigIntStats;
|
|
export function statSync(
|
|
path: string | Buffer | URL,
|
|
options: { bigint: true; throwIfNoEntry: false },
|
|
): BigIntStats | undefined;
|
|
export function statSync(
|
|
path: string | Buffer | URL,
|
|
options: statOptions = defaultSyncOptions,
|
|
): Stats | BigIntStats | undefined {
|
|
path = getValidatedPathToString(path);
|
|
|
|
try {
|
|
const origin = Deno.statSync(path);
|
|
return CFISBIS(origin, options.bigint);
|
|
} catch (err) {
|
|
if (
|
|
options?.throwIfNoEntry === false &&
|
|
ObjectPrototypeIsPrototypeOf(Deno.errors.NotFound.prototype, err)
|
|
) {
|
|
return;
|
|
}
|
|
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, err)) {
|
|
throw denoErrorToNodeError(err as Error, {
|
|
syscall: "stat",
|
|
path,
|
|
});
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|