UNPKG

4.98 kBJavaScriptView Raw
1"use strict";
2
3const crypto = require("crypto");
4const pathUtil = require("path");
5const fs = require("./utils/fs");
6const validate = require("./utils/validate");
7
8const supportedChecksumAlgorithms = ["md5", "sha1", "sha256", "sha512"];
9
10const symlinkOptions = ["report", "follow"];
11
12const validateInput = (methodName, path, options) => {
13 const methodSignature = `${methodName}(path, [options])`;
14 validate.argument(methodSignature, "path", path, ["string"]);
15 validate.options(methodSignature, "options", options, {
16 checksum: ["string"],
17 mode: ["boolean"],
18 times: ["boolean"],
19 absolutePath: ["boolean"],
20 symlinks: ["string"]
21 });
22
23 if (
24 options &&
25 options.checksum !== undefined &&
26 supportedChecksumAlgorithms.indexOf(options.checksum) === -1
27 ) {
28 throw new Error(
29 `Argument "options.checksum" passed to ${methodSignature} must have one of values: ${supportedChecksumAlgorithms.join(
30 ", "
31 )}`
32 );
33 }
34
35 if (
36 options &&
37 options.symlinks !== undefined &&
38 symlinkOptions.indexOf(options.symlinks) === -1
39 ) {
40 throw new Error(
41 `Argument "options.symlinks" passed to ${methodSignature} must have one of values: ${symlinkOptions.join(
42 ", "
43 )}`
44 );
45 }
46};
47
48const createInspectObj = (path, options, stat) => {
49 const obj = {};
50
51 obj.name = pathUtil.basename(path);
52
53 if (stat.isFile()) {
54 obj.type = "file";
55 obj.size = stat.size;
56 } else if (stat.isDirectory()) {
57 obj.type = "dir";
58 } else if (stat.isSymbolicLink()) {
59 obj.type = "symlink";
60 } else {
61 obj.type = "other";
62 }
63
64 if (options.mode) {
65 obj.mode = stat.mode;
66 }
67
68 if (options.times) {
69 obj.accessTime = stat.atime;
70 obj.modifyTime = stat.mtime;
71 obj.changeTime = stat.ctime;
72 }
73
74 if (options.absolutePath) {
75 obj.absolutePath = path;
76 }
77
78 return obj;
79};
80
81// ---------------------------------------------------------
82// Sync
83// ---------------------------------------------------------
84
85const fileChecksum = (path, algo) => {
86 const hash = crypto.createHash(algo);
87 const data = fs.readFileSync(path);
88 hash.update(data);
89 return hash.digest("hex");
90};
91
92const addExtraFieldsSync = (path, inspectObj, options) => {
93 if (inspectObj.type === "file" && options.checksum) {
94 inspectObj[options.checksum] = fileChecksum(path, options.checksum);
95 } else if (inspectObj.type === "symlink") {
96 inspectObj.pointsAt = fs.readlinkSync(path);
97 }
98};
99
100const inspectSync = (path, options) => {
101 let statOperation = fs.lstatSync;
102 let stat;
103 const opts = options || {};
104
105 if (opts.symlinks === "follow") {
106 statOperation = fs.statSync;
107 }
108
109 try {
110 stat = statOperation(path);
111 } catch (err) {
112 // Detection if path exists
113 if (err.code === "ENOENT") {
114 // Doesn't exist. Return undefined instead of throwing.
115 return undefined;
116 }
117 throw err;
118 }
119
120 const inspectObj = createInspectObj(path, opts, stat);
121 addExtraFieldsSync(path, inspectObj, opts);
122
123 return inspectObj;
124};
125
126// ---------------------------------------------------------
127// Async
128// ---------------------------------------------------------
129
130const fileChecksumAsync = (path, algo) => {
131 return new Promise((resolve, reject) => {
132 const hash = crypto.createHash(algo);
133 const s = fs.createReadStream(path);
134 s.on("data", data => {
135 hash.update(data);
136 });
137 s.on("end", () => {
138 resolve(hash.digest("hex"));
139 });
140 s.on("error", reject);
141 });
142};
143
144const addExtraFieldsAsync = (path, inspectObj, options) => {
145 if (inspectObj.type === "file" && options.checksum) {
146 return fileChecksumAsync(path, options.checksum).then(checksum => {
147 inspectObj[options.checksum] = checksum;
148 return inspectObj;
149 });
150 } else if (inspectObj.type === "symlink") {
151 return fs.readlink(path).then(linkPath => {
152 inspectObj.pointsAt = linkPath;
153 return inspectObj;
154 });
155 }
156 return Promise.resolve(inspectObj);
157};
158
159const inspectAsync = (path, options) => {
160 return new Promise((resolve, reject) => {
161 let statOperation = fs.lstat;
162 const opts = options || {};
163
164 if (opts.symlinks === "follow") {
165 statOperation = fs.stat;
166 }
167
168 statOperation(path)
169 .then(stat => {
170 const inspectObj = createInspectObj(path, opts, stat);
171 addExtraFieldsAsync(path, inspectObj, opts).then(resolve, reject);
172 })
173 .catch(err => {
174 // Detection if path exists
175 if (err.code === "ENOENT") {
176 // Doesn't exist. Return undefined instead of throwing.
177 resolve(undefined);
178 } else {
179 reject(err);
180 }
181 });
182 });
183};
184
185// ---------------------------------------------------------
186// API
187// ---------------------------------------------------------
188
189exports.supportedChecksumAlgorithms = supportedChecksumAlgorithms;
190exports.symlinkOptions = symlinkOptions;
191exports.validateInput = validateInput;
192exports.sync = inspectSync;
193exports.async = inspectAsync;