/**
* Generate project structure.
* @function task.worker.generateStructure
* @example
*
* buildStructure({
* "lib": {
* "string": {
* "_prototype.js": {
* "_type": "file",
* "__serializable": true,
* "_tmpl": "/Users/okuni/WebstormProjects/apeman/sites/apeman.info/abstract/tmpl/js/_prototype.js.hbs",
* "_data": {
* "requireName": "prototype/lib/string",
* "exportsName": "lib.string._prototype"
* },
* "_force": true,
* "_mode": "444"
* }
* },
* }
* }, function(err){
* console.log('done!');
* });
*
* @param {object} config - Configuration object.
* @param {object} data - Structure data.
* @param {function} callback - Callback when done.
* @author Taka Okunishi
*
*/
var path = require('path'),
file = require('../../lib/file'),
debug = require('./_debug'),
loadHbsTmpl = file.loadHbsTmpl,
relativeSymlink = file.relativeSymlink,
_newRenderData = require('./_new_render_data'),
fs = require('fs');
exports = module.exports = function (config, callback) {
var data = exports._flatten(config.data);
require('async').series(
data.map(function (data) {
var create = exports._resolveCreator(data._type);
return create(data);
}),
function (err) {
callback(err);
}
);
};
/**
* Flatten the structure data.
* @param data
* @returns {Array}
* @private
*/
exports._flatten = function (data) {
var __path = data.__path;
var result = [],
keys = Object.keys(data).filter(function (key) {
return !key.match(/^__/);
});
if (keys.length) {
keys.forEach(function (key) {
var value = data[key];
if (typeof(value) === 'undefined') return;
if (value === null) return;
value.__path = __path ? [__path, key].join('/') : key;
if (value.__serializable) {
result.push(value);
} else {
result = result.concat(exports._flatten(value));
}
});
} else {
result.push(data);
}
return result;
};
/**
* Resolve a structure entry creator.
* @param {string} type - Create type.
* @returns {function} - Resolve creator.
* @protected
* @ignore
*/
exports._resolveCreator = function (type) {
switch (type && type.toLowerCase()) {
case 'file':
return exports._createFile;
case 'symlink':
return exports._createSymlink;
default:
return exports._createDir;
}
};
/**
* Create a new file.
* @param data
* @returns {Function}
* @private
*/
exports._createFile = function (data) {
var filename = data.__path,
mode = data._mode;
return function (callback) {
fs.exists(filename, function (exists) {
var abort = exists && !data._force;
if (abort) {
callback();
return;
}
file.forceUnlink(filename, function (err) {
if (err) {
callback(err);
return;
}
var tmplName = data._tmpl;
if (tmplName) {
loadHbsTmpl(tmplName, function (err, tmpl) {
if (err) {
callback(err);
} else {
if (typeof(data._data) === 'string') {
data._data = require(data._data);
}
var rendered = tmpl(_newRenderData(data._data || {}));
exports._createFile._write(filename, rendered, mode, callback);
}
});
} else {
var src = data._src;
if (src) {
fs.readFile(src, function (err, buffer) {
if (err) {
callback(err);
} else {
exports._createFile._write(filename, buffer, mode, callback);
}
});
} else {
exports._createFile._write(filename, '', mode, callback);
}
}
});
});
}
};
exports._createFile._write = function (filename, content, mode, callback) {
exports._writeFileP(filename, content, function (err) {
if (err) {
callback(err);
return;
}
if (mode) {
fs.chmod(filename, mode, function (err) {
if (!err) {
debug.didCreateFile(filename);
}
callback(err);
});
} else {
debug.didCreateFile(filename);
callback(err);
}
});
};
/**
* Create a symbolic link.
* @param data
* @private
*/
exports._createSymlink = function (data) {
var linkName = data.__path;
return function (callback) {
file.forceUnlink(linkName, function (err) {
if (err) {
callback(err);
return;
}
var src = data._src;
file.mkdirP(path.dirname(linkName), function (err) {
if (err) {
callback(err);
return;
}
relativeSymlink(src, linkName, function (err) {
if (!err) {
debug.didCreateLink(linkName, src);
}
callback(err);
});
});
});
};
};
/**
* Create a directory.
* @param {object} data - Directory data.
* @returns {function} - Async function.
* @protected
* @ignore
*/
exports._createDir = function (data) {
var dirname = data.__path;
return function (callback) {
fs.exists(dirname, function (exists) {
if (exists) {
callback(null);
return;
}
file.mkdirP(dirname, function (err) {
if (!err) {
debug.didCreateDir(dirname);
}
callback(err);
});
});
}
};
/**
* Write a file. If not the files parent directory exists, create then first.
* @param {string} filename - Filename to write.
* @param {string} content - Content to write.
* @param {function} callback - Callback when done.
* @protected
* @ignore
*/
exports._writeFileP = function (filename, content, callback) {
file.mkdirP(path.dirname(filename), function () {
fs.writeFile(filename, content, callback);
});
};