/**
* Generate style guide document wit KSS.
* @see {@link http://github.com/hughsk/kss-node | kss}
* @function task.worker.generateStyleguide
* @param {object} config - Work configuration
* @param {string|string[]} config.srcDir - Stylesheet source directory.
* @param {string} config.destDir - Directory to create the document.
* @param {object} config.aliases - Aliases to create inside the dest directory.
* @param {function} callback - Callback when done.
* @author Taka Okunishi
*
*/
var path = require('path'),
fs = require('fs'),
os = require('os'),
EOL = os.EOL,
lib = require('../../lib'),
file = lib.file,
copyDir = file.copyDir,
loadHbsTmpl = file.loadHbsTmpl,
readdirRecursive = file.readdirRecursive,
writeReadonlyFile = file.writeReadonlyFile,
prepareCleanDir = file.prepareCleanDir,
_unlinkFiles = require('./_unlink_files'),
_generateStyleguideOverview = require('./_generate_styleguide_overview'),
fork = require('child_process').fork,
generateStyleGuideLess = require('./generate_styleguide_less'),
_createAliases = require('./_create_aliases');
exports = module.exports = function (config, callback) {
exports._prepareStyleguideWorkDir(function (err, workDir) {
if (err) {
callback(err);
return;
}
var srcDir = [].concat(config.srcDir).map(function (srcDir) {
return path.resolve(srcDir);
}),
destDir = path.resolve(config.destDir);
var kssBin = path.resolve(require.resolve("kss"), '../bin/kss-node'),
lessFiles = [],
aliases = Object.keys(config.aliases || {}).map(function (name) {
return {
from: path.resolve(config.aliases[name]),
to: path.resolve(destDir, name)
};
});
var aliasNames = aliases.map(function (data) {
return data.to;
});
_unlinkFiles(aliasNames, function (err) {
if (err) {
callback(err);
return;
}
exports._prepareStyleguideTmpl(function (err, tmplDir) {
if (err) {
callback(err);
return;
}
exports._concatFilesFlatten([], workDir, function (err) {
if (err) {
callback(err);
return;
}
exports._concatFilesFlattenInDir(srcDir, workDir, function (err) {
if (err) {
callback(err);
return;
}
var styleGuideLessFile = path.resolve(workDir, 'styleguide.less');
fs.readdir(workDir, function (err, workFiles) {
if (err) {
callback(err);
return;
}
generateStyleGuideLess({
dest: styleGuideLessFile,
imports: workFiles
.filter(function (filename) {
return path.extname(filename) === '.less';
})
.map(function (filename) {
return path.resolve(workDir, filename);
})
}, function (err) {
if (err) {
callback(err);
return;
}
lessFiles.push(styleGuideLessFile);
var overviewData = {},
overviewFilename = path.resolve(workDir, 'styleguide.md');
_generateStyleguideOverview(overviewFilename, overviewData, function (err) {
if (err) {
callback(err);
return;
}
var args = [
workDir, destDir,
'-t', path.resolve(tmplDir)
];
lessFiles.forEach(function (filename) {
args.push('--less');
args.push(filename);
});
var kss = fork(kssBin, args);
kss.on('exit', function () {
_createAliases(aliases, function (err) {
callback(err);
});
});
}
);
});
});
});
});
});
});
});
};
/**
* Copy a directory to flatten structure. All files to put direct under the destDir.
* @param {string|string[]} srcDir
* @param {string} destDir
* @param {function} callback
* @protected
* @ignore
*/
exports._concatFilesFlattenInDir = function (srcDir, destDir, callback) {
require('async').series(
[].concat(srcDir).map(function (srcDir) {
return function (callback) {
readdirRecursive(srcDir, function (err, filenames) {
if (err) {
callback(err);
return;
}
filenames = filenames.map(function (filename) {
return path.resolve(srcDir, filename);
});
exports._concatFilesFlatten(filenames, destDir, callback);
});
};
}),
callback
);
};
exports._concatFilesFlatten = function (src, destDir, callback) {
var basename = path.basename(path.dirname(src[0])) + path.extname(src[0]);
exports._newFilename(path.resolve(destDir, basename), function (dest) {
require('./concat_files')({
src: src,
dest: dest,
ignore: '.*'
}, function (err) {
if (err) {
callback(err);
return;
}
fs.exists(dest, function (exists) {
if (!exists) {
callback();
return;
}
fs.readFile(dest, function (err, buffer) {
if (err) {
callback(err);
return;
}
var content = buffer.toString().split(EOL).map(function (line) {
if (!!line.match('@import')) {
var importPath = line.replace('@import', '').replace(/['";\s]/g, '');
return line.replace(importPath,
path.relative(
path.dirname(dest),
path.resolve(path.dirname(src[0]), importPath)
)
);
} else {
return line;
}
}).join(EOL);
writeReadonlyFile(dest, content, function (err) {
callback(err, dest);
});
});
});
});
});
};
/**
* Make sure a filename is whole new.
* @param filename
* @param callback
* @protected
* @ignore
*/
exports._newFilename = function (filename, callback) {
fs.exists(filename, function (exists) {
if (exists) {
var extname = path.extname(filename),
basename = path.basename(filename, extname);
var newFilename = path.join(path.dirname(filename), basename + '_' + extname);
exports._newFilename(newFilename, callback);
} else {
callback(filename);
}
});
};
exports._prepareStyleguideWorkDir = function (callback) {
var workDir = path.resolve(__dirname, '../../work/generate_style_guide_work');
prepareCleanDir(workDir, function (err) {
callback(err, workDir);
});
};
exports._prepareStyleguideTmpl = function (callback) {
var src = path.resolve(__dirname, '../../tmpl/_doc/styleguide'),
dest = path.resolve(__dirname, '../../work/styleguide_tmpl');
copyDir(src, dest, function (err) {
if (err) {
callback(err);
}
var meta = global.apemandata.meta,
data = {
__appname__: meta.name,
__apeman_homepage__: require('apeman').pkg.homepage
};
readdirRecursive(dest, function (err, filenames) {
if (err) {
callback(err);
return;
}
require('async').series(
filenames
.map(function (filename) {
return path.resolve(dest, filename);
})
.map(function (filename) {
return function (callback) {
fs.readFile(filename, function (err, buffer) {
if (err) {
callback(err);
} else {
var content = buffer.toString();
Object.keys(data).forEach(function (key) {
var value = data[key];
content = content.replace(new RegExp(key, 'g'), value);
});
fs.writeFile(filename, content, callback);
}
});
};
}),
function (err) {
callback(err, dest);
}
);
});
});
};