fix(node): stat/statSync returns instance of fs.Stats (#22294)

Fixes https://github.com/denoland/deno/issues/22291

---------

Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
This commit is contained in:
Divy Srivastava 2024-03-06 18:29:10 +05:30 committed by GitHub
parent 156950828e
commit 6ba0b7952d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 111 additions and 12 deletions

View file

@ -5,13 +5,16 @@
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";
const { ObjectCreate, ObjectAssign } = primordials;
export type statOptions = {
bigint: boolean;
throwIfNoEntry?: boolean;
};
export type Stats = {
interface IStats {
/** ID of the device containing the file.
*
* _Linux/Mac OS only._ */
@ -80,9 +83,84 @@ export type Stats = {
isFile: () => boolean;
isSocket: () => boolean;
isSymbolicLink: () => boolean;
};
}
export type BigIntStats = {
class StatsBase {
constructor(
dev,
mode,
nlink,
uid,
gid,
rdev,
blksize,
ino,
size,
blocks,
) {
this.dev = dev;
this.mode = mode;
this.nlink = nlink;
this.uid = uid;
this.gid = gid;
this.rdev = rdev;
this.blksize = blksize;
this.ino = ino;
this.size = size;
this.blocks = blocks;
}
}
// The Date constructor performs Math.floor() to the timestamp.
// https://www.ecma-international.org/ecma-262/#sec-timeclip
// Since there may be a precision loss when the timestamp is
// converted to a floating point number, we manually round
// the timestamp here before passing it to Date().
function dateFromMs(ms) {
return new Date(Number(ms) + 0.5);
}
export class Stats extends StatsBase {
constructor(
dev,
mode,
nlink,
uid,
gid,
rdev,
blksize,
ino,
size,
blocks,
atimeMs,
mtimeMs,
ctimeMs,
birthtimeMs,
) {
super(
dev,
mode,
nlink,
uid,
gid,
rdev,
blksize,
ino,
size,
blocks,
);
this.atimeMs = atimeMs;
this.mtimeMs = mtimeMs;
this.ctimeMs = ctimeMs;
this.birthtimeMs = birthtimeMs;
this.atime = dateFromMs(atimeMs);
this.mtime = dateFromMs(mtimeMs);
this.ctime = dateFromMs(ctimeMs);
this.birthtime = dateFromMs(birthtimeMs);
}
}
export interface IBigIntStats {
/** ID of the device containing the file.
*
* _Linux/Mac OS only._ */
@ -159,10 +237,13 @@ export type BigIntStats = {
isFile: () => boolean;
isSocket: () => boolean;
isSymbolicLink: () => boolean;
};
}
export class BigIntStats {}
export function convertFileInfoToStats(origin: Deno.FileInfo): Stats {
return {
const stats = ObjectCreate(Stats.prototype);
ObjectAssign(stats, {
dev: origin.dev,
ino: origin.ino,
mode: origin.mode,
@ -189,7 +270,9 @@ export function convertFileInfoToStats(origin: Deno.FileInfo): Stats {
isSocket: () => false,
ctime: origin.mtime,
ctimeMs: origin.mtime?.getTime() || null,
};
});
return stats;
}
function toBigInt(number?: number | null) {
@ -200,7 +283,8 @@ function toBigInt(number?: number | null) {
export function convertFileInfoToBigIntStats(
origin: Deno.FileInfo,
): BigIntStats {
return {
const stats = ObjectCreate(BigIntStats.prototype);
ObjectAssign(stats, {
dev: toBigInt(origin.dev),
ino: toBigInt(origin.ino),
mode: toBigInt(origin.mode),
@ -233,7 +317,8 @@ export function convertFileInfoToBigIntStats(
ctime: origin.mtime,
ctimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null,
ctimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null,
};
});
return stats;
}
// shortcut for Convert File Info to Stats or BigIntStats

View file

@ -10,7 +10,6 @@ import { promisify } from "node:util";
import { getValidatedPath } from "ext:deno_node/internal/fs/utils.mjs";
import { validateFunction } from "ext:deno_node/internal/validators.mjs";
import { stat, Stats } from "ext:deno_node/_fs/_fs_stat.ts";
import { Stats as StatsClass } from "ext:deno_node/internal/fs/utils.mjs";
import { Buffer } from "node:buffer";
import { delay } from "ext:deno_node/_util/async.ts";
@ -22,7 +21,7 @@ const statAsync = async (filename: string): Promise<Stats | null> => {
return emptyStats;
}
};
const emptyStats = new StatsClass(
const emptyStats = new Stats(
0,
0,
0,

View file

@ -69,7 +69,12 @@ import {
} from "ext:deno_node/_fs/_fs_rename.ts";
import { rmdir, rmdirPromise, rmdirSync } from "ext:deno_node/_fs/_fs_rmdir.ts";
import { rm, rmPromise, rmSync } from "ext:deno_node/_fs/_fs_rm.ts";
import { stat, statPromise, statSync } from "ext:deno_node/_fs/_fs_stat.ts";
import {
stat,
statPromise,
Stats,
statSync,
} from "ext:deno_node/_fs/_fs_stat.ts";
import {
symlink,
symlinkPromise,
@ -105,7 +110,6 @@ import {
writeFilePromise,
writeFileSync,
} from "ext:deno_node/_fs/_fs_writeFile.ts";
import { Stats } from "ext:deno_node/internal/fs/utils.mjs";
// @deno-types="./internal/fs/streams.d.ts"
import {
createReadStream,

View file

@ -9,6 +9,8 @@ import {
mkdtempSync,
promises,
readFileSync,
Stats,
statSync,
writeFileSync,
} from "node:fs";
import { constants as fsPromiseConstants, cp } from "node:fs/promises";
@ -97,6 +99,15 @@ Deno.test(
},
);
Deno.test(
"[node/fs statSync] instanceof fs.Stats",
() => {
const stat = statSync("tests/testdata/assets/fixture.json");
assert(stat);
assert(stat instanceof Stats);
},
);
Deno.test(
"[node/fs/promises cp] copy file",
async () => {