Skip to content

Commit

Permalink
feat: add support for enforcing a path separator
Browse files Browse the repository at this point in the history
  • Loading branch information
thecodrr committed Aug 13, 2023
1 parent b3774d9 commit b9693d5
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 21 deletions.
20 changes: 19 additions & 1 deletion __tests__/fdir.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { fdir } from "../index";
import fs from "fs";
import mock from "mock-fs";
import { test, beforeEach, TestContext } from "vitest";
import path from "path";
import path, { sep } from "path";

beforeEach(() => {
mock.restore();
Expand Down Expand Up @@ -349,6 +349,15 @@ for (const type of apiTypes) {
mock.restore();
});

test(`[${type}] crawl all files and invert path separator`, async (t) => {
const api = new fdir()
.withPathSeparator(sep === "/" ? "\\" : "/")
.crawl("node_modules");
const files = await api[type]();

t.expect(files.every((f) => !f.includes(sep))).toBeTruthy();
});

test("crawl all files (including symlinks) and throw errors", async (t) => {
mock({
"/other/dir": {},
Expand Down Expand Up @@ -393,6 +402,7 @@ test(`paths should never start with ./`, async (t) => {
new fdir().withBasePath().crawl("./node_modules"),
new fdir().withBasePath().crawl("./"),
new fdir().withRelativePaths().crawl("./"),
new fdir().withRelativePaths().crawl("."),
new fdir().withDirs().crawl("."),
new fdir().onlyDirs().crawl("."),
];
Expand All @@ -413,6 +423,14 @@ test(`ignore withRelativePath if root === ./`, async (t) => {
t.expect(relativeFiles.every((r) => files.includes(r))).toBe(true);
});

test(`add path separator if root path does not end with one`, async (t) => {
const relativeFiles = await new fdir()
.withRelativePaths()
.crawl("node_modules")
.withPromise();
t.expect(relativeFiles.every((r) => !r.startsWith(sep))).toBe(true);
});

test(`there should be no empty directory when using withDirs`, async (t) => {
const files = await new fdir().withDirs().crawl("./").withPromise();
t.expect(files.every((r) => r.length > 0)).toBe(true);
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions src/api/functions/join-path.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { sep } from "path";
import { Options } from "../../types";
import { Options, PathSeparator } from "../../types";

function joinPathWithBasePath(filename: string, directoryPath: string) {
return directoryPath + filename;
}

function joinPathWithRelativePath(relativePath: string) {
relativePath += relativePath[relativePath.length - 1] === sep ? "" : sep;
function joinPathWithRelativePath(root: string) {
return function (filename: string, directoryPath: string) {
return directoryPath.substring(relativePath.length) + filename;
return directoryPath.substring(root.length) + filename;
};
}

function joinPath(filename: string) {
return filename;
}

export function joinDirectoryPath(filename: string, directoryPath: string) {
return directoryPath + filename + sep;
export function joinDirectoryPath(
filename: string,
directoryPath: string,
separator: PathSeparator
) {
return directoryPath + filename + separator;
}

export type JoinPathFunction = (
Expand Down
29 changes: 20 additions & 9 deletions src/api/walker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { resolve as pathResolve, sep } from "path";
import { cleanPath } from "../utils";
import { resolve as pathResolve } from "path";
import { cleanPath, convertSlashes } from "../utils";
import { ResultCallback, WalkerState, Options } from "../types";
import * as joinPath from "./functions/join-path";
import * as pushDirectory from "./functions/push-directory";
Expand Down Expand Up @@ -73,18 +73,21 @@ export class Walker<TOutput extends Output> {
}

private normalizePath(path: string) {
const { resolvePaths, normalizePath, pathSeparator } = this.state.options;
const pathNeedsCleaning =
(process.platform === "win32" && path.includes("/")) ||
path.startsWith(".");

if (this.state.options.resolvePaths) path = pathResolve(path);
if (this.state.options.normalizePath || pathNeedsCleaning)
path = cleanPath(path);
if (resolvePaths) path = pathResolve(path);
if (normalizePath || pathNeedsCleaning) path = cleanPath(path);

if (path === ".") return "";

const needsSeperator = path[path.length - 1] !== sep;
return needsSeperator ? path + sep : path;
const needsSeperator = path[path.length - 1] !== pathSeparator;
return convertSlashes(
needsSeperator ? path + pathSeparator : path,
pathSeparator
);
}

private walk = (entries: Dirent[], directoryPath: string, depth: number) => {
Expand All @@ -106,11 +109,19 @@ export class Walker<TOutput extends Output> {
const filename = this.joinPath(entry.name, directoryPath);
this.pushFile(filename, files, this.state.counts, filters);
} else if (entry.isDirectory()) {
let path = joinPath.joinDirectoryPath(entry.name, directoryPath);
let path = joinPath.joinDirectoryPath(
entry.name,
directoryPath,
this.state.options.pathSeparator
);
if (exclude && exclude(entry.name, path)) continue;
this.walkDirectory(this.state, path, depth - 1, this.walk);
} else if (entry.isSymbolicLink() && resolveSymlinks) {
let path = joinPath.joinDirectoryPath(entry.name, directoryPath);
let path = joinPath.joinDirectoryPath(
entry.name,
directoryPath,
this.state.options.pathSeparator
);
this.resolveSymlink!(path, this.state, (stat, resolvedPath) => {
if (stat.isDirectory()) {
resolvedPath = this.normalizePath(resolvedPath);
Expand Down
7 changes: 7 additions & 0 deletions src/builder/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { sep } from "path";
import {
Output,
OnlyCountsOutput,
Expand Down Expand Up @@ -25,6 +26,7 @@ export class Builder<TReturnType extends Output = PathsOutput> {
private options: Options = {
maxDepth: Infinity,
suppressErrors: true,
pathSeparator: sep,
filters: [],
};

Expand All @@ -37,6 +39,11 @@ export class Builder<TReturnType extends Output = PathsOutput> {
return this as Builder<GroupOutput>;
}

withPathSeparator(separator: "/" | "\\") {
this.options.pathSeparator = separator;
return this;
}

withBasePath() {
this.options.includeBasePath = true;
return this;
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type ResultCallback<TOutput extends Output> = (

export type FilterPredicate = (path: string, isDirectory: boolean) => boolean;
export type ExcludePredicate = (dirName: string, dirPath: string) => boolean;

export type PathSeparator = "/" | "\\";
export type Options = {
includeBasePath?: boolean;
includeDirs?: boolean;
Expand All @@ -56,5 +56,6 @@ export type Options = {
excludeFiles?: boolean;
exclude?: ExcludePredicate;
relativePaths?: boolean;
pathSeparator: PathSeparator;
signal?: AbortSignal;
};
10 changes: 9 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { sep, normalize } from "path";
import { PathSeparator } from "./types";

export function cleanPath(path: string) {
let normalized = normalize(path);

// to account for / path
// we have to remove the last path separator
// to account for / root path
if (normalized.length > 1 && normalized[normalized.length - 1] === sep)
normalized = normalized.substring(0, normalized.length - 1);

return normalized;
}

const SLASHES_REGEX = /[\\/]+/g;
export function convertSlashes(path: string, separator: PathSeparator) {
return path.replace(SLASHES_REGEX, separator);
}

0 comments on commit b9693d5

Please sign in to comment.