1 | "use strict";
|
2 |
|
3 | const crypto = require("crypto");
|
4 | const pathUtil = require("path");
|
5 | const inspect = require("./inspect");
|
6 | const list = require("./list");
|
7 | const validate = require("./utils/validate");
|
8 |
|
9 | const validateInput = (methodName, path, options) => {
|
10 | const methodSignature = `${methodName}(path, [options])`;
|
11 | validate.argument(methodSignature, "path", path, ["string"]);
|
12 | validate.options(methodSignature, "options", options, {
|
13 | checksum: ["string"],
|
14 | relativePath: ["boolean"],
|
15 | symlinks: ["string"]
|
16 | });
|
17 |
|
18 | if (
|
19 | options &&
|
20 | options.checksum !== undefined &&
|
21 | inspect.supportedChecksumAlgorithms.indexOf(options.checksum) === -1
|
22 | ) {
|
23 | throw new Error(
|
24 | `Argument "options.checksum" passed to ${methodSignature} must have one of values: ${inspect.supportedChecksumAlgorithms.join(
|
25 | ", "
|
26 | )}`
|
27 | );
|
28 | }
|
29 |
|
30 | if (
|
31 | options &&
|
32 | options.symlinks !== undefined &&
|
33 | inspect.symlinkOptions.indexOf(options.symlinks) === -1
|
34 | ) {
|
35 | throw new Error(
|
36 | `Argument "options.symlinks" passed to ${methodSignature} must have one of values: ${inspect.symlinkOptions.join(
|
37 | ", "
|
38 | )}`
|
39 | );
|
40 | }
|
41 | };
|
42 |
|
43 | const generateTreeNodeRelativePath = (parent, path) => {
|
44 | if (!parent) {
|
45 | return ".";
|
46 | }
|
47 | return `${parent.relativePath}/${pathUtil.basename(path)}`;
|
48 | };
|
49 |
|
50 |
|
51 |
|
52 | const checksumOfDir = (inspectList, algo) => {
|
53 | const hash = crypto.createHash(algo);
|
54 | inspectList.forEach(inspectObj => {
|
55 | hash.update(inspectObj.name + inspectObj[algo]);
|
56 | });
|
57 | return hash.digest("hex");
|
58 | };
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | const inspectTreeNodeSync = (path, options, parent) => {
|
65 | const treeBranch = inspect.sync(path, options);
|
66 |
|
67 | if (treeBranch) {
|
68 | if (options.relativePath) {
|
69 | treeBranch.relativePath = generateTreeNodeRelativePath(parent, path);
|
70 | }
|
71 |
|
72 | if (treeBranch.type === "dir") {
|
73 | treeBranch.size = 0;
|
74 | treeBranch.children = list.sync(path).map(filename => {
|
75 | const subBranchPath = pathUtil.join(path, filename);
|
76 | const treeSubBranch = inspectTreeNodeSync(
|
77 | subBranchPath,
|
78 | options,
|
79 | treeBranch
|
80 | );
|
81 |
|
82 | treeBranch.size += treeSubBranch.size || 0;
|
83 | return treeSubBranch;
|
84 | });
|
85 |
|
86 | if (options.checksum) {
|
87 | treeBranch[options.checksum] = checksumOfDir(
|
88 | treeBranch.children,
|
89 | options.checksum
|
90 | );
|
91 | }
|
92 | }
|
93 | }
|
94 |
|
95 | return treeBranch;
|
96 | };
|
97 |
|
98 | const inspectTreeSync = (path, options) => {
|
99 | const opts = options || {};
|
100 | return inspectTreeNodeSync(path, opts, undefined);
|
101 | };
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | const inspectTreeNodeAsync = (path, options, parent) => {
|
108 | return new Promise((resolve, reject) => {
|
109 | const inspectAllChildren = treeBranch => {
|
110 | return new Promise((resolve2, reject2) => {
|
111 | list.async(path).then(children => {
|
112 | const doNext = index => {
|
113 | if (index === children.length) {
|
114 | if (options.checksum) {
|
115 |
|
116 | treeBranch[options.checksum] = checksumOfDir(
|
117 | treeBranch.children,
|
118 | options.checksum
|
119 | );
|
120 | }
|
121 | resolve2();
|
122 | } else {
|
123 | const subPath = pathUtil.join(path, children[index]);
|
124 | inspectTreeNodeAsync(subPath, options, treeBranch)
|
125 | .then(treeSubBranch => {
|
126 | children[index] = treeSubBranch;
|
127 | treeBranch.size += treeSubBranch.size || 0;
|
128 | doNext(index + 1);
|
129 | })
|
130 | .catch(reject2);
|
131 | }
|
132 | };
|
133 |
|
134 | treeBranch.children = children;
|
135 | treeBranch.size = 0;
|
136 |
|
137 | doNext(0);
|
138 | });
|
139 | });
|
140 | };
|
141 |
|
142 | inspect
|
143 | .async(path, options)
|
144 | .then(treeBranch => {
|
145 | if (!treeBranch) {
|
146 |
|
147 | resolve(treeBranch);
|
148 | } else {
|
149 | if (options.relativePath) {
|
150 | treeBranch.relativePath = generateTreeNodeRelativePath(
|
151 | parent,
|
152 | path
|
153 | );
|
154 | }
|
155 |
|
156 | if (treeBranch.type !== "dir") {
|
157 | resolve(treeBranch);
|
158 | } else {
|
159 | inspectAllChildren(treeBranch)
|
160 | .then(() => {
|
161 | resolve(treeBranch);
|
162 | })
|
163 | .catch(reject);
|
164 | }
|
165 | }
|
166 | })
|
167 | .catch(reject);
|
168 | });
|
169 | };
|
170 |
|
171 | const inspectTreeAsync = (path, options) => {
|
172 | const opts = options || {};
|
173 | return inspectTreeNodeAsync(path, opts);
|
174 | };
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | exports.validateInput = validateInput;
|
181 | exports.sync = inspectTreeSync;
|
182 | exports.async = inspectTreeAsync;
|