From d988e8a50f13462aff6e4b9e89caf17d68fff8a1 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 21 May 2017 18:20:01 -0600 Subject: [PATCH 1/3] Test minifier --- .eslintignore | 1 + src/meta/minifier.js | 2 +- test/build.js | 94 ++++++++++++++++++++++++++++++++++++++++++++ test/files/1.css | 1 + test/files/1.js | 5 +++ test/files/2.js | 3 ++ test/files/2.less | 1 + 7 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 test/files/1.css create mode 100644 test/files/1.js create mode 100644 test/files/2.js create mode 100644 test/files/2.less diff --git a/.eslintignore b/.eslintignore index 3278600389..11b456699f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -17,3 +17,4 @@ logs/ /coverage /build .eslintrc +test/files diff --git a/src/meta/minifier.js b/src/meta/minifier.js index da7c570ce7..7c1a83bea4 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -156,7 +156,7 @@ function concat(data, callback) { return callback(err); } - var output = files.join(os.EOL + ';'); + var output = files.join('\n;'); callback(null, { code: output }); }); diff --git a/test/build.js b/test/build.js index 3b6890ebe6..20d43033a9 100644 --- a/test/build.js +++ b/test/build.js @@ -1,8 +1,102 @@ 'use strict'; +var string = require('string'); +var path = require('path'); +var fs = require('fs'); var assert = require('assert'); +var mkdirp = require('mkdirp'); var db = require('./mocks/databasemock'); +var file = require('../src/file'); + +describe('minifier', function () { + before(function (done) { + mkdirp(path.join(__dirname, '../build/test'), done); + }); + + var minifier = require('../src/meta/minifier'); + var scripts = [ + path.resolve(__dirname, './files/1.js'), + path.resolve(__dirname, './files/2.js'), + ]; + it('.js.bundle() should concat scripts', function (done) { + minifier.js.bundle(scripts, false, false, function (err, bundle) { + assert.ifError(err); + assert.strictEqual( + bundle.code, + '(function (window, document) {' + + '\n\twindow.doStuff = function () {' + + '\n\t\tdocument.body.innerHTML = \'Stuff has been done\';' + + '\n\t};' + + '\n})(window, document);' + + '\n' + + '\n;function foo(name, age) {' + + '\n\treturn \'The person known as "\' + name + \'" is \' + age + \' years old\';' + + '\n}' + + '\n' + ); + done(); + }); + }); + + it('.js.bundle() should minify scripts', function (done) { + minifier.js.bundle(scripts, true, false, function (err, bundle) { + assert.ifError(err); + assert.strictEqual( + bundle.code, + '(function(n,o){n.doStuff=function(){o.body.innerHTML="Stuff has been done"}})(window,document);function foo(n,o){return\'The person known as "\'+n+\'" is \'+o+" years old"}' + ); + done(); + }); + }); + + it('.js.minifyBatch() should minify each script', function (done) { + var s = scripts.map(function (script) { + return { + srcPath: script, + destPath: path.resolve(__dirname, '../build/test', path.basename(script)), + }; + }); + minifier.js.minifyBatch(s, false, function (err) { + assert.ifError(err); + + assert(file.existsSync(s[0].destPath)); + assert(file.existsSync(s[1].destPath)); + + fs.readFile(s[0].destPath, function (err, buffer) { + assert.ifError(err); + assert.strictEqual( + buffer.toString(), + '(function(n,o){n.doStuff=function(){o.body.innerHTML="Stuff has been done"}})(window,document);' + ); + done(); + }); + }); + }); + + var styles = [ + '@import (inline) "./1.css";', + '@import "./2.less";', + ].join('\n'); + var paths = [ + path.resolve(__dirname, './files'), + ]; + it('.css.bundle() should concat styles', function (done) { + minifier.css.bundle(styles, paths, false, false, function (err, bundle) { + assert.ifError(err); + assert.strictEqual(bundle.code, '.help { margin: 10px; } .yellow { background: yellow; }\n.help {\n display: block;\n}\n.help .blue {\n background: blue;\n}\n'); + done(); + }); + }); + + it('.css.bundle() should minify styles', function (done) { + minifier.css.bundle(styles, paths, true, false, function (err, bundle) { + assert.ifError(err); + assert.strictEqual(bundle.code, '.help{margin:10px;display:block}.yellow{background:#ff0}.help .blue{background:#00f}'); + done(); + }); + }); +}); describe('Build', function () { it('should build all assets', function (done) { diff --git a/test/files/1.css b/test/files/1.css new file mode 100644 index 0000000000..840cf64b36 --- /dev/null +++ b/test/files/1.css @@ -0,0 +1 @@ +.help { margin: 10px; } .yellow { background: yellow; } \ No newline at end of file diff --git a/test/files/1.js b/test/files/1.js new file mode 100644 index 0000000000..b20055f8ee --- /dev/null +++ b/test/files/1.js @@ -0,0 +1,5 @@ +(function (window, document) { + window.doStuff = function () { + document.body.innerHTML = 'Stuff has been done'; + }; +})(window, document); diff --git a/test/files/2.js b/test/files/2.js new file mode 100644 index 0000000000..9369213316 --- /dev/null +++ b/test/files/2.js @@ -0,0 +1,3 @@ +function foo(name, age) { + return 'The person known as "' + name + '" is ' + age + ' years old'; +} diff --git a/test/files/2.less b/test/files/2.less new file mode 100644 index 0000000000..cdd5d5b5f2 --- /dev/null +++ b/test/files/2.less @@ -0,0 +1 @@ +.help { display: block; .blue { background: blue; } } \ No newline at end of file From 62546bc4fd06210fa9890746f69a6673c3a99ec8 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 21 May 2017 19:01:41 -0600 Subject: [PATCH 2/3] Build tests --- test/build.js | 96 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/test/build.js b/test/build.js index 20d43033a9..215b823ce2 100644 --- a/test/build.js +++ b/test/build.js @@ -5,6 +5,7 @@ var path = require('path'); var fs = require('fs'); var assert = require('assert'); var mkdirp = require('mkdirp'); +var rimraf = require('rimraf'); var db = require('./mocks/databasemock'); var file = require('../src/file'); @@ -98,12 +99,97 @@ describe('minifier', function () { }); }); -describe('Build', function () { - it('should build all assets', function (done) { - this.timeout(50000); - var build = require('../src/meta/build'); - build.buildAll(function (err) { +describe('Build', function (done) { + var build = require('../src/meta/build'); + + before(function (done) { + rimraf(path.join(__dirname, '../build/public'), done); + }); + + it('should build plugin static dirs', function (done) { + build.build(['plugin static dirs'], function (err) { + assert.ifError(err); + assert(file.existsSync(path.join(__dirname, '../build/public/plugins/nodebb-plugin-dbsearch/dbsearch'))); + done(); + }); + }); + + it('should build requirejs modules', function (done) { + build.build(['requirejs modules'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/src/modules/Chart.js'); + assert(file.existsSync(filename)); + assert(fs.readFileSync(filename).toString().startsWith('/*!\n * Chart.js')); + done(); + }); + }); + + it('should build client js bundle', function (done) { + build.build(['client js bundle'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/nodebb.min.js'); + assert(file.existsSync(filename)); + assert(fs.readFileSync(filename).length > 1000); + done(); + }); + }); + + it('should build admin js bundle', function (done) { + build.build(['admin js bundle'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/acp.min.js'); + assert(file.existsSync(filename)); + assert(fs.readFileSync(filename).length > 1000); + done(); + }); + }); + + it('should build client side styles', function (done) { + build.build(['client side styles'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/stylesheet.css'); + assert(file.existsSync(filename)); + assert(fs.readFileSync(filename).toString().startsWith('/*! normalize.css')); + done(); + }); + }); + + it('should build admin control panel styles', function (done) { + build.build(['admin control panel styles'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/admin.css'); + assert(file.existsSync(filename)); + assert(fs.readFileSync(filename).toString().startsWith('@charset "UTF-8";')); + done(); + }); + }); + + it('should build templates', function (done) { + build.build(['templates'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/templates/admin/header.tpl'); + assert(file.existsSync(filename)); + assert(fs.readFileSync(filename).toString().startsWith('')); + done(); + }); + }); + + it('should build languages', function (done) { + build.build(['languages'], function (err) { + assert.ifError(err); + var filename = path.join(__dirname, '../build/public/language/en-GB/global.json'); + assert(file.existsSync(filename)); + var global = fs.readFileSync(filename).toString(); + assert.strictEqual(JSON.parse(global).home, 'Home'); + done(); + }); + }); + + it('should build sounds', function (done) { + build.build(['sounds'], function (err) { assert.ifError(err); + var filename = path.join(__dirname, '../build/public/sounds/fileMap.json'); + assert(file.existsSync(filename)); done(); }); }); From 2a9cdb7be24f889cc75693f42f4f025d9898860f Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Tue, 23 May 2017 14:00:37 -0600 Subject: [PATCH 3/3] Add `--threads=#` option for setting max threads Make grunt NODE_ENV development by default --- Gruntfile.js | 2 ++ src/meta/build.js | 5 +++++ src/meta/minifier.js | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index c97795e743..9a84c15aa0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,6 +8,8 @@ var initWorker; var incomplete = []; var running = 0; +env.NODE_ENV = env.NODE_ENV || 'development'; + module.exports = function (grunt) { var args = []; var initArgs = ['--build']; diff --git a/src/meta/build.js b/src/meta/build.js index cc3321923f..290957512a 100644 --- a/src/meta/build.js +++ b/src/meta/build.js @@ -180,6 +180,11 @@ function build(targets, callback) { async.series([ beforeBuild, function (next) { + var threads = parseInt(nconf.get('threads'), 10); + if (threads) { + require('./minifier').maxThreads = threads - 1; + } + var parallel = !nconf.get('series'); if (parallel) { winston.info('[build] Building in parallel mode'); diff --git a/src/meta/minifier.js b/src/meta/minifier.js index 7c1a83bea4..07f33d8253 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -41,9 +41,21 @@ function setupDebugging() { var pool = []; var free = []; -Minifier.maxThreads = os.cpus().length - 1; +var maxThreads = 0; + +Object.defineProperty(Minifier, 'maxThreads', { + get: function () { + return maxThreads; + }, + set: function (val) { + maxThreads = val; + winston.verbose('[minifier] utilizing a maximum of ' + maxThreads + ' additional threads'); + }, + configurable: true, + enumerable: true, +}); -winston.verbose('[minifier] utilizing a maximum of ' + Minifier.maxThreads + ' additional threads'); +Minifier.maxThreads = os.cpus().length - 1; Minifier.killAll = function () { pool.forEach(function (child) {