base-fs-tree
Version:
Base plugin for creating file trees using archy. Requires the base-fs plugin, but can also be used as a gulp plugin.
343 lines (285 loc) • 8.69 kB
JavaScript
var path = require('path');
var utils = require('./utils');
var cache;
/**
* Create file trees
*/
module.exports = function(config) {
config = config || {};
cache = cache || {};
var namespace = config.name || 'default';
cache[namespace] = cache[namespace] || {src: {}, dest: {}};
function plugin(app) {
if (!utils.isValid(app, 'base-fs-tree')) return;
/**
* Create a `tree` view collection if one doesn't already exist
*/
if (typeof app.tree !== 'function') {
app.create('tree', {viewType: 'partial'});
}
if (typeof app.srcTree !== 'function') {
app.create('srcTree', {viewType: 'partial'});
}
var method = app.prePlugins ? 'prePlugins' : 'onStream';
app[method](/./, function(file, next) {
var opts = utils.extend({}, config, app.options);
if (opts.tree !== false && !file.isTree) {
addFile(cache[namespace], file, {}, 'src');
}
next();
});
app.preWrite(/./, function(file, next) {
var opts = utils.extend({}, config, app.options);
file.writeFile = !opts.treeOnly && file.isTree;
next();
});
app.postWrite(/./, function(file, next) {
var opts = utils.extend({}, config, app.options);
if (opts.tree !== false && !file.isTree) {
addFile(cache[namespace], file, {}, 'dest');
}
next();
});
app.define('createTrees', function(options) {
var opts = utils.extend({name: 'default'}, config, app.options, options);
if (opts.tree !== false) {
writeFile(app, cache, 'dest', namespace, opts);
writeFile(app, cache, 'src', namespace, opts);
}
// always reset the cache
cache[namespace] = {src: {}, dest: {}};
});
app.define('compareTrees', function(fn) {
return compare(this, app.trees.views, fn);
});
app.define('createSrcTrees', function(fn) {
return createSrcTrees(this, app.srcTrees.views, fn);
});
return plugin;
};
plugin.capture = function(name, options) {
if (utils.isObject(name)) {
options = name;
name = null;
}
var opts = utils.extend({name: name || 'default'}, config, options);
var prop = opts.name;
cache = cache || {};
cache[prop] = {src: {}, dest: {}};
return utils.through.obj(function(file, enc, next) {
if (opts.tree !== false) {
file._isCaptured = true;
addFile(cache[prop], file, opts, 'src');
}
file.writeFile = opts.treeOnly === false;
next(null, file);
});
};
plugin.create = function(name, options) {
if (utils.isObject(name)) {
options = name;
name = null;
}
var opts = utils.extend({name: name || 'default'}, config, options);
var prop = opts.name;
cache = cache || {};
cache[prop] || (cache[prop] = {src: {}, dest: {}});
return utils.through.obj(function(file, enc, next) {
if (opts.tree !== false) {
if (!file._isCaptured) {
addFile(cache[prop], file, opts, 'src');
}
addFile(cache[prop], file, opts, 'dest');
}
file.writeFile = opts.treeOnly === false;
next();
}, function(next) {
if (opts.tree !== false) {
this.push(createFile(cache[prop], 'dest', prop, opts));
this.push(createFile(cache[prop], 'src', prop, opts));
cache[prop] = cache[prop] = {src: {}, dest: {}};
}
next();
});
};
/**
* Create a file
*/
function createFile(tree, name, prop, options) {
var opts = utils.extend({}, options);
var str = create(tree[name], {label: 'cwd'});
var file = new utils.File({path: `${prop}-${name}.txt`, contents: new Buffer(str)});
if (typeof opts.treename === 'function') {
opts.treename(file);
}
file.writeFile = true;
file.render = false;
file.layout = null;
file.isTree = true;
utils.contents.sync(file);
return file;
}
function writeFile(app, tree, prop, namespace, options) {
var opts = utils.extend({label: 'cwd'}, config, options);
var obj = tree[namespace][prop];
var str = create(obj, {label: namespace});
var destPath = opts.destPath || `${opts.name}-${prop}.txt`;
if (typeof opts.dest === 'string') {
destPath = path.resolve(opts.dest, destPath);
}
var file = new utils.File({path: destPath, contents: new Buffer(str)});
utils.contents.sync(file);
if (typeof opts.treename === 'function') {
opts.treename(file);
}
var destBase = file.dirname;
if (typeof opts.dest === 'function') {
destBase = path.resolve(opts.dest(file) || destBase);
}
destPath = path.resolve(destBase, file.path);
if (prop === 'dest') {
var keys = Object.keys(utils.clone(obj[opts.label]));
app.tree(opts.name, {contents: new Buffer(str), tree: keys});
} else {
app.srcTree(opts.name, {contents: new Buffer(str)});
}
if (opts.write !== false) {
utils.write.sync(destPath, file.contents.toString());
}
}
function addFile(tree, file, options, name) {
if (isCached(file, name)) return;
if (isIgnored(file)) return;
var opts = utils.extend({label: 'cwd', prefix: ' '}, options);
var cwd = typeof opts.cwd === 'string'
? path.resolve(opts.cwd)
: process.cwd();
var relative = path.relative(cwd, path.resolve(file.history[0]));
if (name === 'dest') {
relative = file.relative;
}
var filepath = path.join(opts.label, relative);
addBranch(tree[name], filepath);
}
return plugin;
};
function isCached(file, name) {
if (!name && file._added) {
return true;
}
if (name) {
file._added = file._added || {};
if (file._added[name]) {
return true;
}
file._added[name] = true;
return false;
}
file._added = true;
return false;
}
function isIgnored(file) {
return /(\.DS_Store|Thumbs\.db)/i.test(file.path);
}
function create(tree, options) {
var opts = utils.extend({label: 'cwd', prefix: ' '}, options);
var obj = tree[opts.label] || tree.cwd;
var archytree = createTree(obj, {}, opts.label);
var str = utils.archy(archytree, opts.prefix, opts);
return str.replace(/^[^\n]+/, ' .');
}
function addBranch(tree, path) {
var segs = path.split(/[\\\/]+/);
var len = segs.length;
var idx = -1;
while (++idx < len) {
if (!tree[segs[idx]]) {
tree[segs[idx]] = {};
} else if (idx === len - 1) {
tree[segs[idx]] = null;
}
tree = tree[segs[idx]];
}
}
function createTree(tree, obj, label) {
obj.label = label;
for (var key in tree) {
obj.nodes = obj.nodes || [];
obj.nodes.push(tree[key] ? createTree(tree[key], {}, key) : key);
}
return obj;
}
function compare(app, views, fn) {
if (typeof views.default === 'undefined') {
throw new Error('expected a "default" task to be defined');
}
var lines = utils.toFilenames(views.default.content);
var str = '';
for (var key in views) {
if (views.hasOwnProperty(key)) {
var view = views[key];
view.treeDiff = diff(views.default, lines, view.content);
if (typeof fn === 'function') {
fn(view);
}
str += '\n### ' + view.stem;
str += '\n';
str += '\n';
str += `Files generated by the [${view.stem} task](#${view.stem}):`;
str += '\n';
str += '\n```diff\n';
str += view.treeDiff;
str += '```\n';
}
}
return str;
}
function createSrcTrees(app, views, fn) {
var str = '';
for (var key in views) {
if (views.hasOwnProperty(key)) {
var view = views[key];
if (typeof fn === 'function') {
fn(view);
}
str += '\n### ' + view.stem;
str += '\n';
str += '\n';
str += `Source files and/or libraries used by the [${view.stem} task](#${view.stem}):`;
str += '\n';
str += '\n```diff\n';
str += view.content;
str += '```\n';
}
}
return str;
}
function diff(defaultView, lines, content) {
var orig = content.split('\n');
var arr = utils.toFilenames(content);
var temp = arr.slice();
var len = arr.length;
var idx = -1;
while (++idx < len) {
var filename = arr[idx];
if (!filename.trim()) continue;
if (lines.indexOf(filename) === -1) {
arr[idx] = orig[idx].replace(/^ /, '+');
} else {
arr[idx] = orig[idx];
}
}
var origDefault = defaultView.content.split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (temp.indexOf(line) === -1) {
arr.splice(i, 0, origDefault[i].replace(/^ /, '-'));
}
}
return utils.trim(arr.join('\n'));
}
/**
* Expose `compare`
*/
module.exports.compare = compare;
;