UNPKG

zb-utils

Version:
237 lines (210 loc) 5.84 kB
const path = require('path'); const fs = require('fs'); const lodash = require('lodash'); const fsUtils = require('fs-util'); /** * @param {string} filepath * @return {boolean} */ const isDirectory = (filepath) => fs.existsSync(filepath) && (fs.lstatSync(filepath).isDirectory()); /** * @param {string} filepath * @return {boolean} */ const isFile = (filepath) => fs.existsSync(filepath) && (fs.lstatSync(filepath).isFile()); /** * @param {string} filepath * @return {boolean} */ const isSymbolicLink = (filepath) => fs.existsSync(filepath) && (fs.lstatSync(filepath).isSymbolicLink()); /** * @param {string} src * @param {Array<string>=} opt_locations * @return {string} * @private */ const findTemplate = (src, opt_locations = []) => { const isAbsolutePath = src === path.join(path.sep, src); if (isAbsolutePath) { if (fs.existsSync(src)) { return src; } } else { for (let i = 0; i < opt_locations.length; i++) { const templatePath = path.join(opt_locations[i], src); if (fs.existsSync(templatePath)) { return templatePath; } } } throw new Error('Can\'t find template: "' + src + '" in [' + opt_locations.join(', ') + ']'); }; /** * @param {string} src * @param {Object} data * @param {{locations: Array<string>}=} opt_options * @return {string} */ const renderTemplate = (src, data, opt_options = {}) => { data._ = lodash; return lodash.template(fs.readFileSync(findTemplate(src, opt_options.locations), 'utf-8'))(data); }; /** * @param {string} src * @param {string} dst * @param {Object} data * @param {Object=} opt_options */ const renderTemplateDir = (src, dst, data, opt_options = {}) => { const renderDir = (src, options, dest) => { if (fs.existsSync(dest)) { if (!isDirectory(dest)) { throw new Error('dest exists and it is not a directory: ' + dest); } } else { fs.mkdirSync(dest); } const files = fs.readdirSync(src); files.forEach((file) => { const srcPath = path.join(src, file); const dstPath = path.join(dest, file); if (isFile(srcPath)) { const exp = /\.tpl$/; if (exp.test(srcPath)) { fs.writeFileSync( dstPath.replace(exp, ''), renderTemplate(srcPath, data, options), 'utf-8' ); } else { fsUtils.cp(srcPath, dstPath); } } else if (isSymbolicLink(srcPath)) { const linkPath = fs.readlinkSync(srcPath); fs.symlinkSync(linkPath, dstPath); } else { renderDir(srcPath, options, dstPath); } }); }; const template = findTemplate(src, opt_options.locations); renderDir(path.join(template, 'root'), opt_options, dst); // rename const rename = {}; const renameConfig = require(path.join(template, 'rename.json'));// eslint-disable-line global-require Object .keys(renameConfig) .forEach((p) => { rename[lodash.template(p)(data)] = lodash.template(renameConfig[p])(data); }); Object .keys(rename) .forEach((p) => fs.renameSync( path.join(dst, p), path.join(dst, rename[p]) )); }; /** * Find ZombieBox application path up from location. * @param {string} location * @return {?string} */ const findUp = (location) => { let cwd = path.join(path.resolve(location), 'dummy'); while ('/' !== (cwd = path.join(cwd, '..'))) { const src = path.join(cwd, 'package.json'); if (fs.existsSync(src)) { const cfg = require(src);// eslint-disable-line global-require if (cfg.dependencies && cfg.dependencies.hasOwnProperty('zombiebox')) { return cwd; } } } return null; }; /** * Recursive read directory * @param {string} dirPath * @return {Array<string>} */ const ls = (dirPath) => { const files = fs.readdirSync(dirPath); let found = []; const len = files.length; for (let i = 0; i < len; i++) { const file = files[i]; const filePath = path.join(dirPath, file); if (isDirectory(filePath)) { found = found.concat(ls(filePath)); } else if (isFile(filePath) || isSymbolicLink(filePath)) { found.push(filePath); } } return found; }; /** * Merge config objects. * @param {Object} obj * @param {...Object} var_args * @return {Object} */ const confMerge = (obj, ...var_args) => { const hasOwnProperty = Object.prototype.hasOwnProperty; var_args.forEach((src) => { for (const prop in src) { if (hasOwnProperty.call(src, prop)) { if (!hasOwnProperty.call(obj, prop)) { obj[prop] = src[prop]; continue; } const objValueIsArray = obj[prop] instanceof Array; const srcValueIsArray = src[prop] instanceof Array; const objValueIsObject = !objValueIsArray && obj[prop] instanceof Object; const srcValueIsObject = !srcValueIsArray && src[prop] instanceof Object; if (srcValueIsArray || objValueIsArray) { if (!objValueIsArray || !srcValueIsArray) { throw new Error('Trying to combine an array with a non-array (' + prop + ')'); } else { // merge arrays obj[prop] = obj[prop].concat(src[prop]) // unique values .reverse() // reverse array for save order .filter((e, i, array) => array.indexOf(e, i + 1) === -1)// unique .reverse(); // reverse array back } } else if (objValueIsObject || srcValueIsObject) { if (!objValueIsObject || !srcValueIsObject) { throw new Error('Trying to combine an object with a non-object (' + prop + ')'); } else { obj[prop] = confMerge(obj[prop], src[prop]); } } else { obj[prop] = src[prop]; } } } }); return obj; }; /** * See: https://github.com/arboleya/fs-util/pull/6/files * @param {string} filepath */ const touch = (filepath) => { fs.writeFileSync(filepath, ''); }; module.exports = { renderTemplate, renderTemplateDir, findUp, copy: fsUtils.cp, copyR: fsUtils.cp_r, touch, rmRf: fsUtils.rm_rf, mkdirP: fsUtils.mkdir_p, find: fsUtils.find, confMerge, ls, lodash, _: lodash };