Source: apc-static/task/worker/generate_theme_less_files.js

/**
 * Generate themes less.
 * @function task.worker.generateThemeLessFiles
 * @param {object} config - Work configuration.
 * @param {string} config.dataFile - Themes data file.
 * @param {string} config.destDir - Destination directory.
 * @param {function} callback - Callback when done.
 * @author Taka Okunishi
 *
 */
var path = require('path'),
    file = require('../../lib/file'),
    util = require('util'),
    format = util.format,
    object = require('../../lib/object'),
    deepCopy = object.deepCopy,
    loadHbsTmpl = file.loadHbsTmpl,
    mkdirP = file.mkdirP,
    writeReadonlyFile = file.writeReadonlyFile,
    relativeSymlink = file.relativeSymlink,
    debug = require('./_debug'),
    EOL = require('os').EOL,
    _ensureThemeDataGuideIndex = require('./_ensure_theme_data_guide_index'),
    _themeNameForIndex = require('./_theme_name_for_index'),
    _flattenThemesData = require('./_flatten_themes_data');

exports = module.exports = function (config, callback) {
    var dataFile = path.resolve(config.dataFile),
        destDir = path.resolve(config.destDir),
        data = require(dataFile);

    data = _ensureThemeDataGuideIndex(data);
    require('async').series(
        Object.keys(data).map(function (key, i) {
            return function (callback) {
                var themeData = data[key];
                exports._writeThemeLessFiles(key, themeData, destDir, function (err) {
                    callback(err);
                });
            }
        }),
        function (err) {
            callback(err);
        });
};

exports._writeThemeLessFiles = function (key, data, destDir, callback) {
    var tmpl = path.resolve(data.tmpl),
        guideIndex = data.guideIndex;
    loadHbsTmpl(tmpl, function (err, tmpl) {
        if (err) {
            callback(err);
            return;
        }
        require('async').series(
            _flattenThemesData(data.choices || {}).map(function (data, i) {
                var themeName = _themeNameForIndex(i);
                data._themeIndex = i + 1;
                data._themeName = themeName;
                data._guideIndex = guideIndex;
                data._description = Object.keys(data)
                    .filter(function (key) {
                        return !key.match(/_themeIndex|_guideIndex/);
                    })
                    .sort()
                    .map(function (key) {
                        return [key, data[key]].join('=')
                    })
                    .join(', ');
                return function (callback) {
                    var dest = path.resolve(destDir, key, exports._nameLess(key, themeName));
                    mkdirP(path.dirname(dest), function (err) {
                        if (err) {
                            callback(err);
                            return;
                        }
                        var content = tmpl(data) + EOL;
                        writeReadonlyFile(dest, content, function (err) {
                            if (!err) {
                                debug.didCreateFile(dest);
                            }
                            callback(err);
                        });
                    });
                }
            }),
            function (err) {
                callback(err);
            });
    });
};

exports._nameLess = function (key, themeName) {
    return format('%s-theme-%s-mixin.less', key, themeName);
};