-
Notifications
You must be signed in to change notification settings - Fork 633
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fs/unstable): add
fs.stat
(#6258)
- Loading branch information
Showing
10 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
import * as errors from "./unstable_errors.js"; | ||
|
||
type Class<T> = new (...params: unknown[]) => T; | ||
|
||
type ClassOrT<T> = T extends Class<infer U> ? U : T; | ||
|
||
const mapper = (Ctor: typeof errors[keyof typeof errors]) => (err: Error) => | ||
Object.assign(new Ctor(err.message), { | ||
stack: err.stack, | ||
}) as unknown as ClassOrT<typeof Ctor>; | ||
|
||
const map: Record<string, ReturnType<typeof mapper>> = { | ||
EEXIST: mapper(errors.AlreadyExists), | ||
ENOENT: mapper(errors.NotFound), | ||
EBADF: mapper(errors.BadResource), | ||
}; | ||
|
||
const isNodeErr = (e: unknown): e is Error & { code: string } => { | ||
return e instanceof Error && "code" in e; | ||
}; | ||
|
||
export function mapError<E>(e: E) { | ||
if (!isNodeErr(e)) return e; | ||
return map[e.code]?.(e) || e; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
import type { FileInfo } from "./unstable_types.ts"; | ||
import { isWindows } from "./_utils.ts"; | ||
|
||
export function toFileInfo(s: import("node:fs").Stats): FileInfo { | ||
return { | ||
atime: s.atime, | ||
// TODO(kt3k): uncomment this when we drop support for Deno 1.x | ||
// ctime: s.ctime, | ||
birthtime: s.birthtime, | ||
blksize: isWindows ? null : s.blksize, | ||
blocks: isWindows ? null : s.blocks, | ||
dev: s.dev, | ||
gid: isWindows ? null : s.gid, | ||
ino: isWindows ? null : s.ino, | ||
isDirectory: s.isDirectory(), | ||
isFile: s.isFile(), | ||
isSymlink: s.isSymbolicLink(), | ||
isBlockDevice: isWindows ? null : s.isBlockDevice(), | ||
isCharDevice: isWindows ? null : s.isCharacterDevice(), | ||
isFifo: isWindows ? null : s.isFIFO(), | ||
isSocket: isWindows ? null : s.isSocket(), | ||
mode: isWindows ? null : s.mode, | ||
mtime: s.mtime, | ||
nlink: isWindows ? null : s.nlink, | ||
rdev: isWindows ? null : s.rdev, | ||
size: s.size, | ||
uid: isWindows ? null : s.uid, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
// deno-lint-ignore-file no-explicit-any | ||
|
||
/** | ||
* True if the runtime is Deno, false otherwise. | ||
*/ | ||
export const isDeno = navigator.userAgent?.includes("Deno"); | ||
|
||
/** True if the platform is windows, false otherwise */ | ||
export const isWindows = checkWindows(); | ||
|
||
/** | ||
* @returns true if the platform is Windows, false otherwise. | ||
*/ | ||
function checkWindows(): boolean { | ||
if (typeof navigator !== "undefined" && (navigator as any).platform) { | ||
return (navigator as any).platform.startsWith("Win"); | ||
} else if (typeof (globalThis as any).process !== "undefined") { | ||
return (globalThis as any).platform === "win32"; | ||
} | ||
return false; | ||
} | ||
|
||
export function getNodeFsPromises() { | ||
return (globalThis as any).process.getBuiltinModule("node:fs/promises"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
/** | ||
* Raised when trying to create a resource, like a file, that already | ||
* exits. | ||
*/ | ||
export class AlreadyExists extends Error {} | ||
/** | ||
* The underlying IO resource is invalid or closed, and so the operation | ||
* could not be performed. | ||
*/ | ||
export class BadResource extends Error {} | ||
/** | ||
* Raised when trying to write to a resource and a broken pipe error occurs. | ||
* This can happen when trying to write directly to `stdout` or `stderr` | ||
* and the operating system is unable to pipe the output for a reason | ||
* external to the Deno runtime. | ||
*/ | ||
export class BrokenPipe extends Error {} | ||
/** | ||
* Raised when the underlying IO resource is not available because it is | ||
* being awaited on in another block of code. | ||
*/ | ||
export class Busy extends Error {} | ||
/** | ||
* Raised when an operation to returns data that is invalid for the | ||
* operation being performed. | ||
*/ | ||
export class InvalidData extends Error {} | ||
/** | ||
* Raised when the underlying operating system reports an `EINTR` error. In | ||
* many cases, this underlying IO error will be handled internally within | ||
* Deno, or result in an {@linkcode BadResource} error instead. | ||
*/ | ||
export class Interrupted extends Error {} | ||
/** | ||
* Raised when the underlying operating system indicates that the file | ||
* was not found. | ||
*/ | ||
export class NotFound extends Error {} | ||
/** | ||
* Raised when the underlying operating system indicates the current user | ||
* which the Deno process is running under does not have the appropriate | ||
* permissions to a file or resource. | ||
*/ | ||
export class PermissionDenied extends Error {} | ||
/** | ||
* Raised when the underlying operating system reports that an I/O operation | ||
* has timed out (`ETIMEDOUT`). | ||
*/ | ||
export class TimedOut extends Error {} | ||
/** | ||
* Raised when attempting to read bytes from a resource, but the EOF was | ||
* unexpectedly encountered. | ||
*/ | ||
export class UnexpectedEof extends Error {} | ||
/** | ||
* Raised when expecting to write to a IO buffer resulted in zero bytes | ||
* being written. | ||
*/ | ||
export class WriteZero extends Error {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
// @ts-self-types="./unstable_errors.d.ts" | ||
|
||
import { isDeno } from "./_utils.ts"; | ||
|
||
// please keep sorted | ||
export const AlreadyExists = isDeno | ||
? Deno.errors.AlreadyExists | ||
: class AlreadyExists extends Error {}; | ||
export const BadResource = isDeno | ||
? Deno.errors.BadResource | ||
: class BadResource extends Error {}; | ||
export const BrokenPipe = isDeno | ||
? Deno.errors.BrokenPipe | ||
: class BrokenPipe extends Error {}; | ||
export const Busy = isDeno ? Deno.errors.Busy : class Busy extends Error {}; | ||
export const Interrupted = isDeno | ||
? Deno.errors.Interrupted | ||
: class Interrupted extends Error {}; | ||
export const InvalidData = isDeno | ||
? Deno.errors.InvalidData | ||
: class InvalidData extends Error {}; | ||
export const NotFound = isDeno | ||
? Deno.errors.NotFound | ||
: class NotFound extends Error {}; | ||
export const PermissionDenied = isDeno | ||
? Deno.errors.PermissionDenied | ||
: class PermissionDenied extends Error {}; | ||
export const TimedOut = isDeno | ||
? Deno.errors.TimedOut | ||
: class TimedOut extends Error {}; | ||
export const UnexpectedEof = isDeno | ||
? Deno.errors.UnexpectedEof | ||
: class UnexpectedEof extends Error {}; | ||
export const WriteZero = isDeno | ||
? Deno.errors.WriteZero | ||
: class WriteZero extends Error {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
import { getNodeFsPromises, isDeno } from "./_utils.ts"; | ||
import { mapError } from "./_map_error.ts"; | ||
import { toFileInfo } from "./_to_file_info.ts"; | ||
import type { FileInfo } from "./unstable_types.ts"; | ||
|
||
/** Resolves to a {@linkcode FileInfo} for the specified `path`. Will | ||
* always follow symlinks. | ||
* | ||
* ```ts | ||
* import { assert } from "@std/assert"; | ||
* import { stat } from "@std/fs/unstable-stat"; | ||
* const fileInfo = await Deno.stat("README.md"); | ||
* assert(fileInfo.isFile); | ||
* ``` | ||
* | ||
* Requires `allow-read` permission. | ||
* | ||
* @tags allow-read | ||
* @category File System | ||
*/ | ||
export async function stat(path: string | URL): Promise<FileInfo> { | ||
if (isDeno) { | ||
return Deno.stat(path); | ||
} else { | ||
const fsPromises = getNodeFsPromises(); | ||
try { | ||
const stat = await fsPromises.stat(path); | ||
return toFileInfo(stat); | ||
} catch (error) { | ||
throw mapError(error); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
import { assert, assertRejects } from "@std/assert"; | ||
import { stat } from "./unstable_stat.ts"; | ||
import { NotFound } from "./unstable_errors.js"; | ||
|
||
Deno.test("stat() returns FileInfo for a file", async () => { | ||
const fileInfo = await stat("README.md"); | ||
|
||
assert(fileInfo.isFile); | ||
}); | ||
|
||
Deno.test("stat() returns FileInfo for a directory", async () => { | ||
const fileInfo = await stat("fs"); | ||
|
||
assert(fileInfo.isDirectory); | ||
}); | ||
|
||
Deno.test("stat() rejects with NotFound for a non-existent file", async () => { | ||
await assertRejects(async () => { | ||
await stat("non_existent_file"); | ||
}, NotFound); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
export interface FileInfo { | ||
/** True if this is info for a regular file. Mutually exclusive to | ||
* `FileInfo.isDirectory` and `FileInfo.isSymlink`. */ | ||
isFile: boolean; | ||
/** True if this is info for a regular directory. Mutually exclusive to | ||
* `FileInfo.isFile` and `FileInfo.isSymlink`. */ | ||
isDirectory: boolean; | ||
/** True if this is info for a symlink. Mutually exclusive to | ||
* `FileInfo.isFile` and `FileInfo.isDirectory`. */ | ||
isSymlink: boolean; | ||
/** The size of the file, in bytes. */ | ||
size: number; | ||
/** The last modification time of the file. This corresponds to the `mtime` | ||
* field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This | ||
* may not be available on all platforms. */ | ||
mtime: Date | null; | ||
/** The last access time of the file. This corresponds to the `atime` | ||
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not | ||
* be available on all platforms. */ | ||
atime: Date | null; | ||
/** The creation time of the file. This corresponds to the `birthtime` | ||
* field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may | ||
* not be available on all platforms. */ | ||
birthtime: Date | null; | ||
/** The last change time of the file. This corresponds to the `ctime` | ||
* field from `stat` on Mac/BSD and `ChangeTime` on Windows. This may | ||
* not be available on all platforms. */ | ||
// TODO(kt3k): uncomment this when we drop support for Deno 1.x | ||
// ctime: Date | null; | ||
/** ID of the device containing the file. */ | ||
dev: number; | ||
/** Inode number. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
ino: number | null; | ||
/** The underlying raw `st_mode` bits that contain the standard Unix | ||
* permissions for this file/directory. | ||
*/ | ||
mode: number | null; | ||
/** Number of hard links pointing to this file. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
nlink: number | null; | ||
/** User ID of the owner of this file. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
uid: number | null; | ||
/** Group ID of the owner of this file. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
gid: number | null; | ||
/** Device ID of this file. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
rdev: number | null; | ||
/** Blocksize for filesystem I/O. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
blksize: number | null; | ||
/** Number of blocks allocated to the file, in 512-byte units. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
blocks: number | null; | ||
/** True if this is info for a block device. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
isBlockDevice: boolean | null; | ||
/** True if this is info for a char device. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
isCharDevice: boolean | null; | ||
/** True if this is info for a fifo. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
isFifo: boolean | null; | ||
/** True if this is info for a socket. | ||
* | ||
* _Linux/Mac OS only._ */ | ||
isSocket: boolean | null; | ||
} |