-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfs-tree.ts
170 lines (157 loc) · 5.64 KB
/
fs-tree.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import * as fs from "https://deno.land/[email protected]/fs/mod.ts";
import * as path from "https://deno.land/[email protected]/path/mod.ts";
import * as gt from "./govn-tree.ts";
export interface FileSysAssetWalker {
readonly identity: string;
readonly root: string;
readonly rootIsAbsolute: boolean;
readonly options?: fs.WalkOptions;
readonly remarks?: string;
}
export interface FileSysAssetWalkerSupplier {
readonly walker: FileSysAssetWalker;
}
export interface FileSysWalkerAssets extends FileSysAssetWalkerSupplier {
readonly children: FileSysAssetNode[];
readonly descendants: () => Generator<FileSysAssetNode>;
readonly subdirectories: (
maxLevel?: number,
) => Generator<FileSysAssetNode>;
readonly files: (maxLevel?: number) => Generator<FileSysAssetFileNode>;
}
export interface FileSysAssetNode
extends gt.GovnTreeNode<fs.WalkEntry>, FileSysAssetWalkerSupplier {
readonly parent?: FileSysAssetNode;
readonly children: FileSysAssetNode[];
readonly ancestors: FileSysAssetNode[];
readonly fileInfo: () => Promise<Deno.FileInfo>;
readonly fileInfoSync: () => Deno.FileInfo;
readonly descendants: () => Generator<FileSysAssetNode>;
readonly subdirectories: (
maxLevel?: number,
) => Generator<FileSysAssetNode>;
readonly files: (maxLevel?: number) => Generator<FileSysAssetFileNode>;
readonly select: (
relative: string,
noMatch?: (
endIndex: number,
parseError?: boolean,
) => gt.GovnTreeNode<fs.WalkEntry>,
) => FileSysAssetNode | undefined;
}
// deno-lint-ignore no-empty-interface
export interface FileAssetsTreeTerminalSupplier
extends gt.GovnTreeTerminalSupplier<fs.WalkEntry> {
}
export interface FileSysAssetFileNode extends FileSysAssetNode {
readonly terminal: fs.WalkEntry;
}
export function* fileSysAssetsNodeSubdirectories(
parent: { children: FileSysAssetNode[] },
maxLevel?: number,
): Generator<FileSysAssetNode> {
for (const node of parent.children) {
if (typeof node.terminal === "undefined") {
yield node;
}
if (!maxLevel || (node.level <= maxLevel)) {
if (node.children.length > 0) {
yield* fileSysAssetsNodeSubdirectories(node, maxLevel);
}
}
}
}
export function* fileSysAssetsNodeFiles(
parent: { children: FileSysAssetNode[] },
maxLevel?: number,
): Generator<FileSysAssetFileNode> {
for (const node of parent.children) {
if (node.terminal) yield node as FileSysAssetFileNode;
if (!maxLevel || (node.level <= maxLevel)) {
if (node.children.length > 0) {
yield* fileSysAssetsNodeFiles(node, maxLevel);
}
}
}
}
export interface FileSysAssetsTreeRegistrationContext {
walker: FileSysAssetWalker;
destination: gt.GovnTreeNodesSupplier<fs.WalkEntry>;
}
export class FileSysAssetsTree
extends gt.GovernedTree<fs.WalkEntry, FileSysAssetsTreeRegistrationContext> {
readonly assets: FileSysWalkerAssets[] = [];
/**
* Parse the hierarchical "units" which comprise our tree node
* @param ts where we get the fs.WalkEntry from
* @param rc the walker which tells us about our context
* @returns the "units" that will comprise the path in the tree
*/
units(
ts: FileAssetsTreeTerminalSupplier,
rc: FileSysAssetsTreeRegistrationContext,
): string[] {
const we = ts.terminal;
return (rc.walker.rootIsAbsolute
? path.relative(rc.walker.root, we.path)
: we.path).split(path.SEP);
}
/**
* Walk the file system and create nodes for each file in the tree
* @param walker the files to walk
* @returns The assets hierarchy ("tree") of all files in the walker
*/
async consumeAssets(
walker: FileSysAssetWalker,
): Promise<FileSysWalkerAssets> {
const destination: FileSysWalkerAssets = {
walker,
children: [],
descendants: () =>
this.descendants(destination) as Generator<FileSysAssetNode>,
subdirectories: (maxLevel) =>
fileSysAssetsNodeSubdirectories(destination, maxLevel),
files: (maxLevel) => fileSysAssetsNodeFiles(destination, maxLevel),
};
this.assets.push(destination);
// walk each subdirectory and file of the given root and treat each file as
// as "terminal" (leaf) node of our tree
for await (const terminal of fs.walk(walker.root, walker.options)) {
if (!terminal.isFile) continue;
this.populate(
{ terminal },
{ walker, destination },
{
refineConstructed: (unrefined) => {
// gt.GovernedTree.register will create a generic, "unrefined" and
// untyped node; we want to "refine" or transform the node from a
// generic to a FileSysAssetNode so that all nodes are typesafe.
const transformed: FileSysAssetNode = {
...unrefined,
walker,
parent: unrefined.parent as FileSysAssetNode,
children: unrefined.children as FileSysAssetNode[],
ancestors: unrefined.ancestors as FileSysAssetNode[],
descendants: () =>
this.descendants(transformed) as Generator<FileSysAssetNode>,
select: (query, noMatch) =>
this.selectTreeNode(
unrefined,
query,
noMatch,
) as FileSysAssetNode,
fileInfo: async () => await Deno.stat(terminal.path),
fileInfoSync: () => Deno.statSync(terminal.path),
subdirectories: (maxLevel) =>
fileSysAssetsNodeSubdirectories(transformed, maxLevel),
files: (maxLevel) =>
fileSysAssetsNodeFiles(transformed, maxLevel),
};
return transformed;
},
},
);
}
return destination;
}
}