-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtreeify-paths.ts
95 lines (86 loc) · 2.57 KB
/
treeify-paths.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
export class PathTree<T = any> {
declare ctx: T;
constructor(public path: string = "") { }
name: string = "";
children: PathTree[] = [];
}
// Legacy
export class Node extends PathTree { }
function fill(
node: PathTree,
paths: PathContexts,
options: Partial<Options> = {}
) {
const children: Record<string, { paths: PathContexts; obj: PathTree }> = {};
paths.forEach(([file, ctx]) => {
const parts = stripSlashes(file).split("/");
const dir = parts[0];
if (!children[dir]) {
const fullPath = `${node.path}/${parts[0]}`;
children[dir] = {
paths: [],
obj: new PathTree(stripSlashes(fullPath)),
};
}
if (parts.length == 1) {
children[dir].obj.name = dir;
children[dir].obj.ctx = ctx;
} else {
parts.shift();
const rest = parts.join("/");
children[dir].paths.push([rest, ctx]);
children[dir].obj.ctx = ctx;
}
});
const keys = Object.keys(children);
if (options.caseInsensitive)
keys.sort((a, b) =>
a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())
);
else keys.sort();
keys.forEach((key) => {
const child = children[key].obj
fill(child, children[key].paths, options);
if (child.name || child.path || child.children.length)
node.children.push(child);
});
if (options.directoriesFirst)
node.children.sort((a, b) => {
if (a.children.length && !b.children.length) return -1;
if (b.children.length && !a.children.length) return 1;
return 0;
});
else if (options.directoriesLast)
node.children.sort((a, b) => {
if (a.children.length && !b.children.length) return 1;
if (b.children.length && !a.children.length) return -1;
return 0;
});
return node;
}
export type Paths = string[];
export type PathContexts<Ctx = any> = [string, Ctx][];
export type Options = {
caseInsensitive: boolean;
directoriesFirst: boolean;
directoriesLast: boolean;
};
export function treeifyPaths<Ctx = undefined>(
paths: Paths | PathContexts<Ctx> = [],
options: Partial<Options> = {}
): PathTree<Ctx> {
const pathCtxs = isPaths(paths)
? paths.map((path): [string, Ctx] => [path, undefined])
: paths;
return fill(new PathTree(), pathCtxs, options);
}
export default treeifyPaths;
const stripSlashes = (path: string) =>
path
.replace(/^\/*/, "")
.replace(/\/*$/, "");
const isPaths = (data: Paths | PathContexts): data is Paths =>
typeof data[0] === "string";
// uncomment to test typescript completion
// const ctxtree = treeifyPaths([["a.txt", { foo: 'bar' }]])
// const tree = treeifyPaths(["a.txt"])