Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 98 additions & 61 deletions __tests__/fdir.test.ts

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions __tests__/symlinks.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterAll, beforeAll, beforeEach, describe, test } from "vitest";
import { apiTypes, normalize, root } from "./utils";
import { afterAll, beforeAll, describe, test } from "vitest";
import { apiTypes, normalize, root, execute } from "./utils";
import mock from "mock-fs";
import { fdir, Options } from "../src";
import { fdir } from "../src";
import path from "path";

const fsWithRelativeSymlinks = {
Expand Down Expand Up @@ -154,7 +154,7 @@ for (const type of apiTypes) {

test(`resolve symlinks`, async (t) => {
const api = new fdir().withSymlinks().crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"/other/dir/file-2",
Expand All @@ -166,7 +166,7 @@ for (const type of apiTypes) {

test(`resolve recursive symlinks`, async (t) => {
const api = new fdir().withSymlinks().crawl("/recursive");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"/double/recursive/another-file",
Expand All @@ -184,7 +184,7 @@ for (const type of apiTypes) {
const api = new fdir()
.withSymlinks({ resolvePaths: false })
.crawl("/recursive");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"/recursive/dir/not-recursive/another-file",
Expand Down Expand Up @@ -234,7 +234,7 @@ for (const type of apiTypes) {
.withRelativePaths()
.withErrors()
.crawl("./recursive");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"dir/not-recursive/another-file",
Expand Down Expand Up @@ -284,7 +284,7 @@ for (const type of apiTypes) {
.withRelativePaths()
.withErrors()
.crawl("./recursive");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"..//double/recursive/another-file",
Expand All @@ -302,7 +302,7 @@ for (const type of apiTypes) {
const api = new fdir()
.withSymlinks({ resolvePaths: false })
.crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"/some/dir/dirSymlink/file-1",
Expand All @@ -317,7 +317,7 @@ for (const type of apiTypes) {
.withSymlinks({ resolvePaths: false })
.withRelativePaths()
.crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"dirSymlink/file-1",
Expand All @@ -332,7 +332,7 @@ for (const type of apiTypes) {
.withSymlinks()
.withRelativePaths()
.crawl("./relative/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize([
"../../../../other-relative/dir/file-2",
Expand All @@ -347,7 +347,7 @@ for (const type of apiTypes) {
.withSymlinks()
.exclude((_name, path) => path === resolveSymlinkRoot("/sym/linked/"))
.crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(normalize(["/other/dir/file-2"]));
});

Expand All @@ -358,31 +358,31 @@ for (const type of apiTypes) {
(_name, path) => path === resolveSymlinkRoot("/some/dir/dirSymlink/")
)
.crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize(["/some/dir/fileSymlink"])
);
});

test(`do not resolve symlinks`, async (t) => {
const api = new fdir().crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files.sort()).toStrictEqual(
normalize(["dirSymlink", "fileSymlink", "fileSymlink2"])
);
});

test(`exclude symlinks`, async (t) => {
const api = new fdir({ excludeSymlinks: true }).crawl("/some/dir");
const files = await api[type]();
const files = await execute(api, type);
t.expect(files).toHaveLength(0);
});

test(
"doesn't hang when resolving symlinks in the root directory",
async (t) => {
const api = new fdir().withSymlinks({ resolvePaths: false }).crawl("/");
const files = await api[type]();
const files = await execute(api, type);
const expectedFiles = normalize(["/lib/file-1", "/usr/lib/file-1"]);
for (const expectedFile of expectedFiles) {
t.expect(files).toContain(expectedFile);
Expand Down
20 changes: 19 additions & 1 deletion __tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from "path";
import type { APIBuilder } from "../src/builder/api-builder";
import type { IterableOutput } from "../src/types";

export type APITypes = (typeof apiTypes)[number];
export const apiTypes = ["withPromise", "sync"] as const;
export const apiTypes = ["withPromise", "sync", "withIterator"] as const;

export function root() {
return process.platform === "win32" ? process.cwd().split(path.sep)[0] : "/";
Expand All @@ -22,3 +24,19 @@ export function normalize(paths: string[]) {
path.isAbsolute(p) ? path.resolve(p) : path.normalize(p)
);
}

export async function execute<T extends IterableOutput>(
api: APIBuilder<T>,
type: APITypes
): Promise<T> {
let files: T[number][] = [];

if (type === "withIterator") {
for await (const file of api[type]()) {
files.push(file);
}
} else {
files = await api[type]();
}
return files as T;
}
8 changes: 5 additions & 3 deletions src/api/functions/group-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { Group, Options } from "../../types";
export type GroupFilesFunction = (
groups: Group[],
directory: string,
files: string[]
files: string[],
pushGroup: (group: Group, arr: Group[]) => void
) => void;

const groupFiles: GroupFilesFunction = (
groups: Group[],
directory: string,
files: string[]
files: string[],
pushGroup
) => {
groups.push({ directory, files, dir: directory });
pushGroup({ directory, files, dir: directory }, groups);
};

const empty: GroupFilesFunction = () => {};
Expand Down
29 changes: 21 additions & 8 deletions src/api/functions/push-directory.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,53 @@
import { FilterPredicate, Options } from "../../types";
import { FilterPredicate, Options, Counts } from "../../types";

export type PushDirectoryFunction = (
directoryPath: string,
paths: string[],
pushPath: (path: string, arr: string[]) => void,
counts: Counts,
filters?: FilterPredicate[]
) => void;

function pushDirectoryWithRelativePath(root: string): PushDirectoryFunction {
return function (directoryPath, paths) {
paths.push(directoryPath.substring(root.length) || ".");
return function (directoryPath, paths, pushPath, counts) {
pushPath(directoryPath.substring(root.length) || ".", paths);
counts.directories++;
};
}

function pushDirectoryFilterWithRelativePath(
root: string
): PushDirectoryFunction {
return function (directoryPath, paths, filters) {
return function (directoryPath, paths, pushPath, counts, filters) {
const relativePath = directoryPath.substring(root.length) || ".";
if (filters!.every((filter) => filter(relativePath, true))) {
paths.push(relativePath);
pushPath(relativePath, paths);
counts.directories++;
}
};
}

const pushDirectory: PushDirectoryFunction = (directoryPath, paths) => {
paths.push(directoryPath || ".");
const pushDirectory: PushDirectoryFunction = (
directoryPath,
paths,
pushPath,
counts
) => {
pushPath(directoryPath || ".", paths);
counts.directories++;
};

const pushDirectoryFilter: PushDirectoryFunction = (
directoryPath,
paths,
pushPath,
counts,
filters
) => {
const path = directoryPath || ".";
if (filters!.every((filter) => filter(path, true))) {
paths.push(path);
pushPath(path, paths);
counts.directories++;
}
};

Expand Down
16 changes: 12 additions & 4 deletions src/api/functions/push-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { FilterPredicate, Options, Counts } from "../../types";
export type PushFileFunction = (
directoryPath: string,
paths: string[],
pushPath: (path: string, arr: string[]) => void,
counts: Counts,
filters?: FilterPredicate[]
) => void;

const pushFileFilterAndCount: PushFileFunction = (
filename,
_paths,
_pushPath,
counts,
filters
) => {
Expand All @@ -19,23 +21,29 @@ const pushFileFilterAndCount: PushFileFunction = (
const pushFileFilter: PushFileFunction = (
filename,
paths,
_counts,
pushPath,
counts,
filters
) => {
if (filters!.every((filter) => filter(filename, false))) paths.push(filename);
if (filters!.every((filter) => filter(filename, false))) {
pushPath(filename, paths);
counts.files++;
}
};

const pushFileCount: PushFileFunction = (
_filename,
_paths,
_pushPath,
counts,
_filters
) => {
counts.files++;
};

const pushFile: PushFileFunction = (filename, paths) => {
paths.push(filename);
const pushFile: PushFileFunction = (filename, paths, pushPath, counts) => {
pushPath(filename, paths);
counts.files++;
};

const empty: PushFileFunction = () => {};
Expand Down
6 changes: 5 additions & 1 deletion src/api/functions/resolve-symlink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ export function build(
return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
}

function isRecursive(path: string, resolved: string, state: WalkerState) {
export function isRecursive(
path: string,
resolved: string,
state: WalkerState
) {
if (state.options.useRealPaths)
return isRecursiveUsingRealPaths(resolved, state);

Expand Down
2 changes: 0 additions & 2 deletions src/api/functions/walk-directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const walkAsync: WalkDirectoryFunction = (
const { fs } = state;

state.visited.push(crawlPath);
state.counts.directories++;

// Perf: Node >= 10 introduced withFileTypes that helps us
// skip an extra fs.stat call.
Expand All @@ -46,7 +45,6 @@ const walkSync: WalkDirectoryFunction = (
const { fs } = state;
if (currentDepth < 0) return;
state.visited.push(crawlPath);
state.counts.directories++;

let entries: Dirent[] = [];
try {
Expand Down
Loading
Loading