@ -2,149 +2,212 @@
var async = require ( 'async' ) ;
var winston = require ( 'winston' ) ;
var os = require ( 'os' ) ;
var nconf = require ( 'nconf' ) ;
var padstart = require ( 'lodash.padstart' ) ;
var buildStart ;
var cacheBuster = require ( './cacheBuster' ) ;
var meta ;
var valid = [ 'js' , 'clientCSS' , 'acpCSS' , 'tpl' , 'lang' , 'sound' ] ;
function step ( target , callback ) {
var startTime = Date . now ( ) ;
winston . info ( '[build] ' + target + ' build started' ) ;
exports . buildAll = function ( callback ) {
exports . build ( valid . join ( ',' ) , callback ) ;
} ;
return function ( err ) {
if ( err ) {
winston . error ( '[build] ' + target + ' build failed' ) ;
return callback ( err ) ;
}
exports . build = function build ( targets , callback ) {
buildStart = Date . now ( ) ;
var time = ( Date . now ( ) - startTime ) / 1000 ;
var db = require ( '../database' ) ;
var meta = require ( '../meta' ) ;
var plugins = require ( '../plugins' ) ;
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 ) ;
} ,
} ;
targets = ( targets === true ? valid : targets . split ( ',' ) . filter ( function ( target ) {
return valid . indexOf ( target ) !== - 1 ;
} ) ) ;
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' ] ,
} ;
if ( ! targets ) {
winston . error ( '[build] No valid build targets found. Aborting.' ) ;
return process . exit ( 0 ) ;
}
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 ( callback ) {
var db = require ( '../database' ) ;
var plugins = require ( '../plugins' ) ;
meta = require ( '../meta' ) ;
async . series ( [
async . apply ( db . init ) ,
async . apply ( meta . themes . setupPaths ) ,
async . apply ( plugins . prepareForBuild ) ,
db . init ,
meta . themes . setupPaths ,
plugins . prepareForBuild ,
] , function ( err ) {
if ( err ) {
winston . error ( '[build] Encountered error preparing for build: ' + err . message ) ;
return process . exit ( 1 ) ;
return callback( err ) ;
}
exports . buildTargets ( targets , callback ) ;
callback( ) ;
} ) ;
} ;
}
exports . buildTargets = function ( targets , callback ) {
var cacheBuster = require ( './cacheBuster' ) ;
var meta = require ( '../meta' ) ;
var numCpus = require ( 'os' ) . cpus ( ) . length ;
var parallel = targets . length > 1 && numCpus > 1 ;
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 ) ;
}
function build ( targets , callback ) {
if ( targets === true ) {
targets = allTargets ;
} else if ( ! Array . isArray ( targets ) ) {
targets = targets . split ( ',' ) ;
}
targets = targets
// get full target name
. map ( function ( target ) {
target = target . toLowerCase ( ) . replace ( /-/g , '' ) ;
if ( ! aliases [ target ] ) {
winston . warn ( '[build] Unknown target: ' + target ) ;
return false ;
}
return aliases [ target ] ;
} )
// filter nonexistent targets
. filter ( Boolean )
// map multitargets to their sets
. reduce ( function ( prev , target ) {
if ( Array . isArray ( targetHandlers [ target ] ) ) {
return prev . concat ( targetHandlers [ target ] ) ;
}
buildStart = buildStart || Date . now ( ) ;
return prev . concat ( target ) ;
} , [ ] )
// unique
. filter ( function ( target , i , arr ) {
return arr . indexOf ( target ) === i ;
} ) ;
var step = function ( startTime , target , next , err ) {
if ( typeof callback !== 'function' ) {
callback = function ( err ) {
if ( err ) {
winston . error ( 'Build failed: ' + err . stack ) ;
winston . error ( err ) ;
process . exit ( 1 ) ;
} else {
process . exit ( 0 ) ;
}
winston . info ( '[build] ' + target + ' => Completed in ' + ( ( Date . now ( ) - startTime ) / 1000 ) + 's' ) ;
next ( ) ;
} ;
}
if ( parallel ) {
winston . verbose ( '[build] Utilising multiple cores/processes' ) ;
} else {
winston . verbose ( '[build] Utilising single-core' ) ;
if ( ! targets ) {
winston . info ( '[build] No valid targets supplied. Aborting.' ) ;
callback ( ) ;
}
async [ parallel ? 'parallel' : 'series' ] ( [
function ( next ) {
if ( targets . indexOf ( 'js' ) !== - 1 ) {
winston . info ( '[build] Building javascript' ) ;
var startTime = Date . now ( ) ;
var startTime ;
var totalTime ;
async . series ( [
meta . js . buildModules ,
meta . js . linkStatics ,
async . apply ( meta . js . minify , 'nodebb.min.js' ) ,
async . apply ( meta . js . minify , 'acp.min.js' ) ,
] , step . bind ( this , startTime , 'js' , next ) ) ;
beforeBuild ,
function ( next ) {
var parallel = os . cpus ( ) . length > 1 && ! nconf . get ( 'series' ) ;
if ( parallel ) {
winston . info ( '[build] Building in parallel mode' ) ;
} else {
setImmediate ( next ) ;
winston. info ( '[build] Building in series mode' ) ;
}
} ,
function ( next ) {
async . eachSeries ( targets , function ( target , next ) {
var startTime ;
switch ( target ) {
case 'js' :
setImmediate ( next ) ;
break ;
case 'clientCSS' :
winston . info ( '[build] Building client-side CSS' ) ;
startTime = Date . now ( ) ;
meta . css . minify ( 'client' , step . bind ( this , startTime , target , next ) ) ;
break ;
case 'acpCSS' :
winston . info ( '[build] Building admin control panel CSS' ) ;
startTime = Date . now ( ) ;
meta . css . minify ( 'admin' , step . bind ( this , startTime , target , next ) ) ;
break ;
case 'tpl' :
winston . info ( '[build] Building templates' ) ;
startTime = Date . now ( ) ;
meta . templates . compile ( step . bind ( this , startTime , target , next ) ) ;
break ;
case 'lang' :
winston . info ( '[build] Building language files' ) ;
startTime = Date . now ( ) ;
meta . languages . build ( step . bind ( this , startTime , target , next ) ) ;
break ;
case 'sound' :
winston . info ( '[build] Linking sound files' ) ;
startTime = Date . now ( ) ;
meta . sounds . build ( step . bind ( this , startTime , target , next ) ) ;
break ;
default :
winston . warn ( '[build] Unknown build target: \'' + target + '\'' ) ;
setImmediate ( next ) ;
break ;
}
} , next ) ;
buildTargets ( targets , parallel , next ) ;
} ,
function ( next ) {
totalTime = ( Date . now ( ) - startTime ) / 1000 ;
cacheBuster . write ( next ) ;
} ,
] , function ( err ) {
if ( err ) {
winston . error ( '[build] Encountered error during build step: ' + err . message ) ;
return process. exit ( 1 ) ;
return callback ( err ) ;
}
cacheBuster . write ( function ( err ) {
if ( err ) {
winston . error ( '[build] Failed to write `cache-buster.conf`: ' + err . message ) ;
return process . exit ( 1 ) ;
winston . info ( '[build] Asset compilation successful. Completed in ' + totalTime + 'sec.' ) ;
callback ( ) ;
} ) ;
}
var time = ( Date . now ( ) - buildStart ) / 1000 ;
winston . info ( '[build] Asset compilation successful. Completed in ' + time + 's.' ) ;
exports . build = build ;
if ( typeof callback === 'function' ) {
callback ( ) ;
} else {
process . exit ( 0 ) ;
}
} ) ;
} ) ;
exports . buildAll = function ( callback ) {
build ( allTargets , callback ) ;
} ;