mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-30 02:25:55 +01:00
Precompile all templates
- Benchpress compilation is 33x faster now - Native module with JS fallback and pre-built binaries - Dev template build is <1sec now - Minified template build is ~5sec (uglify accounts for almost all)
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
"async": "2.6.1",
|
||||
"autoprefixer": "^8.5.2",
|
||||
"bcryptjs": "2.4.3",
|
||||
"benchpressjs": "^1.2.2",
|
||||
"benchpressjs": "^1.2.3",
|
||||
"body-parser": "^1.18.2",
|
||||
"bootstrap": "^3.3.7",
|
||||
"chart.js": "^2.7.1",
|
||||
|
||||
@@ -289,22 +289,26 @@ Emailer.sendViaFallback = function (data, callback) {
|
||||
function buildCustomTemplates(config) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Emailer.getTemplates(config, next);
|
||||
async.parallel({
|
||||
templates: function (cb) {
|
||||
Emailer.getTemplates(config, cb);
|
||||
},
|
||||
function (templates, next) {
|
||||
templates = templates.filter(function (template) {
|
||||
paths: function (cb) {
|
||||
file.walk(viewsDir, cb);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (result, next) {
|
||||
var templates = result.templates.filter(function (template) {
|
||||
return template.isCustom && template.text !== prevConfig['email:custom:' + path];
|
||||
});
|
||||
async.each(templates, function (template, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
file.walk(viewsDir, next);
|
||||
},
|
||||
function (paths, next) {
|
||||
paths = _.fromPairs(paths.map(function (p) {
|
||||
var paths = _.fromPairs(result.paths.map(function (p) {
|
||||
var relative = path.relative(viewsDir, p).replace(/\\/g, '/');
|
||||
return [relative, p];
|
||||
}));
|
||||
async.each(templates, function (template, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
meta.templates.processImports(paths, template.path, template.text, next);
|
||||
},
|
||||
function (source, next) {
|
||||
|
||||
@@ -8,6 +8,7 @@ var path = require('path');
|
||||
var fs = require('fs');
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var Benchpress = require('benchpressjs');
|
||||
|
||||
var plugins = require('../plugins');
|
||||
var file = require('../file');
|
||||
@@ -113,6 +114,34 @@ function getTemplateFiles(dirs, callback) {
|
||||
], callback);
|
||||
}
|
||||
|
||||
function compileTemplate(filename, source, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
file.walk(viewsPath, next);
|
||||
},
|
||||
function (paths, next) {
|
||||
paths = _.fromPairs(paths.map(function (p) {
|
||||
var relative = path.relative(viewsPath, p).replace(/\\/g, '/');
|
||||
return [relative, p];
|
||||
}));
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
processImports(paths, filename, source, next);
|
||||
},
|
||||
function (source, next) {
|
||||
Benchpress.precompile(source, {
|
||||
minify: global.env !== 'development',
|
||||
}, next);
|
||||
},
|
||||
function (compiled, next) {
|
||||
fs.writeFile(path.join(viewsPath, filename.replace(/\.tpl$/, '.js')), compiled, next);
|
||||
},
|
||||
], next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
Templates.compileTemplate = compileTemplate;
|
||||
|
||||
function compile(callback) {
|
||||
callback = callback || function () {};
|
||||
|
||||
@@ -144,8 +173,22 @@ function compile(callback) {
|
||||
next(err, source);
|
||||
});
|
||||
},
|
||||
function (compiled, next) {
|
||||
fs.writeFile(path.join(viewsPath, name), compiled, next);
|
||||
function (imported, next) {
|
||||
async.parallel([
|
||||
function (cb) {
|
||||
fs.writeFile(path.join(viewsPath, name), imported, cb);
|
||||
},
|
||||
function (cb) {
|
||||
Benchpress.precompile(imported, { minify: global.env !== 'development' }, function (err, compiled) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFile(path.join(viewsPath, name.replace(/\.tpl$/, '.js')), compiled, cb);
|
||||
});
|
||||
},
|
||||
], next);
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
var async = require('async');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var csrf = require('csurf');
|
||||
var validator = require('validator');
|
||||
var nconf = require('nconf');
|
||||
var ensureLoggedIn = require('connect-ensure-login');
|
||||
var toobusy = require('toobusy-js');
|
||||
var Benchpress = require('benchpressjs');
|
||||
var LRU = require('lru-cache');
|
||||
|
||||
var plugins = require('../plugins');
|
||||
@@ -207,58 +205,3 @@ middleware.delayLoading = function (req, res, next) {
|
||||
|
||||
setTimeout(next, 1000);
|
||||
};
|
||||
|
||||
var viewsDir = nconf.get('views_dir');
|
||||
var workingCache = {};
|
||||
|
||||
middleware.templatesOnDemand = function (req, res, next) {
|
||||
var filePath = req.filePath || path.join(viewsDir, req.path);
|
||||
if (!filePath.endsWith('.js')) {
|
||||
return next();
|
||||
}
|
||||
var tplPath = filePath.replace(/\.js$/, '.tpl');
|
||||
if (workingCache[filePath]) {
|
||||
workingCache[filePath].push(next);
|
||||
return;
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (cb) {
|
||||
file.exists(filePath, cb);
|
||||
},
|
||||
function (exists, cb) {
|
||||
if (exists) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// need to check here again
|
||||
// because compilation could have started since last check
|
||||
if (workingCache[filePath]) {
|
||||
workingCache[filePath].push(next);
|
||||
return;
|
||||
}
|
||||
|
||||
workingCache[filePath] = [next];
|
||||
fs.readFile(tplPath, 'utf8', cb);
|
||||
},
|
||||
function (source, cb) {
|
||||
Benchpress.precompile({
|
||||
source: source,
|
||||
minify: global.env !== 'development',
|
||||
}, cb);
|
||||
},
|
||||
function (compiled, cb) {
|
||||
if (!compiled) {
|
||||
return cb(new Error('[[error:templatesOnDemand.compiled-template-empty, ' + tplPath + ']]'));
|
||||
}
|
||||
fs.writeFile(filePath, compiled, cb);
|
||||
},
|
||||
], function (err) {
|
||||
var arr = workingCache[filePath];
|
||||
workingCache[filePath] = null;
|
||||
|
||||
arr.forEach(function (callback) {
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -152,7 +152,6 @@ module.exports = function (app, middleware, hotswapIds, callback) {
|
||||
}
|
||||
|
||||
app.use(middleware.privateUploads);
|
||||
app.use(relativePath + '/assets/templates', middleware.templatesOnDemand);
|
||||
|
||||
var statics = [
|
||||
{ route: '/assets', path: path.join(__dirname, '../../build/public') },
|
||||
|
||||
@@ -147,16 +147,8 @@ function setupExpressApp(app, callback) {
|
||||
app.engine('tpl', function (filepath, data, next) {
|
||||
filepath = filepath.replace(/\.tpl$/, '.js');
|
||||
|
||||
middleware.templatesOnDemand({
|
||||
filePath: filepath,
|
||||
}, null, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
Benchpress.__express(filepath, data, next);
|
||||
});
|
||||
});
|
||||
app.set('view engine', 'tpl');
|
||||
app.set('views', viewsDir);
|
||||
app.set('json spaces', global.env === 'development' ? 4 : 0);
|
||||
|
||||
@@ -72,15 +72,17 @@ describe('Controllers', function () {
|
||||
});
|
||||
}
|
||||
var message = utils.generateUUID();
|
||||
var tplPath = path.join(nconf.get('views_dir'), 'custom.tpl');
|
||||
var name = 'custom.tpl';
|
||||
var tplPath = path.join(nconf.get('views_dir'), name);
|
||||
|
||||
before(function () {
|
||||
before(function (done) {
|
||||
plugins.registerHook('myTestPlugin', {
|
||||
hook: 'action:homepage.get:custom',
|
||||
method: hookMethod,
|
||||
});
|
||||
|
||||
fs.writeFileSync(tplPath, message);
|
||||
meta.templates.compileTemplate(name, message, done);
|
||||
});
|
||||
|
||||
it('should load default', function (done) {
|
||||
|
||||
Reference in New Issue
Block a user