|
|
|
'use strict';
|
|
|
|
|
|
|
|
var winston = require('winston'),
|
|
|
|
fork = require('child_process').fork,
|
|
|
|
path = require('path'),
|
|
|
|
async = require('async'),
|
|
|
|
_ = require('underscore'),
|
|
|
|
os = require('os'),
|
|
|
|
nconf = require('nconf'),
|
|
|
|
fs = require('fs'),
|
|
|
|
|
|
|
|
plugins = require('../plugins'),
|
|
|
|
emitter = require('../emitter'),
|
|
|
|
utils = require('../../public/src/utils');
|
|
|
|
|
|
|
|
module.exports = function(Meta) {
|
|
|
|
|
|
|
|
Meta.js = {
|
|
|
|
cache: '',
|
|
|
|
map: '',
|
|
|
|
hash: +new Date(),
|
|
|
|
scripts: {
|
|
|
|
base: [
|
|
|
|
'public/vendor/jquery/js/jquery.js',
|
|
|
|
'./node_modules/socket.io-client/socket.io.js',
|
|
|
|
'public/vendor/jquery/timeago/jquery.timeago.min.js',
|
|
|
|
'public/vendor/jquery/js/jquery.form.min.js',
|
|
|
|
'public/vendor/visibility/visibility.min.js',
|
|
|
|
'public/vendor/bootstrap/js/bootstrap.min.js',
|
|
|
|
'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js',
|
|
|
|
'public/vendor/jquery/textcomplete/jquery.textcomplete.min.js',
|
|
|
|
'public/vendor/requirejs/require.js',
|
|
|
|
'public/vendor/bootbox/bootbox.min.js',
|
|
|
|
'public/vendor/tinycon/tinycon.js',
|
|
|
|
'public/vendor/xregexp/xregexp.js',
|
|
|
|
'public/vendor/xregexp/unicode/unicode-base.js',
|
|
|
|
'public/vendor/buzz/buzz.min.js',
|
|
|
|
'public/vendor/mousetrap/mousetrap.js',
|
|
|
|
'public/vendor/autosize.js',
|
|
|
|
'./node_modules/templates.js/lib/templates.js',
|
|
|
|
'public/src/utils.js',
|
|
|
|
'public/src/app.js',
|
|
|
|
'public/src/ajaxify.js',
|
|
|
|
'public/src/overrides.js',
|
|
|
|
'public/src/variables.js',
|
|
|
|
'public/src/widgets.js'
|
|
|
|
],
|
|
|
|
rjs: [
|
|
|
|
'public/src/client/footer.js',
|
|
|
|
'public/src/client/chats.js',
|
|
|
|
'public/src/client/infinitescroll.js'
|
|
|
|
]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Meta.js.loadRJS = function(callback) {
|
|
|
|
if (global.env === 'development') {
|
|
|
|
return callback();
|
|
|
|
}
|
|
|
|
|
|
|
|
var rjsPath = path.join(__dirname, '../../public/src');
|
|
|
|
|
|
|
|
utils.walk(path.join(rjsPath, 'modules'), function(err, rjsFiles) {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
rjsFiles = rjsFiles.map(function(file) {
|
|
|
|
return path.join('public/src', file.replace(rjsPath, ''));
|
|
|
|
});
|
|
|
|
|
|
|
|
Meta.js.scripts.rjs = Meta.js.scripts.rjs.concat(rjsFiles);
|
|
|
|
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Meta.js.prepare = function (callback) {
|
|
|
|
async.parallel([
|
|
|
|
async.apply(Meta.js.loadRJS), // Require.js scripts
|
|
|
|
async.apply(getPluginScripts), // plugin scripts via filter:scripts.get
|
|
|
|
function(next) { // client scripts via "scripts" config in plugin.json
|
|
|
|
var pluginsScripts = [],
|
|
|
|
pluginDirectories = [],
|
|
|
|
clientScripts = [];
|
|
|
|
|
|
|
|
pluginsScripts = plugins.clientScripts.filter(function(path) {
|
|
|
|
if (path.endsWith('.js')) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
pluginDirectories.push(path);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Add plugin scripts
|
|
|
|
Meta.js.scripts.client = pluginsScripts;
|
|
|
|
|
|
|
|
// Add plugin script directories
|
|
|
|
async.each(pluginDirectories, function(directory, next) {
|
|
|
|
utils.walk(directory, function(err, scripts) {
|
|
|
|
Meta.js.scripts.client = Meta.js.scripts.client.concat(scripts);
|
|
|
|
next(err);
|
|
|
|
});
|
|
|
|
}, next);
|
|
|
|
}
|
|
|
|
], function(err) {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert all scripts to paths relative to the NodeBB base directory
|
|
|
|
var basePath = path.resolve(__dirname, '../..');
|
|
|
|
Meta.js.scripts.all = Meta.js.scripts.base.concat(Meta.js.scripts.rjs, Meta.js.scripts.plugin, Meta.js.scripts.client).map(function(script) {
|
|
|
|
return path.relative(basePath, script).replace(/\\/g, '/');
|
|
|
|
});
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Meta.js.minify = function(minify, callback) {
|
|
|
|
if (nconf.get('isPrimary') === 'true') {
|
|
|
|
var minifier = Meta.js.minifierProc = fork('minifier.js'),
|
|
|
|
onComplete = function(err) {
|
|
|
|
if (err) {
|
|
|
|
winston.error('[meta/js] Minification failed: ' + err.message);
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
winston.verbose('[meta/js] Minification complete');
|
|
|
|
minifier.kill();
|
|
|
|
|
|
|
|
if (process.send) {
|
|
|
|
process.send({
|
|
|
|
action: 'js-propagate',
|
|
|
|
cache: Meta.js.cache,
|
|
|
|
map: Meta.js.map,
|
|
|
|
hash: Meta.js.hash
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Meta.js.commitToFile();
|
|
|
|
|
|
|
|
if (typeof callback === 'function') {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
minifier.on('message', function(message) {
|
|
|
|
switch(message.type) {
|
|
|
|
case 'end':
|
|
|
|
Meta.js.cache = message.minified;
|
|
|
|
onComplete();
|
|
|
|
break;
|
|
|
|
case 'hash':
|
|
|
|
Meta.js.hash = message.payload;
|
|
|
|
break;
|
|
|
|
case 'error':
|
|
|
|
winston.error('[meta/js] Could not compile client-side scripts! ' + message.payload.message);
|
|
|
|
minifier.kill();
|
|
|
|
if (typeof callback === 'function') {
|
|
|
|
callback(new Error(message.payload.message));
|
|
|
|
} else {
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Meta.js.prepare(function() {
|
|
|
|
minifier.send({
|
|
|
|
action: 'js',
|
|
|
|
minify: global.env !== 'development',
|
|
|
|
scripts: Meta.js.scripts.all
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
if (typeof callback === 'function') {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Meta.js.killMinifier = function(callback) {
|
|
|
|
if (Meta.js.minifierProc) {
|
|
|
|
Meta.js.minifierProc.kill('SIGTERM');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Meta.js.commitToFile = function() {
|
|
|
|
async.parallel([
|
|
|
|
async.apply(fs.writeFile, path.join(__dirname, '../../public/nodebb.min.js'), Meta.js.cache)
|
|
|
|
], function (err) {
|
|
|
|
if (!err) {
|
|
|
|
winston.verbose('[meta/js] Client-side minfile committed to disk.');
|
|
|
|
emitter.emit('meta:js.compiled');
|
|
|
|
} else {
|
|
|
|
winston.error('[meta/js] ' + err.message);
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Meta.js.getFromFile = function(minify, callback) {
|
|
|
|
var scriptPath = path.join(__dirname, '../../public/nodebb.min.js'),
|
|
|
|
mapPath = path.join(__dirname, '../../public/nodebb.min.js.map'),
|
|
|
|
paths = [scriptPath];
|
|
|
|
fs.exists(scriptPath, function(exists) {
|
|
|
|
if (exists) {
|
|
|
|
if (nconf.get('isPrimary') === 'true') {
|
|
|
|
fs.exists(mapPath, function(exists) {
|
|
|
|
if (exists) {
|
|
|
|
paths.push(mapPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
winston.verbose('[meta/js] Reading client-side scripts from file');
|
|
|
|
async.map(paths, fs.readFile, function(err, files) {
|
|
|
|
Meta.js.cache = files[0];
|
|
|
|
Meta.js.map = files[1] || '';
|
|
|
|
|
|
|
|
emitter.emit('meta:js.compiled');
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
winston.warn('[meta/js] No script file found on disk, re-minifying');
|
|
|
|
Meta.js.minify.apply(Meta.js, arguments);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
function getPluginScripts(callback) {
|
|
|
|
plugins.fireHook('filter:scripts.get', [], function(err, scripts) {
|
|
|
|
if (err) {
|
|
|
|
callback(err, []);
|
|
|
|
}
|
|
|
|
|
|
|
|
var jsPaths = scripts.map(function (jsPath) {
|
|
|
|
jsPath = path.normalize(jsPath);
|
|
|
|
|
|
|
|
var matches = _.map(plugins.staticDirs, function(realPath, mappedPath) {
|
|
|
|
if (jsPath.match(mappedPath)) {
|
|
|
|
return mappedPath;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}).filter(function(a) { return a; });
|
|
|
|
|
|
|
|
if (matches.length) {
|
|
|
|
var relPath = jsPath.slice(('plugins/' + matches[0]).length),
|
|
|
|
pluginId = matches[0].split(path.sep)[0];
|
|
|
|
|
|
|
|
return plugins.staticDirs[matches[0]] + relPath;
|
|
|
|
} else {
|
|
|
|
winston.warn('[meta.scripts.get] Could not resolve mapped path: ' + jsPath + '. Are you sure it is defined by a plugin?');
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Meta.js.scripts.plugin = jsPaths.filter(Boolean);
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|