Skip to content

Commit

Permalink
feat: path resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
Johan committed Aug 28, 2019
1 parent ca5633e commit 616a32c
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 12 deletions.
2 changes: 0 additions & 2 deletions src/flexiPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,4 @@ export const flexiPath = (path: string): FlexiPath => {
return api;
};

export const Root = () => flexiPath("/");

export default flexiPath;
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import flexiPath, { Root } from "./flexiPath";
import { isRoot } from "./path";
import { flexiPath, isRoot, resolve, root } from "./types";

export * from "./types";

export default {
path: flexiPath,
root: Root,
isRoot
root: () => flexiPath(root),
isRoot,
resolve
};
4 changes: 2 additions & 2 deletions src/parent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { join } from "path";
import flexi, { FlexiPath, isRoot, ParentQuery } from ".";
import flexi, { FlexiPath, isRoot, isValid, ParentQuery } from ".";

export const up = "../";
export const parentPath = (path: string): string => join(path, up);
Expand All @@ -9,7 +9,7 @@ export const parent = (childPath: string | FlexiPath): ParentQuery => (
): FlexiPath | null => {
const path = typeof childPath === "object" ? childPath.path : childPath;

if (isRoot(path)) {
if (!isValid(path) || isRoot(path)) {
return null;
}

Expand Down
12 changes: 10 additions & 2 deletions src/path.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import p from "path";
import { Stats, existsSync, lstatSync, readdirSync, Dirent } from "fs";
import { PathType, FlexiPath } from ".";

const pathAsString = (path: string | FlexiPath): string => {
return typeof path === "object" ? path.path : path;
};

export const isRoot = (path: string | FlexiPath) =>
["./", "/"].find(x => x === pathAsString(path)) !== undefined;
export const root = "/";

export const isValid = (path: string | FlexiPath): boolean => {
return pathAsString(path).startsWith(root);
};

export const isRoot = (path: string | FlexiPath) => pathAsString(path) === root;

export const exists = (path: string | FlexiPath) =>
existsSync(pathAsString(path));
Expand All @@ -32,6 +38,8 @@ export const readDir = (path: string | FlexiPath): Dirent[] =>

export const path = (currentPath: string | FlexiPath) => {
return {
root,
isValid: () => isValid(currentPath),
isRoot: () => isRoot(currentPath),
exists: () => exists(currentPath),
stats: () => stats(currentPath),
Expand Down
41 changes: 41 additions & 0 deletions src/resolve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import flexi, { FlexiPath, ResolveOptions, NavigationState } from ".";

export const resolve = (
path: string | FlexiPath,
options: ResolveOptions
): FlexiPath | null => {
let current: FlexiPath | null =
typeof path === "object" ? path : flexi.path(path);

let state = NavigationState.Default;

if (options.predicate(current)) {
state = NavigationState.Found;
}

if (options.onNavigate !== undefined) {
state = options.onNavigate(current);
}

if (state === NavigationState.Abort) {
return null;
}

if (state !== NavigationState.Found) {
const parent = current.parent();

if (parent !== null) {
current = resolve(parent, options);

if (current !== null && options.onNavigate !== undefined) {
state = options.onNavigate(current);
} else if (current !== null && options.predicate(current)) {
state = NavigationState.Found;
}
}
}

return state === NavigationState.Found ? current : null;
};

export default resolve;
17 changes: 16 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,27 @@ export interface FlexiPath extends ParsedPath {
isRoot(): boolean;
exists(): boolean;
type(): PathType;
parent: ParentQuery; // FlexiPath | null;
parent: ParentQuery;
subDirectories: SubDirectoryQuery;
files(): FlexiPath[];
}

export enum NavigationState {
Default = 0,
Found = 1,
Resume = 2,
Skip = 3,
Abort = 4
}

export interface ResolveOptions {
predicate: (current: ParsedPath) => boolean;
onNavigate?(current: ParsedPath): NavigationState;
}

export * from "./flexiPath";
export * from "./subDirectories";
export * from "./files";
export * from "./parent";
export * from "./path";
export * from "./resolve";
6 changes: 6 additions & 0 deletions test/parent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ describe("parent", () => {
flex = flexi.path(path);
});

it("should be null when path is invalid", () => {
const invalid = flexi.path("invalid");

expect(invalid.parent()).toBeNull();
});

it("should be parent", () => {
const expected = flexi.path("/directory/containing/a/");

Expand Down
46 changes: 46 additions & 0 deletions test/resolve.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ParsedPath } from "path";
import flexi, { NavigationState } from "../src";

describe("resolve", () => {
it("can resolve with predicate", () => {
const path = flexi.path("/fictional/path/with/file.js");
const expected = flexi.path("/fictional/path/");

const result = flexi.resolve(path, { predicate: x => x.name === "path" });

expect(result).toHaveMatchingMembersOf(expected);
});

it("should be null when predicate does not match", () => {
const path = flexi.path("dummy");

const result = flexi.resolve(path, {
predicate: x => x.root === "invalid"
});

expect(result).toBeNull();
});

it("can resolve with navigate skip override", () => {
const path = flexi.path("/fictional/path/with/file.js");
const expected = flexi.path("/fictional/");

const predicate = (x: ParsedPath) => x.name === "path";
const onNavigate = (x: ParsedPath) =>
x.name === "fictional" ? NavigationState.Found : NavigationState.Skip;
const result = flexi.resolve(path, { predicate, onNavigate });

expect(result).toHaveMatchingMembersOf(expected);
});

it("can abort", () => {
const path = flexi.path("/fictional/path/with/file.js");

const result = flexi.resolve(path, {
predicate: x => true,
onNavigate: x => NavigationState.Abort
});

expect(result).toBeNull();
});
});
2 changes: 1 addition & 1 deletion test/subDirectories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("subDirectories", () => {

describe("when passing name", () => {
it("should have subdirectory with parent", () => {
const parent = flexi.path("root/");
const parent = flexi.path("/root/");
const sub = parent.subDirectories("sub");

expect(sub.parent()).toHaveMatchingMembersOf(parent);
Expand Down

0 comments on commit 616a32c

Please sign in to comment.