830 lines
16 KiB
JavaScript
830 lines
16 KiB
JavaScript
/**
|
|
* XFile Tools Module ...
|
|
* a module for handle all file/folder manipulating task in node js ...
|
|
*
|
|
* Maintainer
|
|
*
|
|
* Hadi Khazaee Asl <https://saherelm.ir> (hadi_khazaee_asl@yahoo.com)
|
|
*/
|
|
|
|
//
|
|
//#region Module Imports ...
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const Path = require('path');
|
|
const http = require('http');
|
|
const https = require('https');
|
|
const XValueTools = require('./x-value.tools');
|
|
//#endregion
|
|
|
|
//
|
|
//#region Constants ...
|
|
/**
|
|
* current os path separators ...
|
|
*/
|
|
const PathSeparator = Path.sep;
|
|
|
|
/**
|
|
* current directory ...
|
|
*/
|
|
const CurrentDir = __dirname;
|
|
//#endregion
|
|
|
|
//
|
|
//#region Actions ...
|
|
//
|
|
//#region Global ...
|
|
/**
|
|
* retrieve a path status ...
|
|
*
|
|
* @param {string} path a path value to check ...
|
|
* @returns an stat object ...
|
|
*/
|
|
function getStatus(path = '') {
|
|
//
|
|
if (!XValueTools.isValidArg(path)) {
|
|
return undefined;
|
|
}
|
|
|
|
//
|
|
return fs.statSync(path);
|
|
}
|
|
|
|
/**
|
|
* retrieve user's Home path ...
|
|
*
|
|
* @returns {string} a path ...
|
|
*/
|
|
function getHomePath() {
|
|
return os.homedir();
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region Path ...
|
|
/**
|
|
* retrieve the base name of specific address path ...
|
|
*
|
|
* @param {string} path address of file or folder ...
|
|
* @returns string ...
|
|
*/
|
|
function basename(path = '') {
|
|
//
|
|
if (!XValueTools.isValidArg(path)) {
|
|
return '';
|
|
}
|
|
|
|
//
|
|
const result = Path.basename(path);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* join several path segments together ...
|
|
*
|
|
* @param {...string} path path params ...
|
|
* @returns a joined paths ...
|
|
*/
|
|
function joinPath(...path) {
|
|
return Path.join(...path);
|
|
}
|
|
|
|
/**
|
|
* resolve a relative path to absolute ...
|
|
*
|
|
* @param {string[]} path a path value to check ...
|
|
* @returns {string}
|
|
*/
|
|
function resolvePath(...path) {
|
|
return Path.resolve(...path);
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region File ...
|
|
/**
|
|
* determines a path destination is a file or not ...
|
|
*
|
|
* @param {string} path a path value to check ...
|
|
* @returns a boolean value ...
|
|
*/
|
|
function isFileExists(path = '') {
|
|
//
|
|
if (!XValueTools.isValidArg(path)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
try {
|
|
const stat = getStatus(path);
|
|
if (!stat) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
return stat.isFile();
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* remove a file ...
|
|
*
|
|
* @param {string} path a file path ...
|
|
* @returns {Promise<boolean>} action done or not ...
|
|
*/
|
|
function removeFile(path = '') {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (!isFileExists(path)) {
|
|
resolve(false);
|
|
}
|
|
|
|
//
|
|
fs.unlink(path, (err) => {
|
|
//
|
|
if (err) {
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(true);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* copy a file to destination path ...
|
|
*
|
|
* @param {string} source source file path ...
|
|
* @param {string} dest dest folder path ...
|
|
* @returns {Promise<boolean>} action done or not ...
|
|
*/
|
|
function copyFile(
|
|
source = '',
|
|
dest = ''
|
|
) {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (
|
|
!isFileExists(source) ||
|
|
!isDirectoryExists(dest)
|
|
) {
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
const destFilePath = Path.join(dest, Path.basename(source));
|
|
fs.copyFile(source, destFilePath, (err) => {
|
|
//
|
|
if (err) {
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(true);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* reading specified file content ...
|
|
*
|
|
* @param {string} path a file path ...
|
|
* @returns {Promise<string>} file content ...
|
|
*/
|
|
function readFile(path = '') {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (!isFileExists(path)) {
|
|
resolve('');
|
|
return;
|
|
}
|
|
|
|
//
|
|
fs.readFile(path, 'utf8', (err, content) => {
|
|
//
|
|
if (err) {
|
|
resolve(undefined);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(content);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* write content to a file ...
|
|
*
|
|
* @param {string} path a file path ...
|
|
* @param {string} content the content which going to write to the file ...
|
|
* @param {bool} overwrite determines file overwrite if exists ...
|
|
* @returns {Promise<boolean>} action done or not ...
|
|
*/
|
|
function writeFile(
|
|
path = '',
|
|
content = '',
|
|
overwrite = true
|
|
) {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (isFileExists(path) && !overwrite) {
|
|
//
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Normalize Content ...
|
|
content = XValueTools.isValidArg(content) ?
|
|
content :
|
|
'';
|
|
|
|
//
|
|
fs.writeFile(path, content, (err) => {
|
|
//
|
|
if (err) {
|
|
//
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(true);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* create a file ...
|
|
*
|
|
* @param {string} path file path ...
|
|
* @param {string} fileName file name ...
|
|
* @returns {Promise<boolean>} action done or not ...
|
|
*/
|
|
function createFile(
|
|
path = '',
|
|
fileName = ''
|
|
) {
|
|
return new Promise((resolve) => {
|
|
//
|
|
const filePath = Path.join(path, fileName);
|
|
if (
|
|
isFileExists(filePath) ||
|
|
!XValueTools.isValidArg(path) ||
|
|
!XValueTools.isValidArg(fileName)
|
|
) {
|
|
//
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
fs.writeFile(filePath, '', (err) => {
|
|
//
|
|
if (err) {
|
|
//
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(true);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* remove a file name extension ...
|
|
*
|
|
* @param {string} name
|
|
* @returns {string} name without extension ...
|
|
*/
|
|
function removeFileExtension(name = '') {
|
|
return name.substring(0, name.lastIndexOf('.')) || name;
|
|
}
|
|
|
|
/**
|
|
* retrieve a file extension ...
|
|
*
|
|
* @param {string} path a path which locate a file ...
|
|
* @returns {string}
|
|
*/
|
|
function getFileExtension(path = '') {
|
|
//
|
|
let result = "";
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (
|
|
!XValueTools.isValidArg(path)
|
|
|| !isFileExists(path)
|
|
) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve just file name ...
|
|
const fileName = basename(path);
|
|
result = fileName.replace(
|
|
removeFileExtension(fileName),
|
|
""
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* retrieve all files list recursively from specific path ...
|
|
*
|
|
* @param {string} path a source folder path ...
|
|
* @param {string[]} extensions which file extensions need to be listed, live empty for all files ...
|
|
* @returns {Promise<string[]>}
|
|
*/
|
|
async function getRecursiveFilesList(
|
|
path = "",
|
|
extensions = []
|
|
) {
|
|
//
|
|
let result = [];
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!XValueTools.isValidArg(path)
|
|
|| !isDirectoryExists(path)
|
|
) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
const contents = await getDirectoryContents(path);
|
|
for(const item of contents) {
|
|
//
|
|
const itemPath = joinPath(
|
|
path,
|
|
item
|
|
);
|
|
|
|
//
|
|
const isItemFile = isFileExists(itemPath);
|
|
if (isItemFile) {
|
|
//
|
|
const itemFileExtension = getFileExtension(itemPath);
|
|
const isFileInSupportedExtensions =
|
|
extensions === undefined || extensions.length === 0
|
|
? true
|
|
: extensions.includes(itemFileExtension)
|
|
;
|
|
if (isFileInSupportedExtensions) {
|
|
result.push(itemPath);
|
|
}
|
|
} else if (isDirectoryExists(itemPath)) {
|
|
//
|
|
const itemPathFiles = await getRecursiveFilesList(itemPath, extensions);
|
|
result.push(...itemPathFiles);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region Directory ...
|
|
/**
|
|
* create a directory ...
|
|
*
|
|
* @param {string} path destination path including dir name ...
|
|
* @param {boolean} recursive create directories recursively ...
|
|
* @returns action done or not ...
|
|
*/
|
|
function createDirectory(
|
|
path = '',
|
|
recursive = true
|
|
) {
|
|
//
|
|
let result = false;
|
|
|
|
//
|
|
if (
|
|
isDirectoryExists(path) ||
|
|
!XValueTools.isValidArg(path)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
try {
|
|
//
|
|
fs.mkdirSync(path, { recursive: recursive });
|
|
result = true;
|
|
return result;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* remove a directory ...
|
|
*
|
|
* @param {string} path destination path including dir name ...
|
|
* @param {boolean} recursive removes directories recursively ...
|
|
* @returns {Promise<boolean>} action done or not ...
|
|
*/
|
|
function removeDirectory(
|
|
path = '',
|
|
recursive = false
|
|
) {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (!isDirectoryExists(path)) {
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
fs.rm(path, {
|
|
recursive
|
|
}, (err) => {
|
|
//
|
|
if (err) {
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(true);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* determines a path destination is a directory or not ...
|
|
*
|
|
* @param {string} path a folder path ...
|
|
* @returns represent destnation path is Directory or not ...
|
|
*/
|
|
function isDirectoryExists(path = '') {
|
|
//
|
|
if (!XValueTools.isValidArg(path)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
try {
|
|
//
|
|
const isExists = fs.existsSync(path);
|
|
if (!isExists) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
const stat = getStatus(path);
|
|
if (!stat) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
const result = stat.isDirectory();
|
|
return result;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* retrieve a directory content ...
|
|
*
|
|
* @param {string} path a folder path ...
|
|
* @returns {Promise<string[]>} a collection of folder files ...
|
|
*/
|
|
function getDirectoryContents(path = '') {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (!isDirectoryExists(path)) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
fs.readdir(path, (err, content) => {
|
|
//
|
|
if (err) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
resolve(content);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* retrieve a directory files ...
|
|
*
|
|
* @param {string} path a folder path ...
|
|
* @returns {Promise<string[]>} a collection of folder files ...
|
|
*/
|
|
function getDirectoryFiles(
|
|
path = '',
|
|
containsHiddenFiles = false
|
|
) {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (!isDirectoryExists(path)) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
fs.readdir(path, (err, content) => {
|
|
//
|
|
if (err) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
if (!containsHiddenFiles) {
|
|
content = content.filter(c => !c.startsWith('.'));
|
|
}
|
|
|
|
//
|
|
const result = [];
|
|
content
|
|
.forEach(c => {
|
|
//
|
|
const cPath = Path.join(path, c);
|
|
if (isFileExists(cPath)) {
|
|
result.push(c);
|
|
}
|
|
});
|
|
|
|
//
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* retrieve a directory folders ...
|
|
*
|
|
* @param {string} path a folder path ...
|
|
* @returns {Promise<string[]>} a collection of folder names ...
|
|
*/
|
|
function getDirectoryFolders(path = '') {
|
|
return new Promise((resolve) => {
|
|
//
|
|
if (!isDirectoryExists(path)) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
fs.readdir(path, (err, content) => {
|
|
//
|
|
if (err) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
const result = [];
|
|
content.forEach(c => {
|
|
//
|
|
const cPath = Path.join(path, c);
|
|
if (isDirectoryExists(cPath)) {
|
|
result.push(c);
|
|
}
|
|
});
|
|
|
|
//
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* copy a folder with all of it's content to dest ...
|
|
*
|
|
* @param {string} source source folder path ...
|
|
* @param {string} dest dest folder path ...
|
|
* @returns {Promise<boolean>} action done or not ...
|
|
*/
|
|
async function copyFolder(
|
|
source = '',
|
|
dest = ''
|
|
) {
|
|
//
|
|
if (
|
|
!isDirectoryExists(source) ||
|
|
!XValueTools.isValidArg(dest) ||
|
|
!XValueTools.isValidArg(source)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
const folderName = Path.basename(source);
|
|
const destPath = Path.join(dest, folderName);
|
|
|
|
//
|
|
// Create Dest Path folder if not exists ...
|
|
if (!isDirectoryExists(destPath)) {
|
|
//
|
|
let result = createDirectory(destPath, true);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Files ...
|
|
const files = await getDirectoryFiles(source);
|
|
if (files && files.constructor === Array && files.length > 0) {
|
|
//
|
|
const filesPromises = files.map(file => copyFile(Path.join(source, file), destPath));
|
|
const filesResult = (await Promise.all(filesPromises)).every(r => !!r);
|
|
if (!filesResult) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Folders ...
|
|
const folders = await getDirectoryFolders(source);
|
|
if (folders && folders.constructor === Array && folders.length > 0) {
|
|
//
|
|
const folderPromises = folders.map(folder => copyFolder(Path.join(source, folder), destPath));
|
|
const filesResult = (await Promise.all(folderPromises)).every(r => !!r);
|
|
if (!filesResult) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
return true;
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region Downloader ...
|
|
/**
|
|
* download a file from specific url and store it ...
|
|
*
|
|
* @param {string} filepath the file name and path which required to put download file on it ...
|
|
* @param {string} url the web url for downloading ...
|
|
*/
|
|
function download(filepath, url) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!XValueTools.isValidArg(url)
|
|
|| !XValueTools.isValidURL(url)
|
|
|| !XValueTools.isValidArg(filepath)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
var file = fs.createWriteStream(filepath);
|
|
|
|
//
|
|
if (url.startsWith('https:')) {
|
|
https.get(url, function (response) {
|
|
response.pipe(file);
|
|
});
|
|
} else if (url.startsWith('http:')) {
|
|
http.get(url, function (response) {
|
|
response.pipe(file);
|
|
});
|
|
}
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region JSON ...
|
|
/**
|
|
* read and parse a JSON content from a file ...
|
|
*
|
|
* @param {string} path source file path ...
|
|
* @returns {any}
|
|
*/
|
|
function readJSON(path = "") {
|
|
//
|
|
// Validate Args ...
|
|
if (!isFileExists(path)) {
|
|
return undefined;
|
|
}
|
|
|
|
//
|
|
let result = undefined;
|
|
try {
|
|
result = require(path);
|
|
} catch {
|
|
result = undefined;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* write a JSON Object content into a file ...
|
|
*
|
|
* @param {string} path dest file path ...
|
|
* @param {any} content an object which required to write to file ...
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async function writeJSON(
|
|
path = "",
|
|
content = undefined
|
|
) {
|
|
//
|
|
let result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (!XValueTools.isValidArg(path)) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Normallize Content ...
|
|
if (content === undefined) {
|
|
content = {};
|
|
}
|
|
|
|
//
|
|
const contentString = XValueTools.beautifyJSON(content);
|
|
|
|
//
|
|
result = await writeFile(
|
|
path,
|
|
contentString,
|
|
true
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
//#endregion
|
|
//#endregion
|
|
|
|
//
|
|
//#region Module Exports ...
|
|
module.exports = {
|
|
//
|
|
CurrentDir,
|
|
PathSeparator,
|
|
|
|
//
|
|
getStatus,
|
|
getHomePath,
|
|
|
|
//
|
|
basename,
|
|
joinPath,
|
|
resolvePath,
|
|
|
|
//
|
|
download,
|
|
|
|
//
|
|
readJSON,
|
|
writeJSON,
|
|
|
|
//
|
|
copyFile,
|
|
readFile,
|
|
writeFile,
|
|
createFile,
|
|
removeFile,
|
|
isFileExists,
|
|
getFileExtension,
|
|
removeFileExtension,
|
|
getRecursiveFilesList,
|
|
|
|
//
|
|
copyFolder,
|
|
createDirectory,
|
|
removeDirectory,
|
|
createDirectory,
|
|
removeDirectory,
|
|
isDirectoryExists,
|
|
isDirectoryExists,
|
|
getDirectoryFiles,
|
|
getDirectoryFolders,
|
|
getDirectoryContents,
|
|
}
|
|
//#endregion
|