diff --git a/package.json b/package.json index 7897e42..8d8bf3a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "browserify": "10.2.6", + "enb-source-map": "1.8.0", "uglify-js": "2.4.24", "vow": "0.4.10", "vow-node": "0.3.0" diff --git a/techs/browser-js.js b/techs/browser-js.js index 4db2ade..f9c78fb 100644 --- a/techs/browser-js.js +++ b/techs/browser-js.js @@ -1,5 +1,8 @@ -var minify = require('uglify-js').minify, - EOL = require('os').EOL; +var vow = require('vow'), + vfs = require('enb/lib/fs/async-fs'), + utils = require('enb-source-map/lib/utils'), + File = require('enb-source-map/lib/file'), + minify = require('uglify-js').minify; /** * @class BrowserJsTech @@ -21,6 +24,7 @@ var minify = require('uglify-js').minify, * @param {Boolean} [options.iife=false] Adds an option to wrap merged
* files to IIFE. * @param {Boolean} [options.compress=false] Minifies and compresses JS code. + * @param {Boolean} [options.sourcemap=false] Includes inline source maps. * * @example * // Code in a file system before build: @@ -59,33 +63,60 @@ module.exports = require('enb/lib/build-flow').create() .useFileList(['vanilla.js', 'js', 'browser.js']) .defineOption('iife', false) .defineOption('compress', false) - .wrapper(function (str) { - if (this._compress) { - return minify(str, { fromString: true }).code; - } + .defineOption('sourcemap', false) + .builder(function (sourceFiles) { + return this._readSourceFiles(sourceFiles) + .then(function (sources) { + var file = new File(this.node.resolvePath(this._target), { sourceMap: this._sourcemap }), + needWrapIIFE = this._iife, + needToAddComments = !this._compress, + compressOptions = { fromString: true }, + compressed; - return str; - }) - .justJoinFiles(function (filename, contents) { - var relPath = this.node.relativePath(filename); + sources.forEach(function (source) { + needToAddComments && file.writeLine('/* begin: ' + source.relPath + ' */'); + needWrapIIFE && file.writeLine('(function(){'); + file.writeFileContent(source.path, source.contents); + needWrapIIFE && file.writeLine('}());'); + needToAddComments && file.writeLine('/* end: ' + source.relPath + ' */'); + }); - if (this._iife) { - contents = [ - '(function(){', - contents, - '}());' - ].join(EOL); - } + if (!this._compress) { + return file.render(); + } - // after compression comments will be removed - if (!this._compress) { - contents = [ - '/* begin: ' + relPath + ' */', - contents, - '/* end: ' + relPath + ' *' + '/' - ].join(EOL); - } + if (!this._sourcemap) { + return minify(file.render(), compressOptions).code; + } - return contents; + compressOptions.inSourceMap = file.getSourceMap(); + compressOptions.outSourceMap = this._target + '.map'; + + compressed = minify(file.getContent(), compressOptions); + return utils.joinContentAndSourceMap(compressed.code, compressed.map); + }, this); + }) + .methods({ + /** + * Reads source js files. + * + * @protected + * @param {FileList} files + * @returns {FileData[]} + */ + _readSourceFiles: function (files) { + var node = this.node; + + return vow.all(files.map(function (file) { + return vfs.read(file.fullname, 'utf8') + .then(function (contents) { + return { + path: file.fullname, + relPath: node.relativePath(file.fullname), + contents: contents + }; + }); + })); + } }) .createTech(); diff --git a/test/techs/browser-js.test.js b/test/techs/browser-js.test.js index c46e99a..2c1eaeb 100644 --- a/test/techs/browser-js.test.js +++ b/test/techs/browser-js.test.js @@ -25,7 +25,8 @@ describe('browser-js', function () { '/* end: ../blocks/block0.vanilla.js */', '/* begin: ../blocks/block1.browser.js */', 'Hello1', - '/* end: ../blocks/block1.browser.js */' + '/* end: ../blocks/block1.browser.js */', + '' ].join(EOL); return build(blocks) @@ -75,6 +76,32 @@ describe('browser-js', function () { }); }); }); + + describe('sourcemap', function () { + it('must generate sourcemap', function () { + var blocks = { + 'block0.vanilla.js': 'Hello0', + 'block1.browser.js': 'Hello1' + }; + + return build(blocks, { sourcemap: true }) + .spread(function (content) { + content.should.match(/sourceMappingURL/); + }); + }); + + it('must generate sourcemap with minification', function () { + var blocks = { + 'block0.vanilla.js': 'Hello0', + 'block1.browser.js': 'Hello1' + }; + + return build(blocks, { sourcemap: true, compress: true }) + .spread(function (content) { + content.should.match(/Hello0,Hello1;\n\/\/#\ssourceMappingURL/); + }); + }); + }); }); function build(blocks, options, isNeedRequire) {