You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/src/meta/build.js

235 lines
5.9 KiB
JavaScript

'use strict';
const os = require('os');
const async = require('async');
const winston = require('winston');
const nconf = require('nconf');
const _ = require('lodash');
const cacheBuster = require('./cacheBuster');
let meta;
function step(target, callback) {
var startTime = Date.now();
winston.info('[build] ' + target + ' build started');
return function (err) {
if (err) {
winston.error('[build] ' + target + ' build failed');
return callback(err);
}
var time = (Date.now() - startTime) / 1000;
winston.info('[build] ' + target + ' build completed in ' + time + 'sec');
callback();
};
}
var targetHandlers = {
'plugin static dirs': function (parallel, callback) {
meta.js.linkStatics(callback);
},
'requirejs modules': function (parallel, callback) {
meta.js.buildModules(parallel, callback);
},
'client js bundle': function (parallel, callback) {
meta.js.buildBundle('client', parallel, callback);
},
'admin js bundle': function (parallel, callback) {
meta.js.buildBundle('admin', parallel, callback);
},
javascript: [
'plugin static dirs',
'requirejs modules',
'client js bundle',
'admin js bundle',
],
'client side styles': function (parallel, callback) {
meta.css.buildBundle('client', parallel, callback);
},
'admin control panel styles': function (parallel, callback) {
meta.css.buildBundle('admin', parallel, callback);
},
styles: [
'client side styles',
'admin control panel styles',
],
templates: function (parallel, callback) {
meta.templates.compile(callback);
},
languages: function (parallel, callback) {
meta.languages.build(callback);
},
sounds: function (parallel, callback) {
meta.sounds.build(callback);
},
};
var aliases = {
'plugin static dirs': ['staticdirs'],
'requirejs modules': ['rjs', 'modules'],
'client js bundle': ['clientjs', 'clientscript', 'clientscripts'],
'admin js bundle': ['adminjs', 'adminscript', 'adminscripts'],
javascript: ['js'],
'client side styles': [
'clientcss', 'clientless', 'clientstyles', 'clientstyle',
],
'admin control panel styles': [
'admincss', 'adminless', 'adminstyles', 'adminstyle', 'acpcss', 'acpless', 'acpstyles', 'acpstyle',
],
styles: ['css', 'less', 'style'],
templates: ['tpl'],
languages: ['lang', 'i18n'],
sounds: ['sound'],
};
exports.aliases = aliases;
aliases = Object.keys(aliases).reduce(function (prev, key) {
var arr = aliases[key];
arr.forEach(function (alias) {
prev[alias] = key;
});
prev[key] = key;
return prev;
}, {});
function beforeBuild(targets, callback) {
var db = require('../database');
require('colors');
process.stdout.write(' started'.green + '\n'.reset);
async.series([
function (next) {
db.init(next);
},
function (next) {
meta = require('../meta');
meta.themes.setupPaths(next);
},
function (next) {
var plugins = require('../plugins');
plugins.prepareForBuild(targets, next);
},
], function (err) {
if (err) {
winston.error('[build] Encountered error preparing for build', err);
return callback(err);
}
callback();
});
}
var allTargets = Object.keys(targetHandlers).filter(function (name) {
return typeof targetHandlers[name] === 'function';
});
function buildTargets(targets, parallel, callback) {
var all = parallel ? async.each : async.eachSeries;
var length = Math.max.apply(Math, targets.map(function (name) {
return name.length;
}));
all(targets, function (target, next) {
targetHandlers[target](parallel, step(_.padStart(target, length) + ' ', next));
}, callback);
}
exports.build = function (targets, options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options = {};
} else if (!options) {
options = {};
}
if (targets === true) {
targets = allTargets;
} else if (!Array.isArray(targets)) {
targets = targets.split(',');
}
let series = nconf.get('series') || options.series;
if (series === undefined) {
// Detect # of CPUs and select strategy as appropriate
winston.verbose('[build] Querying CPU core count for build strategy');
const cpus = os.cpus();
series = cpus.length < 4;
winston.verbose('[build] System returned ' + cpus.length + ' cores, opting for ' + (series ? 'series' : 'parallel') + ' build strategy');
}
targets = targets
// get full target name
.map(function (target) {
target = target.toLowerCase().replace(/-/g, '');
if (!aliases[target]) {
winston.warn('[build] Unknown target: ' + target);
if (target.includes(',')) {
winston.warn('[build] Are you specifying multiple targets? Separate them with spaces:');
winston.warn('[build] e.g. `./nodebb build adminjs tpl`');
}
return false;
}
return aliases[target];
})
// filter nonexistent targets
.filter(Boolean);
// map multitargets to their sets
targets = _.uniq(_.flatMap(targets, target => (
Array.isArray(targetHandlers[target]) ?
targetHandlers[target] :
target
)));
winston.verbose('[build] building the following targets: ' + targets.join(', '));
if (!targets) {
winston.info('[build] No valid targets supplied. Aborting.');
callback();
}
var startTime;
var totalTime;
async.series([
async.apply(beforeBuild, targets),
function (next) {
var threads = parseInt(nconf.get('threads'), 10);
if (threads) {
require('./minifier').maxThreads = threads - 1;
}
if (!series) {
winston.info('[build] Building in parallel mode');
} else {
winston.info('[build] Building in series mode');
}
startTime = Date.now();
buildTargets(targets, !series, next);
},
function (next) {
totalTime = (Date.now() - startTime) / 1000;
cacheBuster.write(next);
},
], function (err) {
if (err) {
winston.error('[build] Encountered error during build step', err);
return callback(err);
}
winston.info('[build] Asset compilation successful. Completed in ' + totalTime + 'sec.');
callback();
});
};
exports.buildAll = function (callback) {
exports.build(allTargets, callback);
};
require('../promisify')(exports);