From 53eced6be061f0505a6f2386383b60a7009f2191 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Thu, 25 May 2017 01:33:04 -0600 Subject: [PATCH] uglify-js@3, JS source maps :cake: --- package.json | 2 +- src/meta/js.js | 26 +++++--- src/meta/minifier.js | 145 ++++++++++++++++++++++++++----------------- test/build.js | 56 +++++++++++------ 4 files changed, 145 insertions(+), 84 deletions(-) diff --git a/package.json b/package.json index 7d7bacd4d9..c8a988b62f 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "string": "^3.0.0", "templates.js": "0.3.10", "toobusy-js": "^0.5.1", - "uglify-js": "^2.6.0", + "uglify-js": "^3.0.11", "underscore": "^1.8.3", "underscore.deep": "^0.5.1", "validator": "7.0.0", diff --git a/src/meta/js.js b/src/meta/js.js index d0399c70c1..4858f69bf5 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -88,6 +88,8 @@ module.exports = function (Meta) { }, }; + var basePath = path.resolve(__dirname, '../..'); + function minifyModules(modules, fork, callback) { var moduleDirs = modules.reduce(function (prev, mod) { var dir = path.resolve(path.dirname(mod.destPath)); @@ -201,7 +203,10 @@ module.exports = function (Meta) { }; }); - moduleFiles = moduleFiles.concat(mods); + moduleFiles = moduleFiles.concat(mods).map(function (mod) { + mod.filename = path.relative(basePath, mod.srcPath).replace(/\\/g, '/'); + return mod; + }); next(); }); @@ -287,8 +292,6 @@ module.exports = function (Meta) { return callback(err); } - var basePath = path.resolve(__dirname, '../..'); - var scripts = Meta.js.scripts.base.concat(pluginScripts); if (target === 'client' && global.env !== 'development') { @@ -296,7 +299,11 @@ module.exports = function (Meta) { } scripts = scripts.map(function (script) { - return path.resolve(basePath, script).replace(/\\/g, '/'); + var srcPath = path.resolve(basePath, script).replace(/\\/g, '/'); + return { + srcPath: srcPath, + filename: path.relative(basePath, srcPath).replace(/\\/g, '/'), + }; }); callback(null, scripts); @@ -315,12 +322,13 @@ module.exports = function (Meta) { }, function (files, next) { var minify = global.env !== 'development'; - - minifier.js.bundle(files, minify, fork, next); - }, - function (bundle, next) { var filePath = path.join(__dirname, '../../build/public', fileNames[target]); - fs.writeFile(filePath, bundle.code, next); + + minifier.js.bundle({ + files: files, + filename: fileNames[target], + destPath: filePath, + }, minify, fork, next); }, ], callback); }; diff --git a/src/meta/minifier.js b/src/meta/minifier.js index 07f33d8253..5e78203c26 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -1,18 +1,16 @@ 'use strict'; -var uglifyjs = require('uglify-js'); -var async = require('async'); var fs = require('fs'); var childProcess = require('child_process'); var os = require('os'); +var uglifyjs = require('uglify-js'); +var async = require('async'); var winston = require('winston'); var less = require('less'); var postcss = require('postcss'); var autoprefixer = require('autoprefixer'); var clean = require('postcss-clean'); -var file = require('../file'); - var Minifier = module.exports; function setupDebugging() { @@ -163,13 +161,21 @@ function executeAction(action, fork, callback) { function concat(data, callback) { if (data.files && data.files.length) { - async.mapLimit(data.files, 1000, fs.readFile, function (err, files) { + async.mapLimit(data.files, 1000, function (ref, next) { + fs.readFile(ref.srcPath, function (err, buffer) { + if (err) { + return next(err); + } + + next(null, buffer.toString()); + }); + }, function (err, files) { if (err) { return callback(err); } var output = files.join('\n;'); - callback(null, { code: output }); + fs.writeFile(data.destPath, output, callback); }); return; @@ -179,81 +185,108 @@ function concat(data, callback) { } actions.concat = concat; -function minifyJS(data, callback) { - if (data.batch) { - async.eachLimit(data.files, 1000, function (ref, next) { - var srcPath = ref.srcPath; - var destPath = ref.destPath; - - fs.readFile(srcPath, function (err, buffer) { - if (err && err.code === 'ENOENT') { - return next(null, null); - } - if (err) { - return next(err); - } - - try { - var minified = uglifyjs.minify(buffer.toString(), { - // outSourceMap: data.filename + '.map', - compress: data.compress, - fromString: true, - output: { - // suppress uglify line length warnings - max_line_len: 400000, - }, - }); - - fs.writeFile(destPath, minified.code, next); - } catch (e) { - next(e); - } - }); - }, callback); - - return; - } +function minifyJS_batch(data, callback) { + async.eachLimit(data.files, 1000, function (ref, next) { + var srcPath = ref.srcPath; + var destPath = ref.destPath; + var filename = ref.filename; - if (data.files && data.files.length) { - async.filter(data.files, file.exists, function (err, scripts) { + fs.readFile(srcPath, function (err, buffer) { if (err) { - return callback(err); + return next(err); } + var scripts = {}; + scripts[filename] = buffer.toString(); + try { var minified = uglifyjs.minify(scripts, { - // outSourceMap: data.filename + '.map', - compress: data.compress, - fromString: false, + sourceMap: { + filename: filename, + url: filename + '.map', + includeSources: true, + }, }); - callback(null, minified); + async.parallel([ + async.apply(fs.writeFile, destPath, minified.code), + async.apply(fs.writeFile, destPath + '.map', minified.map), + ], next); } catch (e) { - callback(e); + next(e); } }); + }, callback); +} +actions.minifyJS_batch = minifyJS_batch; - return; - } +function minifyJS(data, callback) { + async.mapLimit(data.files, 1000, function (ref, next) { + var srcPath = ref.srcPath; + var filename = ref.filename; - callback(); + fs.readFile(srcPath, function (err, buffer) { + if (err) { + return next(err); + } + + next(null, { + srcPath: srcPath, + filename: filename, + source: buffer.toString(), + }); + }); + }, function (err, files) { + if (err) { + return callback(err); + } + + var scripts = {}; + files.forEach(function (ref) { + if (!ref) { + return; + } + + scripts[ref.filename] = ref.source; + }); + + try { + var minified = uglifyjs.minify(scripts, { + sourceMap: { + filename: data.filename, + url: data.filename + '.map', + includeSources: true, + }, + compress: { + hoist_funs: false, + }, + }); + + async.parallel([ + async.apply(fs.writeFile, data.destPath, minified.code), + async.apply(fs.writeFile, data.destPath + '.map', minified.map), + ], callback); + } catch (e) { + callback(e); + } + }); } actions.minifyJS = minifyJS; Minifier.js = {}; -Minifier.js.bundle = function (scripts, minify, fork, callback) { +Minifier.js.bundle = function (data, minify, fork, callback) { executeAction({ act: minify ? 'minifyJS' : 'concat', - files: scripts, - compress: false, + files: data.files, + filename: data.filename, + destPath: data.destPath, }, fork, callback); }; Minifier.js.minifyBatch = function (scripts, fork, callback) { executeAction({ - act: 'minifyJS', + act: 'minifyJS_batch', files: scripts, - batch: true, }, fork, callback); }; diff --git a/test/build.js b/test/build.js index 0d61283eac..b8c90f57e4 100644 --- a/test/build.js +++ b/test/build.js @@ -20,12 +20,28 @@ describe('minifier', function () { var scripts = [ path.resolve(__dirname, './files/1.js'), path.resolve(__dirname, './files/2.js'), - ]; + ].map(function (script) { + return { + srcPath: script, + destPath: path.resolve(__dirname, '../build/test', path.basename(script)), + filename: path.basename(script), + }; + }); + it('.js.bundle() should concat scripts', function (done) { - minifier.js.bundle(scripts, false, false, function (err, bundle) { + var destPath = path.resolve(__dirname, '../build/test/concatenated.js'); + + minifier.js.bundle({ + files: scripts, + destPath: destPath, + filename: 'concatenated.js', + }, false, false, function (err) { assert.ifError(err); + + assert(file.existsSync(destPath)); + assert.strictEqual( - bundle.code, + fs.readFileSync(destPath).toString(), '(function (window, document) {' + '\n\twindow.doStuff = function () {' + '\n\t\tdocument.body.innerHTML = \'Stuff has been done\';' + @@ -40,36 +56,40 @@ describe('minifier', function () { done(); }); }); - it('.js.bundle() should minify scripts', function (done) { - minifier.js.bundle(scripts, true, false, function (err, bundle) { + var destPath = path.resolve(__dirname, '../build/test/minified.js'); + + minifier.js.bundle({ + files: scripts, + destPath: destPath, + filename: 'minified.js', + }, true, false, function (err) { assert.ifError(err); + + assert(file.existsSync(destPath)); + 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"}' + fs.readFileSync(destPath).toString(), + '!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"}' + + '\n//# sourceMappingURL=minified.js.map' ); 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) { + minifier.js.minifyBatch(scripts, false, function (err) { assert.ifError(err); - assert(file.existsSync(s[0].destPath)); - assert(file.existsSync(s[1].destPath)); + assert(file.existsSync(scripts[0].destPath)); + assert(file.existsSync(scripts[1].destPath)); - fs.readFile(s[0].destPath, function (err, buffer) { + fs.readFile(scripts[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);' + '!function(n,o){n.doStuff=function(){o.body.innerHTML="Stuff has been done"}}(window,document);' + + '\n//# sourceMappingURL=1.js.map' ); done(); });