UNPKG

@salesforce/core

Version:

Core libraries to interact with SFDX projects, orgs, and APIs.

378 lines 14.7 kB
"use strict"; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ Object.defineProperty(exports, "__esModule", { value: true }); exports.fs = void 0; const crypto = require("crypto"); const path = require("path"); const util_1 = require("util"); const kit_1 = require("@salesforce/kit"); const fsLib = require("graceful-fs"); const mkdirpLib = require("mkdirp"); const sfdxError_1 = require("../sfdxError"); /** * @deprecated Use fs/promises instead */ exports.fs = Object.assign({}, fsLib, { /** * The default file system mode to use when creating directories. */ DEFAULT_USER_DIR_MODE: '700', /** * The default file system mode to use when creating files. */ DEFAULT_USER_FILE_MODE: '600', /** * A convenience reference to {@link https://nodejs.org/api/fsLib.html#fs_fs_constants} * to reduce the need to import multiple `fs` modules. */ constants: fsLib.constants, /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_readfile_path_options_callback|fsLib.readFile}. */ readFile: util_1.promisify(fsLib.readFile), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_readdir_path_options_callback|fsLib.readdir}. */ readdir: util_1.promisify(fsLib.readdir), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_writefile_file_data_options_callback|fsLib.writeFile}. */ writeFile: util_1.promisify(fsLib.writeFile), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_access_path_mode_callback|fsLib.access}. */ access: util_1.promisify(fsLib.access), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_open_path_flags_mode_callback|fsLib.open}. */ open: util_1.promisify(fsLib.open), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_unlink_path_callback|fsLib.unlink}. */ unlink: util_1.promisify(fsLib.unlink), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_readdir_path_options_callback|fsLib.rmdir}. */ rmdir: util_1.promisify(fsLib.rmdir), /** * Promisified version of {@link https://nodejs.org/api/fsLib.html#fs_fs_fstat_fd_callback|fsLib.stat}. */ stat: util_1.promisify(fsLib.stat), /** * Promisified version of {@link https://npmjs.com/package/mkdirp|mkdirp}. */ // eslint-disable-next-line @typescript-eslint/ban-types mkdirp: (folderPath, mode) => mkdirpLib(folderPath, mode), mkdirpSync: mkdirpLib.sync, /** * Deletes a folder recursively, removing all descending files and folders. * * **Throws** *PathIsNullOrUndefined* The path is not defined. * **Throws** *DirMissingOrNoAccess* The folder or any sub-folder is missing or has no access. * * @param {string} dirPath The path to remove. */ remove: async (dirPath) => { if (!dirPath) { throw new sfdxError_1.SfdxError('Path is null or undefined.', 'PathIsNullOrUndefined'); } try { await exports.fs.access(dirPath, fsLib.constants.R_OK); } catch (err) { throw new sfdxError_1.SfdxError(`The path: ${dirPath} doesn't exist or access is denied.`, 'DirMissingOrNoAccess'); } const files = await exports.fs.readdir(dirPath); const stats = await Promise.all(files.map((file) => exports.fs.stat(path.join(dirPath, file)))); const metas = stats.map((value, index) => Object.assign(value, { path: path.join(dirPath, files[index]) })); await Promise.all(metas.map((meta) => (meta.isDirectory() ? exports.fs.remove(meta.path) : exports.fs.unlink(meta.path)))); await exports.fs.rmdir(dirPath); }, /** * Deletes a folder recursively, removing all descending files and folders. * * NOTE: It is recommended to call the asynchronous `remove` when possible as it will remove all files in parallel rather than serially. * * **Throws** *PathIsNullOrUndefined* The path is not defined. * **Throws** *DirMissingOrNoAccess* The folder or any sub-folder is missing or has no access. * * @param {string} dirPath The path to remove. */ removeSync: (dirPath) => { if (!dirPath) { throw new sfdxError_1.SfdxError('Path is null or undefined.', 'PathIsNullOrUndefined'); } try { exports.fs.accessSync(dirPath, fsLib.constants.R_OK); } catch (err) { throw new sfdxError_1.SfdxError(`The path: ${dirPath} doesn't exist or access is denied.`, 'DirMissingOrNoAccess'); } exports.fs.actOnSync(dirPath, (fullPath, file) => { if (file) { exports.fs.unlinkSync(fullPath); } else { // All files in this directory will be acted on before the directory. exports.fs.rmdirSync(fullPath); } }, 'all'); // Remove the top level exports.fs.rmdirSync(dirPath); }, /** * Searches a file path in an ascending manner (until reaching the filesystem root) for the first occurrence a * specific file name. Resolves with the directory path containing the located file, or `null` if the file was * not found. * * @param dir The directory path in which to start the upward search. * @param file The file name to look for. */ traverseForFile: async (dir, file) => { let foundProjectDir; try { await exports.fs.stat(path.join(dir, file)); foundProjectDir = dir; } catch (err) { if (err && err.code === 'ENOENT') { const nextDir = path.resolve(dir, '..'); if (nextDir !== dir) { // stop at root foundProjectDir = await exports.fs.traverseForFile(nextDir, file); } } } return foundProjectDir; }, /** * Searches a file path synchronously in an ascending manner (until reaching the filesystem root) for the first occurrence a * specific file name. Resolves with the directory path containing the located file, or `null` if the file was * not found. * * @param dir The directory path in which to start the upward search. * @param file The file name to look for. */ traverseForFileSync: (dir, file) => { let foundProjectDir; try { exports.fs.statSync(path.join(dir, file)); foundProjectDir = dir; } catch (err) { if (err && err.code === 'ENOENT') { const nextDir = path.resolve(dir, '..'); if (nextDir !== dir) { // stop at root foundProjectDir = exports.fs.traverseForFileSync(nextDir, file); } } } return foundProjectDir; }, /** * Read a file and convert it to JSON. Returns the contents of the file as a JSON object * * @param jsonPath The path of the file. * @param throwOnEmpty Whether to throw an error if the JSON file is empty. */ readJson: async (jsonPath, throwOnEmpty) => { const fileData = await exports.fs.readFile(jsonPath, 'utf8'); return kit_1.parseJson(fileData, jsonPath, throwOnEmpty); }, /** * Read a file and convert it to JSON. Returns the contents of the file as a JSON object * * @param jsonPath The path of the file. * @param throwOnEmpty Whether to throw an error if the JSON file is empty. */ readJsonSync: (jsonPath, throwOnEmpty) => { const fileData = exports.fs.readFileSync(jsonPath, 'utf8'); return kit_1.parseJson(fileData, jsonPath, throwOnEmpty); }, /** * Read a file and convert it to JSON, throwing an error if the parsed contents are not a `JsonMap`. * * @param jsonPath The path of the file. * @param throwOnEmpty Whether to throw an error if the JSON file is empty. */ readJsonMap: async (jsonPath, throwOnEmpty) => { const fileData = await exports.fs.readFile(jsonPath, 'utf8'); return kit_1.parseJsonMap(fileData, jsonPath, throwOnEmpty); }, /** * Read a file and convert it to JSON, throwing an error if the parsed contents are not a `JsonMap`. * * @param jsonPath The path of the file. * @param throwOnEmpty Whether to throw an error if the JSON file is empty. */ readJsonMapSync: (jsonPath, throwOnEmpty) => { const fileData = exports.fs.readFileSync(jsonPath, 'utf8'); return kit_1.parseJsonMap(fileData, jsonPath, throwOnEmpty); }, /** * Convert a JSON-compatible object to a `string` and write it to a file. * * @param jsonPath The path of the file to write. * @param data The JSON object to write. */ writeJson: async (jsonPath, data, options = {}) => { options = Object.assign({ space: 2 }, options); const fileData = JSON.stringify(data, null, options.space); await exports.fs.writeFile(jsonPath, fileData, { encoding: 'utf8', mode: exports.fs.DEFAULT_USER_FILE_MODE, }); }, /** * Convert a JSON-compatible object to a `string` and write it to a file. * * @param jsonPath The path of the file to write. * @param data The JSON object to write. */ writeJsonSync: (jsonPath, data, options = {}) => { options = Object.assign({ space: 2 }, options); const fileData = JSON.stringify(data, null, options.space); exports.fs.writeFileSync(jsonPath, fileData, { encoding: 'utf8', mode: exports.fs.DEFAULT_USER_FILE_MODE, }); }, /** * Checks if a file path exists * * @param filePath the file path to check the existence of */ fileExists: async (filePath) => { try { await exports.fs.access(filePath); return true; } catch (err) { return false; } }, /** * Checks if a file path exists * * @param filePath the file path to check the existence of */ fileExistsSync: (filePath) => { try { exports.fs.accessSync(filePath); return true; } catch (err) { return false; } }, /** * Recursively act on all files or directories in a directory * * @param dir path to directory * @param perform function to be run on contents of dir * @param onType optional parameter to specify type to actOn * @returns void */ actOn: async (dir, perform, onType = 'file') => { for (const file of await exports.fs.readdir(dir)) { const filePath = path.join(dir, file); const stat = await exports.fs.stat(filePath); if (stat) { if (stat.isDirectory()) { await exports.fs.actOn(filePath, perform, onType); if (onType === 'dir' || onType === 'all') { await perform(filePath); } } else if (stat.isFile() && (onType === 'file' || onType === 'all')) { await perform(filePath, file, dir); } } } }, /** * Recursively act on all files or directories in a directory * * @param dir path to directory * @param perform function to be run on contents of dir * @param onType optional parameter to specify type to actOn * @returns void */ actOnSync: (dir, perform, onType = 'file') => { for (const file of exports.fs.readdirSync(dir)) { const filePath = path.join(dir, file); const stat = exports.fs.statSync(filePath); if (stat) { if (stat.isDirectory()) { exports.fs.actOnSync(filePath, perform, onType); if (onType === 'dir' || onType === 'all') { perform(filePath); } } else if (stat.isFile() && (onType === 'file' || onType === 'all')) { perform(filePath, file, dir); } } } }, /** * Checks if files are the same * * @param file1Path the first file path to check * @param file2Path the second file path to check * @returns boolean */ areFilesEqual: async (file1Path, file2Path) => { try { const file1Size = (await exports.fs.stat(file1Path)).size; const file2Size = (await exports.fs.stat(file2Path)).size; if (file1Size !== file2Size) { return false; } const contentA = await exports.fs.readFile(file1Path); const contentB = await exports.fs.readFile(file2Path); return exports.fs.getContentHash(contentA) === exports.fs.getContentHash(contentB); } catch (err) { throw new sfdxError_1.SfdxError(`The path: ${err.path} doesn't exist or access is denied.`, 'DirMissingOrNoAccess'); } }, /** * Checks if files are the same * * @param file1Path the first file path to check * @param file2Path the second file path to check * @returns boolean */ areFilesEqualSync: (file1Path, file2Path) => { try { const file1Size = exports.fs.statSync(file1Path).size; const file2Size = exports.fs.statSync(file2Path).size; if (file1Size !== file2Size) { return false; } const contentA = exports.fs.readFileSync(file1Path); const contentB = exports.fs.readFileSync(file2Path); return exports.fs.getContentHash(contentA) === exports.fs.getContentHash(contentB); } catch (err) { throw new sfdxError_1.SfdxError(`The path: ${err.path} doesn't exist or access is denied.`, 'DirMissingOrNoAccess'); } }, /** * Creates a hash for the string that's passed in * * @param contents The string passed into the function * @returns string */ getContentHash(contents) { return crypto.createHash('sha1').update(contents).digest('hex'); }, }); //# sourceMappingURL=fs.js.map