You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

286 lines
7.5 KiB

/*
* Utilities: A classic collection of JavaScript utilities
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
let fs = require('fs');
let path = require('path');
/**
@name file
@namespace file
*/
let fileUtils = new (function () {
// Recursively copy files and directories
let _copyFile = function (fromPath, toPath, opts) {
let from = path.normalize(fromPath)
let to = path.normalize(toPath)
let options = opts || {}
let fromStat;
let toStat;
let destExists;
let destDoesNotExistErr;
let content;
let filename;
let dirContents;
let targetDir;
fromStat = fs.statSync(from);
try {
//console.dir(to + ' destExists');
toStat = fs.statSync(to);
destExists = true;
}
catch(e) {
//console.dir(to + ' does not exist');
destDoesNotExistErr = e;
destExists = false;
}
// Destination dir or file exists, copy into (directory)
// or overwrite (file)
if (destExists) {
// If there's a rename-via-copy file/dir name passed, use it.
// Otherwise use the actual file/dir name
filename = options.rename || path.basename(from);
// Copying a directory
if (fromStat.isDirectory()) {
dirContents = fs.readdirSync(from);
targetDir = path.join(to, filename);
// We don't care if the target dir already exists
try {
fs.mkdirSync(targetDir, {mode: fromStat.mode & 0o777});
}
catch(e) {
if (e.code !== 'EEXIST') {
throw e;
}
}
for (let i = 0, ii = dirContents.length; i < ii; i++) {
_copyFile(path.join(from, dirContents[i]), targetDir, {preserveMode: options.preserveMode});
}
}
// Copying a file
else {
content = fs.readFileSync(from);
let mode = fromStat.mode & 0o777;
let targetFile = to;
if (toStat.isDirectory()) {
targetFile = path.join(to, filename);
}
let fileExists = fs.existsSync(targetFile);
fs.writeFileSync(targetFile, content);
// If the file didn't already exist, use the original file mode.
// Otherwise, only update the mode if preserverMode is true.
if(!fileExists || options.preserveMode) {
fs.chmodSync(targetFile, mode);
}
}
}
// Dest doesn't exist, can't create it
else {
throw destDoesNotExistErr;
}
};
// Remove the given directory
let _rmDir = function (dirPath) {
let dir = path.normalize(dirPath);
let paths = [];
paths = fs.readdirSync(dir);
paths.forEach(function (p) {
let curr = path.join(dir, p);
let stat = fs.lstatSync(curr);
if (stat.isDirectory()) {
_rmDir(curr);
}
else {
try {
fs.unlinkSync(curr);
} catch(e) {
if (e.code === 'EPERM') {
fs.chmodSync(curr, parseInt(666, 8));
fs.unlinkSync(curr);
} else {
throw e;
}
}
}
});
fs.rmdirSync(dir);
};
/**
@name file#cpR
@public
@function
@description Copies a directory/file to a destination
@param {String} fromPath The source path to copy from
@param {String} toPath The destination path to copy to
@param {Object} opts Options to use
@param {Boolean} [opts.preserveMode] If target file already exists, this
determines whether the original file's mode is copied over. The default of
false mimics the behavior of the `cp` command line tool. (Default: false)
*/
this.cpR = function (fromPath, toPath, options) {
let from = path.normalize(fromPath);
let to = path.normalize(toPath);
let toStat;
let doesNotExistErr;
let filename;
let opts = options || {};
if (from == to) {
throw new Error('Cannot copy ' + from + ' to itself.');
}
// Handle rename-via-copy
try {
toStat = fs.statSync(to);
}
catch(e) {
doesNotExistErr = e;
// Get abs path so it's possible to check parent dir
if (!this.isAbsolute(to)) {
to = path.join(process.cwd(), to);
}
// Save the file/dir name
filename = path.basename(to);
// See if a parent dir exists, so there's a place to put the
/// renamed file/dir (resets the destination for the copy)
to = path.dirname(to);
try {
toStat = fs.statSync(to);
}
catch(e) {}
if (toStat && toStat.isDirectory()) {
// Set the rename opt to pass to the copy func, will be used
// as the new file/dir name
opts.rename = filename;
//console.log('filename ' + filename);
}
else {
throw doesNotExistErr;
}
}
_copyFile(from, to, opts);
};
/**
@name file#mkdirP
@public
@function
@description Create the given directory(ies) using the given mode permissions
@param {String} dir The directory to create
@param {Number} mode The mode to give the created directory(ies)(Default: 0755)
*/
this.mkdirP = function (dir, mode) {
let dirPath = path.normalize(dir);
let paths = dirPath.split(/\/|\\/);
let currPath = '';
let next;
if (paths[0] == '' || /^[A-Za-z]+:/.test(paths[0])) {
currPath = paths.shift() || '/';
currPath = path.join(currPath, paths.shift());
//console.log('basedir');
}
while ((next = paths.shift())) {
if (next == '..') {
currPath = path.join(currPath, next);
continue;
}
currPath = path.join(currPath, next);
try {
//console.log('making ' + currPath);
fs.mkdirSync(currPath, mode || parseInt(755, 8));
}
catch(e) {
if (e.code != 'EEXIST') {
throw e;
}
}
}
};
/**
@name file#rmRf
@public
@function
@description Deletes the given directory/file
@param {String} p The path to delete, can be a directory or file
*/
this.rmRf = function (p, options) {
let stat;
try {
stat = fs.lstatSync(p);
if (stat.isDirectory()) {
_rmDir(p);
}
else {
fs.unlinkSync(p);
}
}
catch (e) {}
};
/**
@name file#isAbsolute
@public
@function
@return {Boolean/String} If it's absolute the first character is returned otherwise false
@description Checks if a given path is absolute or relative
@param {String} p Path to check
*/
this.isAbsolute = function (p) {
let match = /^[A-Za-z]+:\\|^\//.exec(p);
if (match && match.length) {
return match[0];
}
return false;
};
/**
@name file#absolutize
@public
@function
@return {String} Returns the absolute path for the given path
@description Returns the absolute path for the given path
@param {String} p The path to get the absolute path for
*/
this.absolutize = function (p) {
if (this.isAbsolute(p)) {
return p;
}
else {
return path.join(process.cwd(), p);
}
};
})();
module.exports = fileUtils;