UNPKG

14.7 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright (c) 2020, salesforce.com, inc.
4 * All rights reserved.
5 * Licensed under the BSD 3-Clause license.
6 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7 */
8Object.defineProperty(exports, "__esModule", { value: true });
9exports.fs = void 0;
10const crypto = require("crypto");
11const path = require("path");
12const util_1 = require("util");
13const kit_1 = require("@salesforce/kit");
14const fsLib = require("graceful-fs");
15const mkdirpLib = require("mkdirp");
16const sfdxError_1 = require("../sfdxError");
17/**
18 * @deprecated Use fs/promises instead
19 */
20exports.fs = Object.assign({}, fsLib, {
21 /**
22 * The default file system mode to use when creating directories.
23 */
24 DEFAULT_USER_DIR_MODE: '700',
25 /**
26 * The default file system mode to use when creating files.
27 */
28 DEFAULT_USER_FILE_MODE: '600',
29 /**
30 * A convenience reference to {@link https://nodejs.org/api/fsLib.html#fs_fs_constants}
31 * to reduce the need to import multiple `fs` modules.
32 */
33 constants: fsLib.constants,
34 /**
35 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_readfile_path_options_callback|fsLib.readFile}.
36 */
37 readFile: util_1.promisify(fsLib.readFile),
38 /**
39 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_readdir_path_options_callback|fsLib.readdir}.
40 */
41 readdir: util_1.promisify(fsLib.readdir),
42 /**
43 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_writefile_file_data_options_callback|fsLib.writeFile}.
44 */
45 writeFile: util_1.promisify(fsLib.writeFile),
46 /**
47 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_access_path_mode_callback|fsLib.access}.
48 */
49 access: util_1.promisify(fsLib.access),
50 /**
51 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_open_path_flags_mode_callback|fsLib.open}.
52 */
53 open: util_1.promisify(fsLib.open),
54 /**
55 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_unlink_path_callback|fsLib.unlink}.
56 */
57 unlink: util_1.promisify(fsLib.unlink),
58 /**
59 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_readdir_path_options_callback|fsLib.rmdir}.
60 */
61 rmdir: util_1.promisify(fsLib.rmdir),
62 /**
63 * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_fstat_fd_callback|fsLib.stat}.
64 */
65 stat: util_1.promisify(fsLib.stat),
66 /**
67 * Promisified version of {@link https://npmjs.com/package/mkdirp|mkdirp}.
68 */
69 // eslint-disable-next-line @typescript-eslint/ban-types
70 mkdirp: (folderPath, mode) => mkdirpLib(folderPath, mode),
71 mkdirpSync: mkdirpLib.sync,
72 /**
73 * Deletes a folder recursively, removing all descending files and folders.
74 *
75 * **Throws** *PathIsNullOrUndefined* The path is not defined.
76 * **Throws** *DirMissingOrNoAccess* The folder or any sub-folder is missing or has no access.
77 *
78 * @param {string} dirPath The path to remove.
79 */
80 remove: async (dirPath) => {
81 if (!dirPath) {
82 throw new sfdxError_1.SfdxError('Path is null or undefined.', 'PathIsNullOrUndefined');
83 }
84 try {
85 await exports.fs.access(dirPath, fsLib.constants.R_OK);
86 }
87 catch (err) {
88 throw new sfdxError_1.SfdxError(`The path: ${dirPath} doesn't exist or access is denied.`, 'DirMissingOrNoAccess');
89 }
90 const files = await exports.fs.readdir(dirPath);
91 const stats = await Promise.all(files.map((file) => exports.fs.stat(path.join(dirPath, file))));
92 const metas = stats.map((value, index) => Object.assign(value, { path: path.join(dirPath, files[index]) }));
93 await Promise.all(metas.map((meta) => (meta.isDirectory() ? exports.fs.remove(meta.path) : exports.fs.unlink(meta.path))));
94 await exports.fs.rmdir(dirPath);
95 },
96 /**
97 * Deletes a folder recursively, removing all descending files and folders.
98 *
99 * NOTE: It is recommended to call the asynchronous `remove` when possible as it will remove all files in parallel rather than serially.
100 *
101 * **Throws** *PathIsNullOrUndefined* The path is not defined.
102 * **Throws** *DirMissingOrNoAccess* The folder or any sub-folder is missing or has no access.
103 *
104 * @param {string} dirPath The path to remove.
105 */
106 removeSync: (dirPath) => {
107 if (!dirPath) {
108 throw new sfdxError_1.SfdxError('Path is null or undefined.', 'PathIsNullOrUndefined');
109 }
110 try {
111 exports.fs.accessSync(dirPath, fsLib.constants.R_OK);
112 }
113 catch (err) {
114 throw new sfdxError_1.SfdxError(`The path: ${dirPath} doesn't exist or access is denied.`, 'DirMissingOrNoAccess');
115 }
116 exports.fs.actOnSync(dirPath, (fullPath, file) => {
117 if (file) {
118 exports.fs.unlinkSync(fullPath);
119 }
120 else {
121 // All files in this directory will be acted on before the directory.
122 exports.fs.rmdirSync(fullPath);
123 }
124 }, 'all');
125 // Remove the top level
126 exports.fs.rmdirSync(dirPath);
127 },
128 /**
129 * Searches a file path in an ascending manner (until reaching the filesystem root) for the first occurrence a
130 * specific file name. Resolves with the directory path containing the located file, or `null` if the file was
131 * not found.
132 *
133 * @param dir The directory path in which to start the upward search.
134 * @param file The file name to look for.
135 */
136 traverseForFile: async (dir, file) => {
137 let foundProjectDir;
138 try {
139 await exports.fs.stat(path.join(dir, file));
140 foundProjectDir = dir;
141 }
142 catch (err) {
143 if (err && err.code === 'ENOENT') {
144 const nextDir = path.resolve(dir, '..');
145 if (nextDir !== dir) {
146 // stop at root
147 foundProjectDir = await exports.fs.traverseForFile(nextDir, file);
148 }
149 }
150 }
151 return foundProjectDir;
152 },
153 /**
154 * Searches a file path synchronously in an ascending manner (until reaching the filesystem root) for the first occurrence a
155 * specific file name. Resolves with the directory path containing the located file, or `null` if the file was
156 * not found.
157 *
158 * @param dir The directory path in which to start the upward search.
159 * @param file The file name to look for.
160 */
161 traverseForFileSync: (dir, file) => {
162 let foundProjectDir;
163 try {
164 exports.fs.statSync(path.join(dir, file));
165 foundProjectDir = dir;
166 }
167 catch (err) {
168 if (err && err.code === 'ENOENT') {
169 const nextDir = path.resolve(dir, '..');
170 if (nextDir !== dir) {
171 // stop at root
172 foundProjectDir = exports.fs.traverseForFileSync(nextDir, file);
173 }
174 }
175 }
176 return foundProjectDir;
177 },
178 /**
179 * Read a file and convert it to JSON. Returns the contents of the file as a JSON object
180 *
181 * @param jsonPath The path of the file.
182 * @param throwOnEmpty Whether to throw an error if the JSON file is empty.
183 */
184 readJson: async (jsonPath, throwOnEmpty) => {
185 const fileData = await exports.fs.readFile(jsonPath, 'utf8');
186 return kit_1.parseJson(fileData, jsonPath, throwOnEmpty);
187 },
188 /**
189 * Read a file and convert it to JSON. Returns the contents of the file as a JSON object
190 *
191 * @param jsonPath The path of the file.
192 * @param throwOnEmpty Whether to throw an error if the JSON file is empty.
193 */
194 readJsonSync: (jsonPath, throwOnEmpty) => {
195 const fileData = exports.fs.readFileSync(jsonPath, 'utf8');
196 return kit_1.parseJson(fileData, jsonPath, throwOnEmpty);
197 },
198 /**
199 * Read a file and convert it to JSON, throwing an error if the parsed contents are not a `JsonMap`.
200 *
201 * @param jsonPath The path of the file.
202 * @param throwOnEmpty Whether to throw an error if the JSON file is empty.
203 */
204 readJsonMap: async (jsonPath, throwOnEmpty) => {
205 const fileData = await exports.fs.readFile(jsonPath, 'utf8');
206 return kit_1.parseJsonMap(fileData, jsonPath, throwOnEmpty);
207 },
208 /**
209 * Read a file and convert it to JSON, throwing an error if the parsed contents are not a `JsonMap`.
210 *
211 * @param jsonPath The path of the file.
212 * @param throwOnEmpty Whether to throw an error if the JSON file is empty.
213 */
214 readJsonMapSync: (jsonPath, throwOnEmpty) => {
215 const fileData = exports.fs.readFileSync(jsonPath, 'utf8');
216 return kit_1.parseJsonMap(fileData, jsonPath, throwOnEmpty);
217 },
218 /**
219 * Convert a JSON-compatible object to a `string` and write it to a file.
220 *
221 * @param jsonPath The path of the file to write.
222 * @param data The JSON object to write.
223 */
224 writeJson: async (jsonPath, data, options = {}) => {
225 options = Object.assign({ space: 2 }, options);
226 const fileData = JSON.stringify(data, null, options.space);
227 await exports.fs.writeFile(jsonPath, fileData, {
228 encoding: 'utf8',
229 mode: exports.fs.DEFAULT_USER_FILE_MODE,
230 });
231 },
232 /**
233 * Convert a JSON-compatible object to a `string` and write it to a file.
234 *
235 * @param jsonPath The path of the file to write.
236 * @param data The JSON object to write.
237 */
238 writeJsonSync: (jsonPath, data, options = {}) => {
239 options = Object.assign({ space: 2 }, options);
240 const fileData = JSON.stringify(data, null, options.space);
241 exports.fs.writeFileSync(jsonPath, fileData, {
242 encoding: 'utf8',
243 mode: exports.fs.DEFAULT_USER_FILE_MODE,
244 });
245 },
246 /**
247 * Checks if a file path exists
248 *
249 * @param filePath the file path to check the existence of
250 */
251 fileExists: async (filePath) => {
252 try {
253 await exports.fs.access(filePath);
254 return true;
255 }
256 catch (err) {
257 return false;
258 }
259 },
260 /**
261 * Checks if a file path exists
262 *
263 * @param filePath the file path to check the existence of
264 */
265 fileExistsSync: (filePath) => {
266 try {
267 exports.fs.accessSync(filePath);
268 return true;
269 }
270 catch (err) {
271 return false;
272 }
273 },
274 /**
275 * Recursively act on all files or directories in a directory
276 *
277 * @param dir path to directory
278 * @param perform function to be run on contents of dir
279 * @param onType optional parameter to specify type to actOn
280 * @returns void
281 */
282 actOn: async (dir, perform, onType = 'file') => {
283 for (const file of await exports.fs.readdir(dir)) {
284 const filePath = path.join(dir, file);
285 const stat = await exports.fs.stat(filePath);
286 if (stat) {
287 if (stat.isDirectory()) {
288 await exports.fs.actOn(filePath, perform, onType);
289 if (onType === 'dir' || onType === 'all') {
290 await perform(filePath);
291 }
292 }
293 else if (stat.isFile() && (onType === 'file' || onType === 'all')) {
294 await perform(filePath, file, dir);
295 }
296 }
297 }
298 },
299 /**
300 * Recursively act on all files or directories in a directory
301 *
302 * @param dir path to directory
303 * @param perform function to be run on contents of dir
304 * @param onType optional parameter to specify type to actOn
305 * @returns void
306 */
307 actOnSync: (dir, perform, onType = 'file') => {
308 for (const file of exports.fs.readdirSync(dir)) {
309 const filePath = path.join(dir, file);
310 const stat = exports.fs.statSync(filePath);
311 if (stat) {
312 if (stat.isDirectory()) {
313 exports.fs.actOnSync(filePath, perform, onType);
314 if (onType === 'dir' || onType === 'all') {
315 perform(filePath);
316 }
317 }
318 else if (stat.isFile() && (onType === 'file' || onType === 'all')) {
319 perform(filePath, file, dir);
320 }
321 }
322 }
323 },
324 /**
325 * Checks if files are the same
326 *
327 * @param file1Path the first file path to check
328 * @param file2Path the second file path to check
329 * @returns boolean
330 */
331 areFilesEqual: async (file1Path, file2Path) => {
332 try {
333 const file1Size = (await exports.fs.stat(file1Path)).size;
334 const file2Size = (await exports.fs.stat(file2Path)).size;
335 if (file1Size !== file2Size) {
336 return false;
337 }
338 const contentA = await exports.fs.readFile(file1Path);
339 const contentB = await exports.fs.readFile(file2Path);
340 return exports.fs.getContentHash(contentA) === exports.fs.getContentHash(contentB);
341 }
342 catch (err) {
343 throw new sfdxError_1.SfdxError(`The path: ${err.path} doesn't exist or access is denied.`, 'DirMissingOrNoAccess');
344 }
345 },
346 /**
347 * Checks if files are the same
348 *
349 * @param file1Path the first file path to check
350 * @param file2Path the second file path to check
351 * @returns boolean
352 */
353 areFilesEqualSync: (file1Path, file2Path) => {
354 try {
355 const file1Size = exports.fs.statSync(file1Path).size;
356 const file2Size = exports.fs.statSync(file2Path).size;
357 if (file1Size !== file2Size) {
358 return false;
359 }
360 const contentA = exports.fs.readFileSync(file1Path);
361 const contentB = exports.fs.readFileSync(file2Path);
362 return exports.fs.getContentHash(contentA) === exports.fs.getContentHash(contentB);
363 }
364 catch (err) {
365 throw new sfdxError_1.SfdxError(`The path: ${err.path} doesn't exist or access is denied.`, 'DirMissingOrNoAccess');
366 }
367 },
368 /**
369 * Creates a hash for the string that's passed in
370 *
371 * @param contents The string passed into the function
372 * @returns string
373 */
374 getContentHash(contents) {
375 return crypto.createHash('sha1').update(contents).digest('hex');
376 },
377});
378//# sourceMappingURL=fs.js.map
\No newline at end of file