diff --git a/build.js b/build.js index 32359e14f8..a8ad5007d1 100644 --- a/build.js +++ b/build.js @@ -46,7 +46,11 @@ exports.buildTargets = function (targets, callback) { var meta = require('./src/meta'); buildStart = buildStart || Date.now(); - var step = function (startTime, target, next) { + var step = function (startTime, target, next, err) { + if (err) { + winston.error('Build failed: ' + err.message); + process.exit(1); + } winston.info('[build] ' + target + ' => Completed in ' + ((Date.now() - startTime) / 1000) + 's'); next(); }; @@ -75,13 +79,13 @@ exports.buildTargets = function (targets, callback) { case 'clientCSS': winston.info('[build] Building client-side CSS'); startTime = Date.now(); - meta.css.minify('stylesheet.css', step.bind(this, startTime, target, next)); + 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.css', step.bind(this, startTime, target, next)); + meta.css.minify('admin', step.bind(this, startTime, target, next)); break; case 'tpl': diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 2b1a60e281..25b2ae34fd 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -108,11 +108,7 @@ uploadsController.uploadSound = function (req, res, next) { var soundsPath = path.join(__dirname, '../../../build/public/sounds'), filePath = path.join(__dirname, '../../../public/uploads/sounds', uploadedFile.name); - if (process.platform === 'win32') { - fs.link(filePath, path.join(soundsPath, path.basename(filePath))); - } else { - fs.symlink(filePath, path.join(soundsPath, path.basename(filePath)), 'file'); - } + file.link(filePath, path.join(soundsPath, path.basename(filePath))); fs.unlink(uploadedFile.path, function (err) { if (err) { diff --git a/src/file.js b/src/file.js index df820cc47f..65f86a0bdd 100644 --- a/src/file.js +++ b/src/file.js @@ -102,4 +102,12 @@ file.existsSync = function (path) { return !!exists; }; +file.link = function link(filePath, destPath, cb) { + if (process.platform === 'win32') { + fs.link(filePath, destPath, cb); + } else { + fs.symlink(filePath, destPath, 'file', cb); + } +}; + module.exports = file; diff --git a/src/meta/css.js b/src/meta/css.js index f72ef6004d..d779ff8018 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -18,8 +18,32 @@ var utils = require('../../public/src/utils'); module.exports = function (Meta) { Meta.css = {}; - Meta.css.cache = undefined; - Meta.css.acpCache = undefined; + + var buildImports = { + client: function (source) { + return '@import "./theme";\n' + source + '\n' + [ + '@import "font-awesome";', + '@import (inline) "../../public/vendor/jquery/css/smoothness/jquery-ui.css";', + '@import (inline) "../../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', + '@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";', + '@import "../../public/less/flags.less";', + '@import "../../public/less/blacklist.less";', + '@import "../../public/less/generics.less";', + '@import "../../public/less/mixins.less";', + '@import "../../public/less/global.less";', + ].map(function (str) { return str.replace(/\//g, path.sep); }).join('\n'); + }, + admin: function (source) { + return source + '\n' + [ + '@import "font-awesome";', + '@import "../public/less/admin/admin";', + '@import "../public/less/generics.less";', + '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', + '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', + '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', + ].map(function (str) { return str.replace(/\//g, path.sep); }).join('\n'); + }, + }; Meta.css.minify = function (target, callback) { callback = callback || function () {}; @@ -30,25 +54,25 @@ module.exports = function (Meta) { return callback(err); } - var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'), - baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')), - paths = [ - baseThemePath, - path.join(__dirname, '../../node_modules'), - path.join(__dirname, '../../public/vendor/fontawesome/less') - ], - source = '@import "font-awesome";'; + var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'); + var baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')); + var paths = [ + baseThemePath, + path.join(__dirname, '../../node_modules'), + path.join(__dirname, '../../public/vendor/fontawesome/less') + ]; + var source = ''; - plugins.lessFiles = filterMissingFiles(plugins.lessFiles); - plugins.cssFiles = filterMissingFiles(plugins.cssFiles); + var lessFiles = filterMissingFiles(plugins.lessFiles); + var cssFiles = filterMissingFiles(plugins.cssFiles); async.waterfall([ function (next) { - getStyleSource(plugins.cssFiles, '\n@import (inline) ".', '.css', next); + getStyleSource(cssFiles, '\n@import (inline) ".', '.css', next); }, function (src, next) { source += src; - getStyleSource(plugins.lessFiles, '\n@import ".', '.less', next); + getStyleSource(lessFiles, '\n@import ".', '.less', next); }, function (src, next) { source += src; @@ -59,29 +83,7 @@ module.exports = function (Meta) { return callback(err); } - var acpSource = source; - - if (target !== 'admin.css') { - source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui.css";'; - source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";'; - source += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";'; - source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/flags.less";'; - source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";'; - source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";'; - source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";'; - source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/global.less";'; - source = '@import "./theme";\n' + source; - - minify(source, paths, 'cache', callback); - } else { - acpSource += '\n@import "..' + path.sep + 'public/less/admin/admin";\n'; - acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";\n'; - acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";\n'; - acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui.css";'; - acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";'; - - minify(acpSource, paths, 'acpCache', callback); - } + minify(buildImports[target](source), paths, target, callback); }); }); }; @@ -115,22 +117,22 @@ module.exports = function (Meta) { }); } - Meta.css.commitToFile = function (filename, callback) { - var file = (filename === 'acpCache' ? 'admin' : 'stylesheet') + '.css'; + Meta.css.commitToFile = function (target, source, callback) { + var filename = (target === 'client' ? 'stylesheet' : 'admin') + '.css'; - fs.writeFile(path.join(__dirname, '../../build/public/' + file), Meta.css[filename], function (err) { + fs.writeFile(path.join(__dirname, '../../build/public/' + filename), source, function (err) { if (!err) { - winston.verbose('[meta/css] ' + file + ' committed to disk.'); + winston.verbose('[meta/css] ' + target + ' CSS committed to disk.'); } else { winston.error('[meta/css] ' + err.message); - process.exit(0); + process.exit(1); } callback(); }); }; - function minify(source, paths, destination, callback) { + function minify(source, paths, target, callback) { callback = callback || function () {}; less.render(source, { paths: paths @@ -140,20 +142,14 @@ module.exports = function (Meta) { return callback(err); } - postcss([ autoprefixer, clean() ]).process(lessOutput.css).then(function (result) { + postcss(global.env === 'development' ? [ autoprefixer ] : [ autoprefixer, clean() ]).process(lessOutput.css).then(function (result) { result.warnings().forEach(function (warn) { winston.verbose(warn.toString()); }); - Meta.css[destination] = result.css; - // Save the compiled CSS in public/ so things like nginx can serve it - if (nconf.get('local-assets') === undefined || nconf.get('local-assets') !== false) { - return Meta.css.commitToFile(destination, function () { - callback(null, result.css); - }); - } - - callback(null, result.css); + return Meta.css.commitToFile(target, result.css, function () { + callback(null, result.css); + }); }); }); } diff --git a/src/meta/js.js b/src/meta/js.js index 8c3794043f..d479e9161c 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -4,9 +4,11 @@ var winston = require('winston'); var fork = require('child_process').fork; var path = require('path'); var async = require('async'); -var nconf = require('nconf'); var fs = require('fs'); var mkdirp = require('mkdirp'); +var rimraf = require('rimraf'); + +var file = require('../file'); var plugins = require('../plugins'); var utils = require('../../public/src/utils'); @@ -88,19 +90,10 @@ module.exports = function (Meta) { }; Meta.js.linkModules = function (callback) { - function link(filePath, destPath, cb) { - if (process.platform === 'win32') { - fs.link(filePath, destPath, cb); - } else { - fs.symlink(filePath, destPath, 'file', cb); - } - } - - plugins.reload(function (err) { + rimraf(path.join(__dirname, '../../build/public/src/modules'), function (err) { if (err) { return callback(err); } - async.each(Object.keys(Meta.js.scripts.modules), function (relPath, next) { var filePath = path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]); var destPath = path.join(__dirname, '../../build/public/src/modules', relPath); @@ -110,7 +103,7 @@ module.exports = function (Meta) { return next(err); } - link(filePath, destPath, next); + file.link(filePath, destPath, next); }); }, callback); }); @@ -143,12 +136,7 @@ module.exports = function (Meta) { winston.verbose('[meta/js] ' + target + ' minification complete'); minifier.kill(); - if (nconf.get('local-assets') === undefined || nconf.get('local-assets') !== false) { - return Meta.js.commitToFile(target, callback); - } else { - return callback(); - } - + Meta.js.commitToFile(target, callback); break; case 'error': winston.error('[meta/js] Could not compile ' + target + ': ' + message.message); diff --git a/src/meta/sounds.js b/src/meta/sounds.js index 6acbd77a1f..e44f43cccc 100644 --- a/src/meta/sounds.js +++ b/src/meta/sounds.js @@ -8,6 +8,7 @@ var rimraf = require('rimraf'); var mkdirp = require('mkdirp'); var async = require('async'); +var file = require('../file'); var plugins = require('../plugins'); var db = require('../database'); @@ -107,21 +108,6 @@ module.exports = function (Meta) { return; } - if (nconf.get('local-assets') === false) { - // Don't regenerate the public/sounds/ directory. Instead, create a mapping for the router to use - Meta.sounds._filePathHash = filePaths.reduce(function (hash, filePath) { - hash[path.basename(filePath)] = filePath; - return hash; - }, {}); - - winston.verbose('[sounds] Sounds OK'); - if (typeof next === 'function') { - return next(); - } else { - return; - } - } - // Clear the sounds directory async.series([ function (next) { @@ -138,11 +124,7 @@ module.exports = function (Meta) { // Link paths async.each(filePaths, function (filePath, next) { - if (process.platform === 'win32') { - fs.link(filePath, path.join(soundsPath, path.basename(filePath)), next); - } else { - fs.symlink(filePath, path.join(soundsPath, path.basename(filePath)), 'file', next); - } + file.link(filePath, path.join(soundsPath, path.basename(filePath)), next); }, function (err) { if (!err) { winston.verbose('[sounds] Sounds OK'); diff --git a/src/plugins/load.js b/src/plugins/load.js index 3121f8457e..cf6e0b4780 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -54,7 +54,8 @@ module.exports = function (Plugins) { async.parallel([ async.apply(mapFiles, pluginData, 'css', 'cssFiles'), async.apply(mapFiles, pluginData, 'less', 'lessFiles'), - async.apply(mapClientSideScripts, pluginData) + async.apply(mapClientSideScripts, pluginData), + async.apply(mapClientModules, pluginData), ], next); }, next); } diff --git a/src/routes/index.js b/src/routes/index.js index e1f95891cf..506a0063ec 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -167,7 +167,8 @@ module.exports = function (app, middleware, hotswapIds) { '/templates/', '/src/', '/images/', - // '/sounds/', + '/language/', + '/sounds/', ]; app.use(relativePath, function (req, res, next) { if (deprecatedPaths.some(function (path) { return req.path.startsWith(path); })) { diff --git a/src/routes/meta.js b/src/routes/meta.js index af4e89d897..cb089f1200 100644 --- a/src/routes/meta.js +++ b/src/routes/meta.js @@ -1,20 +1,5 @@ "use strict"; -var path = require('path'); -var nconf = require('nconf'); - -var meta = require('../meta'); - -function sendSoundFile(req, res, next) { - var resolved = meta.sounds._filePathHash[path.basename(req.path)]; - - if (resolved) { - res.status(200).sendFile(resolved); - } else { - next(); - } -} - module.exports = function (app, middleware, controllers) { app.get('/sitemap.xml', controllers.sitemap.render); app.get('/sitemap/pages.xml', controllers.sitemap.getPages); @@ -23,8 +8,4 @@ module.exports = function (app, middleware, controllers) { app.get('/robots.txt', controllers.robots); app.get('/manifest.json', controllers.manifest); app.get('/css/previews/:theme', controllers.admin.themes.get); - - if (nconf.get('local-assets') === false) { - app.get('/sounds/*', middleware.addExpiresHeaders, sendSoundFile); - } }; diff --git a/src/webserver.js b/src/webserver.js index 2e7afdb228..a8324f7376 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -45,7 +45,7 @@ server.on('error', function (err) { winston.error(err); if (err.code === 'EADDRINUSE') { winston.error('NodeBB address in use, exiting...'); - process.exit(0); + process.exit(1); } else { throw err; }