-
Notifications
You must be signed in to change notification settings - Fork 260
Expand file tree
/
Copy pathutils.ts
More file actions
99 lines (80 loc) · 2.8 KB
/
utils.ts
File metadata and controls
99 lines (80 loc) · 2.8 KB
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
96
97
98
99
import { createLogger } from '@sourcebot/shared';
import { FileTreeItem, FileTreeNode } from "./types";
export const logger = createLogger('git');
// @note: reject refs starting with '-' to prevent git flag injection.
export const isGitRefValid = (ref: string): boolean => {
return !ref.startsWith('-');
}
// @note: we don't allow directory traversal
// or null bytes in the path.
export const isPathValid = (path: string) => {
const pathSegments = path.split('/');
if (pathSegments.some(segment => segment === '..') || path.includes('\0')) {
return false;
}
return true;
}
export const normalizePath = (path: string): string => {
// Normalize the path by...
let normalizedPath = path;
// ... adding a trailing slash if it doesn't have one.
// This is important since ls-tree won't return the contents
// of a directory if it doesn't have a trailing slash.
if (!normalizedPath.endsWith('/')) {
normalizedPath = `${normalizedPath}/`;
}
// ... removing any leading slashes. This is needed since
// the path is relative to the repository's root, so we
// need a relative path.
if (normalizedPath.startsWith('/')) {
normalizedPath = normalizedPath.slice(1);
}
return normalizedPath;
}
export const compareFileTreeItems = (a: FileTreeItem, b: FileTreeItem): number => {
if (a.type !== b.type) {
return a.type === 'tree' ? -1 : 1;
}
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
}
export const buildFileTree = (flatList: { type: string, path: string }[]): FileTreeNode => {
const root: FileTreeNode = {
name: 'root',
path: '',
type: 'tree',
children: [],
};
for (const item of flatList) {
const parts = item.path.split('/');
let current: FileTreeNode = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isLeaf = i === parts.length - 1;
const nodeType = isLeaf ? item.type : 'tree';
let next = current.children.find((child: FileTreeNode) => child.name === part && child.type === nodeType);
if (!next) {
next = {
name: part,
path: item.path,
type: nodeType,
children: [],
};
current.children.push(next);
}
current = next;
}
}
const sortTree = (node: FileTreeNode): FileTreeNode => {
if (node.type === 'blob') {
return node;
}
const sortedChildren = node.children
.map(sortTree)
.sort(compareFileTreeItems);
return {
...node,
children: sortedChildren,
};
};
return sortTree(root);
}