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
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; |
|
|
|
|