summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert Belder <bertbelder@gmail.com>2020-04-29 22:00:31 +0200
committerGitHub <noreply@github.com>2020-04-29 16:00:31 -0400
commit3e6ea6284178df0be4982d9775f47b47b14c6139 (patch)
treeed684ea536e32023e72004110556ad8285126676
parent721a4ad59d4a8bdd8470d6b98839137f14c84ba9 (diff)
BREAKING: Include limited metadata in 'DirEntry' objects (#4941)
This change is to prevent needed a separate stat syscall for each file when using readdir. For consistency, this PR also modifies std's `WalkEntry` interface to extend `DirEntry` with an additional `path` field.
-rw-r--r--cli/js/lib.deno.ns.d.ts5
-rw-r--r--cli/js/ops/fs/read_dir.ts14
-rw-r--r--cli/js/ops/fs/stat.ts2
-rw-r--r--cli/js/tests/read_dir_test.ts13
-rw-r--r--cli/ops/fs.rs28
-rw-r--r--std/fs/README.md4
-rw-r--r--std/fs/copy.ts38
-rw-r--r--std/fs/expand_glob.ts82
-rw-r--r--std/fs/expand_glob_test.ts6
-rw-r--r--std/fs/walk.ts80
-rw-r--r--std/fs/walk_test.ts4
-rwxr-xr-xstd/http/file_server.ts22
-rw-r--r--std/node/_fs/_fs_dirent.ts8
-rw-r--r--std/node/_fs/_fs_dirent_test.ts89
14 files changed, 191 insertions, 204 deletions
diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts
index e58ec0b55..d7d885b5b 100644
--- a/cli/js/lib.deno.ns.d.ts
+++ b/cli/js/lib.deno.ns.d.ts
@@ -1386,8 +1386,11 @@ declare namespace Deno {
* Requires `allow-read` permission. */
export function realpath(path: string): Promise<string>;
- export interface DirEntry extends FileInfo {
+ export interface DirEntry {
name: string;
+ isFile: boolean;
+ isDirectory: boolean;
+ isSymlink: boolean;
}
/** Synchronously reads the directory given by `path` and returns an iterable
diff --git a/cli/js/ops/fs/read_dir.ts b/cli/js/ops/fs/read_dir.ts
index 29b8676ef..7d65fed48 100644
--- a/cli/js/ops/fs/read_dir.ts
+++ b/cli/js/ops/fs/read_dir.ts
@@ -1,21 +1,19 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { sendSync, sendAsync } from "../dispatch_json.ts";
-import { FileInfo, StatResponse, parseFileInfo } from "./stat.ts";
-export interface DirEntry extends FileInfo {
+export interface DirEntry {
name: string;
+ isFile: boolean;
+ isDirectory: boolean;
+ isSymlink: boolean;
}
interface ReadDirResponse {
- entries: StatResponse[];
+ entries: DirEntry[];
}
function res(response: ReadDirResponse): DirEntry[] {
- return response.entries.map(
- (statRes: StatResponse): DirEntry => {
- return { ...parseFileInfo(statRes), name: statRes.name! };
- }
- );
+ return response.entries;
}
export function readdirSync(path: string): Iterable<DirEntry> {
diff --git a/cli/js/ops/fs/stat.ts b/cli/js/ops/fs/stat.ts
index c3563a8c4..e8fd28218 100644
--- a/cli/js/ops/fs/stat.ts
+++ b/cli/js/ops/fs/stat.ts
@@ -29,8 +29,6 @@ export interface StatResponse {
mtime: number | null;
atime: number | null;
birthtime: number | null;
- // Null for stat(), but exists for readdir().
- name: string | null;
// Unix only members
dev: number;
ino: number;
diff --git a/cli/js/tests/read_dir_test.ts b/cli/js/tests/read_dir_test.ts
index 2c7f42103..f1c7ea121 100644
--- a/cli/js/tests/read_dir_test.ts
+++ b/cli/js/tests/read_dir_test.ts
@@ -4,19 +4,14 @@ import { unitTest, assert, assertEquals } from "./test_util.ts";
function assertSameContent(files: Deno.DirEntry[]): void {
let counter = 0;
- for (const file of files) {
- if (file.name === "subdir") {
- assert(file.isDirectory);
- counter++;
- }
-
- if (file.name === "002_hello.ts") {
- assertEquals(file.mode!, Deno.statSync(`cli/tests/${file.name}`).mode!);
+ for (const entry of files) {
+ if (entry.name === "subdir") {
+ assert(entry.isDirectory);
counter++;
}
}
- assertEquals(counter, 2);
+ assertEquals(counter, 1);
}
unitTest({ perms: { read: true } }, function readdirSyncSuccess(): void {
diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs
index 28e260694..55ee1afa3 100644
--- a/cli/ops/fs.rs
+++ b/cli/ops/fs.rs
@@ -460,10 +460,7 @@ fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> serde_json::Value {
}
#[inline(always)]
-fn get_stat_json(
- metadata: std::fs::Metadata,
- maybe_name: Option<String>,
-) -> JsonResult {
+fn get_stat_json(metadata: std::fs::Metadata) -> JsonResult {
// Unix stat member (number types only). 0 if not on unix.
macro_rules! usm {
($member: ident) => {{
@@ -480,7 +477,7 @@ fn get_stat_json(
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
- let mut json_val = json!({
+ let json_val = json!({
"isFile": metadata.is_file(),
"isDirectory": metadata.is_dir(),
"isSymlink": metadata.file_type().is_symlink(),
@@ -502,14 +499,6 @@ fn get_stat_json(
"blksize": usm!(blksize),
"blocks": usm!(blocks),
});
-
- // "name" is an optional field by our design.
- if let Some(name) = maybe_name {
- if let serde_json::Value::Object(ref mut m) = json_val {
- m.insert("name".to_owned(), json!(name));
- }
- }
-
Ok(json_val)
}
@@ -540,7 +529,7 @@ fn op_stat(
} else {
std::fs::metadata(&path)?
};
- get_stat_json(metadata, None)
+ get_stat_json(metadata)
})
}
@@ -599,10 +588,15 @@ fn op_read_dir(
let entries: Vec<_> = std::fs::read_dir(path)?
.filter_map(|entry| {
let entry = entry.unwrap();
- let metadata = entry.metadata().unwrap();
+ let file_type = entry.file_type().unwrap();
// Not all filenames can be encoded as UTF-8. Skip those for now.
- if let Ok(filename) = into_string(entry.file_name()) {
- Some(get_stat_json(metadata, Some(filename)).unwrap())
+ if let Ok(name) = into_string(entry.file_name()) {
+ Some(json!({
+ "name": name,
+ "isFile": file_type.is_file(),
+ "isDirectory": file_type.is_dir(),
+ "isSymlink": file_type.is_symlink()
+ }))
} else {
None
}
diff --git a/std/fs/README.md b/std/fs/README.md
index 289069694..8d1be54bf 100644
--- a/std/fs/README.md
+++ b/std/fs/README.md
@@ -156,8 +156,8 @@ for (const fileInfo of walkSync(".")) {
// Async
async function printFilesNames() {
- for await (const fileInfo of walk()) {
- console.log(fileInfo.filename);
+ for await (const entry of walk()) {
+ console.log(entry.path);
}
}
diff --git a/std/fs/copy.ts b/std/fs/copy.ts
index 27b3c2a3c..d442e46ae 100644
--- a/std/fs/copy.ts
+++ b/std/fs/copy.ts
@@ -157,15 +157,15 @@ async function copyDir(
await Deno.utime(dest, srcStatInfo.atime, srcStatInfo.mtime);
}
- for await (const file of Deno.readdir(src)) {
- const srcPath = path.join(src, file.name);
+ for await (const entry of Deno.readdir(src)) {
+ const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, path.basename(srcPath as string));
- if (file.isDirectory) {
+ if (entry.isSymlink) {
+ await copySymLink(srcPath, destPath, options);
+ } else if (entry.isDirectory) {
await copyDir(srcPath, destPath, options);
- } else if (file.isFile) {
+ } else if (entry.isFile) {
await copyFile(srcPath, destPath, options);
- } else if (file.isSymlink) {
- await copySymLink(srcPath, destPath, options);
}
}
}
@@ -185,16 +185,16 @@ function copyDirSync(src: string, dest: string, options: CopyOptions): void {
Deno.utimeSync(dest, srcStatInfo.atime, srcStatInfo.mtime);
}
- for (const file of Deno.readdirSync(src)) {
- assert(file.name != null, "file.name must be set");
- const srcPath = path.join(src, file.name);
+ for (const entry of Deno.readdirSync(src)) {
+ assert(entry.name != null, "file.name must be set");
+ const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, path.basename(srcPath as string));
- if (file.isDirectory) {
+ if (entry.isSymlink) {
+ copySymlinkSync(srcPath, destPath, options);
+ } else if (entry.isDirectory) {
copyDirSync(srcPath, destPath, options);
- } else if (file.isFile) {
+ } else if (entry.isFile) {
copyFileSync(srcPath, destPath, options);
- } else if (file.isSymlink) {
- copySymlinkSync(srcPath, destPath, options);
}
}
}
@@ -229,12 +229,12 @@ export async function copy(
);
}
- if (srcStat.isDirectory) {
+ if (srcStat.isSymlink) {
+ await copySymLink(src, dest, options);
+ } else if (srcStat.isDirectory) {
await copyDir(src, dest, options);
} else if (srcStat.isFile) {
await copyFile(src, dest, options);
- } else if (srcStat.isSymlink) {
- await copySymLink(src, dest, options);
}
}
@@ -268,11 +268,11 @@ export function copySync(
);
}
- if (srcStat.isDirectory) {
+ if (srcStat.isSymlink) {
+ copySymlinkSync(src, dest, options);
+ } else if (srcStat.isDirectory) {
copyDirSync(src, dest, options);
} else if (srcStat.isFile) {
copyFileSync(src, dest, options);
- } else if (srcStat.isSymlink) {
- copySymlinkSync(src, dest, options);
}
}
diff --git a/std/fs/expand_glob.ts b/std/fs/expand_glob.ts
index 386f61ad5..803c67cdf 100644
--- a/std/fs/expand_glob.ts
+++ b/std/fs/expand_glob.ts
@@ -8,9 +8,15 @@ import {
joinGlobs,
normalize,
} from "../path/mod.ts";
-import { WalkEntry, walk, walkSync } from "./walk.ts";
+import {
+ WalkEntry,
+ createWalkEntry,
+ createWalkEntrySync,
+ walk,
+ walkSync,
+} from "./walk.ts";
import { assert } from "../testing/asserts.ts";
-const { cwd, stat, statSync } = Deno;
+const { cwd } = Deno;
type FileInfo = Deno.FileInfo;
export interface ExpandGlobOptions extends GlobOptions {
@@ -48,6 +54,12 @@ function throwUnlessNotFound(error: Error): void {
}
}
+function comparePath(a: WalkEntry, b: WalkEntry): number {
+ if (a.path < b.path) return -1;
+ if (a.path > b.path) return 1;
+ return 0;
+}
+
/**
* Expand the glob string from the specified `root` directory and yield each
* result as a `WalkEntry` object.
@@ -73,8 +85,8 @@ export async function* expandGlob(
const excludePatterns = exclude
.map(resolveFromRoot)
.map((s: string): RegExp => globToRegExp(s, globOptions));
- const shouldInclude = (filename: string): boolean =>
- !excludePatterns.some((p: RegExp): boolean => !!filename.match(p));
+ const shouldInclude = (path: string): boolean =>
+ !excludePatterns.some((p: RegExp): boolean => !!path.match(p));
const { segments, hasTrailingSep, winRoot } = split(resolveFromRoot(glob));
let fixedRoot = winRoot != undefined ? winRoot : "/";
@@ -86,7 +98,7 @@ export async function* expandGlob(
let fixedRootInfo: WalkEntry;
try {
- fixedRootInfo = { filename: fixedRoot, info: await stat(fixedRoot) };
+ fixedRootInfo = await createWalkEntry(fixedRoot);
} catch (error) {
return throwUnlessNotFound(error);
}
@@ -95,29 +107,29 @@ export async function* expandGlob(
walkInfo: WalkEntry,
globSegment: string
): AsyncIterableIterator<WalkEntry> {
- if (!walkInfo.info.isDirectory) {
+ if (!walkInfo.isDirectory) {
return;
} else if (globSegment == "..") {
- const parentPath = joinGlobs([walkInfo.filename, ".."], globOptions);
+ const parentPath = joinGlobs([walkInfo.path, ".."], globOptions);
try {
if (shouldInclude(parentPath)) {
- return yield { filename: parentPath, info: await stat(parentPath) };
+ return yield await createWalkEntry(parentPath);
}
} catch (error) {
throwUnlessNotFound(error);
}
return;
} else if (globSegment == "**") {
- return yield* walk(walkInfo.filename, {
+ return yield* walk(walkInfo.path, {
includeFiles: false,
skip: excludePatterns,
});
}
- yield* walk(walkInfo.filename, {
+ yield* walk(walkInfo.path, {
maxDepth: 1,
match: [
globToRegExp(
- joinGlobs([walkInfo.filename, globSegment], globOptions),
+ joinGlobs([walkInfo.path, globSegment], globOptions),
globOptions
),
],
@@ -129,27 +141,22 @@ export async function* expandGlob(
for (const segment of segments) {
// Advancing the list of current matches may introduce duplicates, so we
// pass everything through this Map.
- const nextMatchMap: Map<string, FileInfo> = new Map();
+ const nextMatchMap: Map<string, WalkEntry> = new Map();
for (const currentMatch of currentMatches) {
for await (const nextMatch of advanceMatch(currentMatch, segment)) {
- nextMatchMap.set(nextMatch.filename, nextMatch.info);
+ nextMatchMap.set(nextMatch.path, nextMatch);
}
}
- currentMatches = [...nextMatchMap].sort().map(
- ([filename, info]): WalkEntry => ({
- filename,
- info,
- })
- );
+ currentMatches = [...nextMatchMap.values()].sort(comparePath);
}
if (hasTrailingSep) {
currentMatches = currentMatches.filter(
- ({ info }): boolean => info.isDirectory
+ (entry: WalkEntry): boolean => entry.isDirectory
);
}
if (!includeDirs) {
currentMatches = currentMatches.filter(
- ({ info }): boolean => !info.isDirectory
+ (entry: WalkEntry): boolean => !entry.isDirectory
);
}
yield* currentMatches;
@@ -177,8 +184,8 @@ export function* expandGlobSync(
const excludePatterns = exclude
.map(resolveFromRoot)
.map((s: string): RegExp => globToRegExp(s, globOptions));
- const shouldInclude = (filename: string): boolean =>
- !excludePatterns.some((p: RegExp): boolean => !!filename.match(p));
+ const shouldInclude = (path: string): boolean =>
+ !excludePatterns.some((p: RegExp): boolean => !!path.match(p));
const { segments, hasTrailingSep, winRoot } = split(resolveFromRoot(glob));
let fixedRoot = winRoot != undefined ? winRoot : "/";
@@ -190,7 +197,7 @@ export function* expandGlobSync(
let fixedRootInfo: WalkEntry;
try {
- fixedRootInfo = { filename: fixedRoot, info: statSync(fixedRoot) };
+ fixedRootInfo = createWalkEntrySync(fixedRoot);
} catch (error) {
return throwUnlessNotFound(error);
}
@@ -199,29 +206,29 @@ export function* expandGlobSync(
walkInfo: WalkEntry,
globSegment: string
): IterableIterator<WalkEntry> {
- if (!walkInfo.info.isDirectory) {
+ if (!walkInfo.isDirectory) {
return;
} else if (globSegment == "..") {
- const parentPath = joinGlobs([walkInfo.filename, ".."], globOptions);
+ const parentPath = joinGlobs([walkInfo.path, ".."], globOptions);
try {
if (shouldInclude(parentPath)) {
- return yield { filename: parentPath, info: statSync(parentPath) };
+ return yield createWalkEntrySync(parentPath);
}
} catch (error) {
throwUnlessNotFound(error);
}
return;
} else if (globSegment == "**") {
- return yield* walkSync(walkInfo.filename, {
+ return yield* walkSync(walkInfo.path, {
includeFiles: false,
skip: excludePatterns,
});
}
- yield* walkSync(walkInfo.filename, {
+ yield* walkSync(walkInfo.path, {
maxDepth: 1,
match: [
globToRegExp(
- joinGlobs([walkInfo.filename, globSegment], globOptions),
+ joinGlobs([walkInfo.path, globSegment], globOptions),
globOptions
),
],
@@ -233,27 +240,22 @@ export function* expandGlobSync(
for (const segment of segments) {
// Advancing the list of current matches may introduce duplicates, so we
// pass everything through this Map.
- const nextMatchMap: Map<string, FileInfo> = new Map();
+ const nextMatchMap: Map<string, WalkEntry> = new Map();
for (const currentMatch of currentMatches) {
for (const nextMatch of advanceMatch(currentMatch, segment)) {
- nextMatchMap.set(nextMatch.filename, nextMatch.info);
+ nextMatchMap.set(nextMatch.path, nextMatch);
}
}
- currentMatches = [...nextMatchMap].sort().map(
- ([filename, info]): WalkEntry => ({
- filename,
- info,
- })
- );
+ currentMatches = [...nextMatchMap.values()].sort(comparePath);
}
if (hasTrailingSep) {
currentMatches = currentMatches.filter(
- ({ info }): boolean => info.isDirectory
+ (entry: WalkEntry): boolean => entry.isDirectory
);
}
if (!includeDirs) {
currentMatches = currentMatches.filter(
- ({ info }): boolean => !info.isDirectory
+ (entry: WalkEntry): boolean => !entry.isDirectory
);
}
yield* currentMatches;
diff --git a/std/fs/expand_glob_test.ts b/std/fs/expand_glob_test.ts
index a2e6b4333..98abe0d73 100644
--- a/std/fs/expand_glob_test.ts
+++ b/std/fs/expand_glob_test.ts
@@ -19,12 +19,12 @@ async function expandGlobArray(
options: ExpandGlobOptions
): Promise<string[]> {
const paths: string[] = [];
- for await (const { filename } of expandGlob(globString, options)) {
- paths.push(filename);
+ for await (const { path } of expandGlob(globString, options)) {
+ paths.push(path);
}
paths.sort();
const pathsSync = [...expandGlobSync(globString, options)].map(
- ({ filename }): string => filename
+ ({ path }): string => path
);
pathsSync.sort();
assertEquals(paths, pathsSync);
diff --git a/std/fs/walk.ts b/std/fs/walk.ts
index e4cf4674f..a99d35a50 100644
--- a/std/fs/walk.ts
+++ b/std/fs/walk.ts
@@ -2,9 +2,35 @@
// https://golang.org/pkg/path/filepath/#Walk
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
import { unimplemented, assert } from "../testing/asserts.ts";
-import { join } from "../path/mod.ts";
+import { basename, join, normalize } from "../path/mod.ts";
const { readdir, readdirSync, stat, statSync } = Deno;
+export function createWalkEntrySync(path: string): WalkEntry {
+ path = normalize(path);
+ const name = basename(path);
+ const info = statSync(path);
+ return {
+ path,
+ name,
+ isFile: info.isFile,
+ isDirectory: info.isDirectory,
+ isSymlink: info.isSymlink,
+ };
+}
+
+export async function createWalkEntry(path: string): Promise<WalkEntry> {
+ path = normalize(path);
+ const name = basename(path);
+ const info = await stat(path);
+ return {
+ path,
+ name,
+ isFile: info.isFile,
+ isDirectory: info.isDirectory,
+ isSymlink: info.isSymlink,
+ };
+}
+
export interface WalkOptions {
maxDepth?: number;
includeFiles?: boolean;
@@ -16,26 +42,25 @@ export interface WalkOptions {
}
function include(
- filename: string,
+ path: string,
exts?: string[],
match?: RegExp[],
skip?: RegExp[]
): boolean {
- if (exts && !exts.some((ext): boolean => filename.endsWith(ext))) {
+ if (exts && !exts.some((ext): boolean => path.endsWith(ext))) {
return false;
}
- if (match && !match.some((pattern): boolean => !!filename.match(pattern))) {
+ if (match && !match.some((pattern): boolean => !!path.match(pattern))) {
return false;
}
- if (skip && skip.some((pattern): boolean => !!filename.match(pattern))) {
+ if (skip && skip.some((pattern): boolean => !!path.match(pattern))) {
return false;
}
return true;
}
-export interface WalkEntry {
- filename: string;
- info: Deno.FileInfo;
+export interface WalkEntry extends Deno.DirEntry {
+ path: string;
}
/** Walks the file tree rooted at root, yielding each file or directory in the
@@ -52,8 +77,8 @@ export interface WalkEntry {
* - match?: RegExp[];
* - skip?: RegExp[];
*
- * for await (const { filename, info } of walk(".")) {
- * console.log(filename);
+ * for await (const { name, info } of walk(".")) {
+ * console.log(name);
* assert(info.isFile);
* };
*/
@@ -73,13 +98,13 @@ export async function* walk(
return;
}
if (includeDirs && include(root, exts, match, skip)) {
- yield { filename: root, info: await stat(root) };
+ yield await createWalkEntry(root);
}
if (maxDepth < 1 || !include(root, undefined, undefined, skip)) {
return;
}
- for await (const dirEntry of readdir(root)) {
- if (dirEntry.isSymlink) {
+ for await (const entry of readdir(root)) {
+ if (entry.isSymlink) {
if (followSymlinks) {
// TODO(ry) Re-enable followSymlinks.
unimplemented();
@@ -88,14 +113,15 @@ export async function* walk(
}
}
- const filename = join(root, dirEntry.name);
+ assert(entry.name != null);
+ const path = join(root, entry.name);
- if (dirEntry.isFile) {
- if (includeFiles && include(filename, exts, match, skip)) {
- yield { filename, info: dirEntry };
+ if (entry.isFile) {
+ if (includeFiles && include(path, exts, match, skip)) {
+ yield { path, ...entry };
}
} else {
- yield* walk(filename, {
+ yield* walk(path, {
maxDepth: maxDepth - 1,
includeFiles,
includeDirs,
@@ -125,13 +151,13 @@ export function* walkSync(
return;
}
if (includeDirs && include(root, exts, match, skip)) {
- yield { filename: root, info: statSync(root) };
+ yield createWalkEntrySync(root);
}
if (maxDepth < 1 || !include(root, undefined, undefined, skip)) {
return;
}
- for (const dirEntry of readdirSync(root)) {
- if (dirEntry.isSymlink) {
+ for (const entry of readdirSync(root)) {
+ if (entry.isSymlink) {
if (followSymlinks) {
unimplemented();
} else {
@@ -139,15 +165,15 @@ export function* walkSync(
}
}
- assert(dirEntry.name != null);
- const filename = join(root, dirEntry.name);
+ assert(entry.name != null);
+ const path = join(root, entry.name);
- if (dirEntry.isFile) {
- if (includeFiles && include(filename, exts, match, skip)) {
- yield { filename, info: dirEntry };
+ if (entry.isFile) {
+ if (includeFiles && include(path, exts, match, skip)) {
+ yield { path, ...entry };
}
} else {
- yield* walkSync(filename, {
+ yield* walkSync(path, {
maxDepth: maxDepth - 1,
includeFiles,
includeDirs,
diff --git a/std/fs/walk_test.ts b/std/fs/walk_test.ts
index ea9a33773..8bd4577b9 100644
--- a/std/fs/walk_test.ts
+++ b/std/fs/walk_test.ts
@@ -24,8 +24,8 @@ export function testWalk(
Deno.test({ ignore, name: `[walk] ${name}`, fn });
}
-function normalize({ filename }: WalkEntry): string {
- return filename.replace(/\\/g, "/");
+function normalize({ path }: WalkEntry): string {
+ return path.replace(/\\/g, "/");
}
export async function walkArray(
diff --git a/std/http/file_server.ts b/std/http/file_server.ts
index 90f8b8792..c225dbef1 100755
--- a/std/http/file_server.ts
+++ b/std/http/file_server.ts
@@ -140,22 +140,22 @@ async function serveDir(
): Promise<Response> {
const dirUrl = `/${posix.relative(target, dirPath)}`;
const listEntry: EntryInfo[] = [];
- for await (const dirEntry of readdir(dirPath)) {
- const filePath = posix.join(dirPath, dirEntry.name);
- const fileUrl = posix.join(dirUrl, dirEntry.name);
- if (dirEntry.name === "index.html" && dirEntry.isFile) {
+ for await (const entry of readdir(dirPath)) {
+ const filePath = posix.join(dirPath, entry.name);
+ const fileUrl = posix.join(dirUrl, entry.name);
+ if (entry.name === "index.html" && entry.isFile) {
// in case index.html as dir...
return serveFile(req, filePath);
}
// Yuck!
- let mode = null;
+ let fileInfo = null;
try {
- mode = (await stat(filePath)).mode;
+ fileInfo = await stat(filePath);
} catch (e) {}
listEntry.push({
- mode: modeToString(dirEntry.isDirectory, mode),
- size: dirEntry.isFile ? fileLenToString(dirEntry.size) : "",
- name: dirEntry.name,
+ mode: modeToString(entry.isDirectory, fileInfo?.mode ?? null),
+ size: entry.isFile ? fileLenToString(fileInfo?.size ?? 0) : "",
+ name: entry.name,
url: fileUrl,
});
}
@@ -331,8 +331,8 @@ function main(): void {
let response: Response | undefined;
try {
- const info = await stat(fsPath);
- if (info.isDirectory) {
+ const fileInfo = await stat(fsPath);
+ if (fileInfo.isDirectory) {
response = await serveDir(req, fsPath);
} else {
response = await serveFile(req, fsPath);
diff --git a/std/node/_fs/_fs_dirent.ts b/std/node/_fs/_fs_dirent.ts
index 55fbad142..3ea1def42 100644
--- a/std/node/_fs/_fs_dirent.ts
+++ b/std/node/_fs/_fs_dirent.ts
@@ -4,11 +4,15 @@ export default class Dirent {
constructor(private entry: Deno.DirEntry) {}
isBlockDevice(): boolean {
- return this.entry.blocks != null;
+ notImplemented("Deno does not yet support identification of block devices");
+ return false;
}
isCharacterDevice(): boolean {
- return this.entry.blocks == null;
+ notImplemented(
+ "Deno does not yet support identification of character devices"
+ );
+ return false;
}
isDirectory(): boolean {
diff --git a/std/node/_fs/_fs_dirent_test.ts b/std/node/_fs/_fs_dirent_test.ts
index 5288cb3db..43becedd1 100644
--- a/std/node/_fs/_fs_dirent_test.ts
+++ b/std/node/_fs/_fs_dirent_test.ts
@@ -3,107 +3,74 @@ import { assert, assertEquals, assertThrows } from "../../testing/asserts.ts";
import Dirent from "./_fs_dirent.ts";
class DirEntryMock implements Deno.DirEntry {
+ name = "";
isFile = false;
isDirectory = false;
isSymlink = false;
- size = -1;
- mtime = new Date(-1);
- atime = new Date(-1);
- birthtime = new Date(-1);
- name = "";
- dev = -1;
- ino = -1;
- mode = -1;
- nlink = -1;
- uid = -1;
- gid = -1;
- rdev = -1;
- blksize = -1;
- blocks: number | null = null;
}
test({
- name: "Block devices are correctly identified",
- fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
- fileInfo.blocks = 5;
- assert(new Dirent(fileInfo).isBlockDevice());
- assert(!new Dirent(fileInfo).isCharacterDevice());
- },
-});
-
-test({
- name: "Character devices are correctly identified",
- fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
- fileInfo.blocks = null;
- assert(new Dirent(fileInfo).isCharacterDevice());
- assert(!new Dirent(fileInfo).isBlockDevice());
- },
-});
-
-test({
name: "Directories are correctly identified",
fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
- fileInfo.isDirectory = true;
- fileInfo.isFile = false;
- fileInfo.isSymlink = false;
- assert(new Dirent(fileInfo).isDirectory());
- assert(!new Dirent(fileInfo).isFile());
- assert(!new Dirent(fileInfo).isSymbolicLink());
+ const entry: DirEntryMock = new DirEntryMock();
+ entry.isDirectory = true;
+ entry.isFile = false;
+ entry.isSymlink = false;
+ assert(new Dirent(entry).isDirectory());
+ assert(!new Dirent(entry).isFile());
+ assert(!new Dirent(entry).isSymbolicLink());
},
});
test({
name: "Files are correctly identified",
fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
- fileInfo.isDirectory = false;
- fileInfo.isFile = true;
- fileInfo.isSymlink = false;
- assert(!new Dirent(fileInfo).isDirectory());
- assert(new Dirent(fileInfo).isFile());
- assert(!new Dirent(fileInfo).isSymbolicLink());
+ const entry: DirEntryMock = new DirEntryMock();
+ entry.isDirectory = false;
+ entry.isFile = true;
+ entry.isSymlink = false;
+ assert(!new Dirent(entry).isDirectory());
+ assert(new Dirent(entry).isFile());
+ assert(!new Dirent(entry).isSymbolicLink());
},
});
test({
name: "Symlinks are correctly identified",
fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
- fileInfo.isDirectory = false;
- fileInfo.isFile = false;
- fileInfo.isSymlink = true;
- assert(!new Dirent(fileInfo).isDirectory());
- assert(!new Dirent(fileInfo).isFile());
- assert(new Dirent(fileInfo).isSymbolicLink());
+ const entry: DirEntryMock = new DirEntryMock();
+ entry.isDirectory = false;
+ entry.isFile = false;
+ entry.isSymlink = true;
+ assert(!new Dirent(entry).isDirectory());
+ assert(!new Dirent(entry).isFile());
+ assert(new Dirent(entry).isSymbolicLink());
},
});
test({
name: "File name is correct",
fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
- fileInfo.name = "my_file";
- assertEquals(new Dirent(fileInfo).name, "my_file");
+ const entry: DirEntryMock = new DirEntryMock();
+ entry.name = "my_file";
+ assertEquals(new Dirent(entry).name, "my_file");
},
});
test({
name: "Socket and FIFO pipes aren't yet available",
fn() {
- const fileInfo: DirEntryMock = new DirEntryMock();
+ const entry: DirEntryMock = new DirEntryMock();
assertThrows(
() => {
- new Dirent(fileInfo).isFIFO();
+ new Dirent(entry).isFIFO();
},
Error,
"does not yet support"
);
assertThrows(
() => {
- new Dirent(fileInfo).isSocket();
+ new Dirent(entry).isSocket();
},
Error,
"does not yet support"