diff --git a/src/emailer.js b/src/emailer.js
index 10163cc512..a841eb1f91 100644
--- a/src/emailer.js
+++ b/src/emailer.js
@@ -10,6 +10,7 @@ var htmlToText = require('html-to-text');
 var url = require('url');
 var path = require('path');
 var fs = require('fs');
+var _ = require('lodash');
 
 var User = require('./user');
 var Plugins = require('./plugins');
@@ -289,11 +290,10 @@ function buildCustomTemplates(config) {
 						file.walk(viewsDir, next);
 					},
 					function (paths, next) {
-						paths = paths.reduce(function (obj, p) {
-							var relative = path.relative(viewsDir, p);
-							obj['/' + relative] = p;
-							return obj;
-						}, {});
+						paths = _.fromPairs(paths.map(function (p) {
+							var relative = path.relative(viewsDir, p).replace(/\\/g, '/');
+							return [relative, p];
+						}));
 						meta.templates.processImports(paths, template.path, template.text, next);
 					},
 					function (source, next) {
diff --git a/src/meta/templates.js b/src/meta/templates.js
index 943978ff56..f8b63d41fa 100644
--- a/src/meta/templates.js
+++ b/src/meta/templates.js
@@ -7,6 +7,7 @@ var async = require('async');
 var path = require('path');
 var fs = require('fs');
 var nconf = require('nconf');
+var _ = require('lodash');
 
 var plugins = require('../plugins');
 var file = require('../file');
@@ -24,7 +25,7 @@ function processImports(paths, templatePath, source, callback) {
 		return callback(null, source);
 	}
 
-	var partial = '/' + matches[1];
+	var partial = matches[1];
 	if (paths[partial] && templatePath !== partial) {
 		fs.readFile(paths[partial], 'utf8', function (err, partialSource) {
 			if (err) {
@@ -43,124 +44,108 @@ function processImports(paths, templatePath, source, callback) {
 }
 Templates.processImports = processImports;
 
-Templates.compile = function (callback) {
-	callback = callback || function () {};
+function getTemplateDirs(callback) {
+	var pluginTemplates = _.values(plugins.pluginsData)
+		.filter(function (pluginData) {
+			return !pluginData.id.startsWith('nodebb-theme-');
+		})
+		.map(function (pluginData) {
+			return path.join(__dirname, '../../node_modules/', pluginData.id, pluginData.templates || 'templates');
+		});
 
 	var themeConfig = require(nconf.get('theme_config'));
-	var baseTemplatesPaths = themeConfig.baseTheme ? getBaseTemplates(themeConfig.baseTheme) : [nconf.get('base_templates_path')];
+	var theme = themeConfig.baseTheme;
+
+	var themePath;
+	var themeTemplates = [nconf.get('theme_templates_path')];
+	while (theme) {
+		themePath = path.join(nconf.get('themes_path'), theme);
+		themeConfig = require(path.join(themePath, 'theme.json'));
+
+		themeTemplates.push(path.join(themePath, themeConfig.templates || 'templates'));
+		theme = themeConfig.baseTheme;
+	}
+
+	themeTemplates.push(nconf.get('base_templates_path'));
+	themeTemplates = _.uniq(themeTemplates.reverse());
+
+	var coreTemplatesPath = nconf.get('core_templates_path');
+
+	var templateDirs = _.uniq([coreTemplatesPath].concat(themeTemplates, pluginTemplates));
+
+	async.filter(templateDirs, file.exists, callback);
+}
+
+function getTemplateFiles(dirs, callback) {
+	async.waterfall([
+		function (cb) {
+			async.map(dirs, function (dir, next) {
+				file.walk(dir, function (err, files) {
+					if (err) { return next(err); }
+
+					files = files.filter(function (path) {
+						return path.endsWith('.tpl');
+					}).map(function (file) {
+						return {
+							name: path.relative(dir, file).replace(/\\/g, '/'),
+							path: file,
+						};
+					});
+					next(null, files);
+				});
+			}, cb);
+		},
+		function (buckets, cb) {
+			var dict = {};
+			buckets.forEach(function (files) {
+				files.forEach(function (file) {
+					dict[file.name] = file.path;
+				});
+			});
+
+			cb(null, dict);
+		},
+	], callback);
+}
+
+function compile(callback) {
+	callback = callback || function () {};
 
 	async.waterfall([
 		function (next) {
-			preparePaths(baseTemplatesPaths, next);
+			rimraf(viewsPath, function (err) { next(err); });
+		},
+		function (next) {
+			mkdirp(viewsPath, function (err) { next(err); });
 		},
-		function (paths, next) {
-			async.each(Object.keys(paths), function (relativePath, next) {
+		getTemplateDirs,
+		getTemplateFiles,
+		function (files, next) {
+			async.each(Object.keys(files), function (name, next) {
+				var filePath = files[name];
+
 				async.waterfall([
 					function (next) {
-						fs.readFile(paths[relativePath], 'utf8', next);
+						fs.readFile(filePath, 'utf8', next);
 					},
 					function (source, next) {
-						processImports(paths, relativePath, source, next);
+						processImports(files, name, source, next);
 					},
 					function (source, next) {
-						mkdirp(path.join(viewsPath, path.dirname(relativePath)), function (err) {
+						mkdirp(path.join(viewsPath, path.dirname(name)), function (err) {
 							next(err, source);
 						});
 					},
 					function (compiled, next) {
-						fs.writeFile(path.join(viewsPath, relativePath), compiled, next);
+						fs.writeFile(path.join(viewsPath, name), compiled, next);
 					},
 				], next);
 			}, next);
 		},
-		function (next) {
-			rimraf(path.join(viewsPath, '*.js'), next);
-		},
 		function (next) {
 			winston.verbose('[meta/templates] Successfully compiled templates.');
 			next();
 		},
 	], callback);
-};
-
-function getBaseTemplates(theme) {
-	var baseTemplatesPaths = [];
-	var baseThemePath;
-	var baseThemeConfig;
-
-	while (theme) {
-		baseThemePath = path.join(nconf.get('themes_path'), theme);
-		baseThemeConfig = require(path.join(baseThemePath, 'theme.json'));
-
-		baseTemplatesPaths.push(path.join(baseThemePath, baseThemeConfig.templates || 'templates'));
-		theme = baseThemeConfig.baseTheme;
-	}
-
-	return baseTemplatesPaths.reverse();
-}
-
-function preparePaths(baseTemplatesPaths, callback) {
-	var coreTemplatesPath = nconf.get('core_templates_path');
-	var pluginTemplates;
-	async.waterfall([
-		function (next) {
-			rimraf(viewsPath, next);
-		},
-		function (next) {
-			mkdirp(viewsPath, next);
-		},
-		function (viewsPath, next) {
-			plugins.fireHook('static:templates.precompile', {}, next);
-		},
-		function (next) {
-			plugins.getTemplates(next);
-		},
-		function (_pluginTemplates, next) {
-			pluginTemplates = _pluginTemplates;
-			winston.verbose('[meta/templates] Compiling templates');
-
-			async.parallel({
-				coreTpls: function (next) {
-					file.walk(coreTemplatesPath, next);
-				},
-				baseThemes: function (next) {
-					async.map(baseTemplatesPaths, function (baseTemplatePath, next) {
-						file.walk(baseTemplatePath, function (err, paths) {
-							paths = paths.map(function (tpl) {
-								return {
-									base: baseTemplatePath,
-									path: tpl.replace(baseTemplatePath, ''),
-								};
-							});
-
-							next(err, paths);
-						});
-					}, next);
-				},
-			}, next);
-		},
-		function (data, next) {
-			var baseThemes = data.baseThemes;
-			var coreTpls = data.coreTpls;
-			var paths = {};
-
-			coreTpls.forEach(function (el, i) {
-				paths[coreTpls[i].replace(coreTemplatesPath, '')] = coreTpls[i];
-			});
-
-			baseThemes.forEach(function (baseTpls) {
-				baseTpls.forEach(function (el, i) {
-					paths[baseTpls[i].path] = path.join(baseTpls[i].base, baseTpls[i].path);
-				});
-			});
-
-			for (var tpl in pluginTemplates) {
-				if (pluginTemplates.hasOwnProperty(tpl)) {
-					paths[tpl] = pluginTemplates[tpl];
-				}
-			}
-
-			next(null, paths);
-		},
-	], callback);
 }
+Templates.compile = compile;
diff --git a/src/plugins.js b/src/plugins.js
index f11ed63494..a1193125e8 100644
--- a/src/plugins.js
+++ b/src/plugins.js
@@ -138,10 +138,13 @@ Plugins.reloadRoutes = function (callback) {
 	});
 };
 
+// DEPRECATED: remove in v1.8.0
 Plugins.getTemplates = function (callback) {
 	var templates = {};
 	var tplName;
 
+	winston.warn('[deprecated] Plugins.getTemplates is DEPRECATED to be removed in v1.8.0');
+
 	Plugins.data.getActive(function (err, plugins) {
 		if (err) {
 			return callback(err);