From f76e0b593d4d92d1ec4d0fcf3b760eb5015579dc Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 3 Mar 2015 08:38:50 -0800 Subject: [PATCH] Updates from Tuesday, March 3rd - [ReactNative] Fix OSS Projects | Spencer Ahrens - [react-packager] check-in node_modules and update tests | Amjad Masad - [react-packager] Cleanup package.json | Amjad Masad - [react-packager] Implement bundle minification | Amjad Masad - [react-packager] Add dev option to CLI | James Ide | Amjad Masad - [react-packager] Add uglify-js library | Amjad Masad - [f8] Make map zoomable on double-tap | Alex Kotliarskyi --- packager.js | 6 ++- react-packager/src/Packager/Package.js | 51 ++++++++++++++++--- .../src/Packager/__tests__/Package-test.js | 19 ++++++- .../src/Packager/__tests__/Packager-test.js | 1 + .../src/Server/__tests__/Server-test.js | 1 + react-packager/src/Server/index.js | 7 ++- 6 files changed, 74 insertions(+), 11 deletions(-) diff --git a/packager.js b/packager.js index 6d5336ef..08bab38b 100644 --- a/packager.js +++ b/packager.js @@ -29,6 +29,10 @@ var options = parseCommandLine([{ }, { command: 'root', description: 'add another root(s) to be used by the packager in this project', +}, { + command: 'dev', + default: true, + description: 'produce development packages with extra warnings enabled', }]); if (!options.projectRoots) { @@ -93,7 +97,7 @@ function openStackFrameInEditor(req, res, next) { function getAppMiddleware(options) { return ReactPackager.middleware({ - dev: true, + dev: options.dev, projectRoots: options.projectRoots, blacklistRE: blacklist(false), cacheVersion: '2', diff --git a/react-packager/src/Packager/Package.js b/react-packager/src/Packager/Package.js index a4080d3b..3ef9c528 100644 --- a/react-packager/src/Packager/Package.js +++ b/react-packager/src/Packager/Package.js @@ -2,6 +2,7 @@ var _ = require('underscore'); var base64VLQ = require('./base64-vlq'); +var UglifyJS = require('uglify-js'); module.exports = Package; @@ -27,6 +28,7 @@ Package.prototype.addModule = function( }; Package.prototype.finalize = function(options) { + options = options || {}; if (options.runMainModule) { var runCode = ';require("' + this._mainModuleId + '");'; this.addModule( @@ -40,12 +42,49 @@ Package.prototype.finalize = function(options) { Object.seal(this._modules); }; -Package.prototype.getSource = function() { - return this._source || ( - this._source = _.pluck(this._modules, 'transformedCode').join('\n') + '\n' + - 'RAW_SOURCE_MAP = ' + JSON.stringify(this.getSourceMap({excludeSource: true})) + - ';\n' + '\/\/@ sourceMappingURL=' + this._sourceMapUrl - ); +Package.prototype.getSource = function(options) { + if (!this._source) { + options = options || {}; + this._source = _.pluck(this._modules, 'transformedCode').join('\n'); + if (options.inlineSourceMap) { + var sourceMap = this.getSourceMap({excludeSource: true}); + this._source += '\nRAW_SOURCE_MAP = ' + JSON.stringify(sourceMap) + ';'; + } + this._source += '\n\/\/@ sourceMappingURL=' + this._sourceMapUrl; + } + return this._source; +}; + +Package.prototype.getMinifiedSourceAndMap = function() { + var source = this.getSource({inlineSourceMap: false}); + try { + return UglifyJS.minify(source, { + fromString: true, + outSourceMap: 'bundle.js', + inSourceMap: this.getSourceMap(), + }); + } catch(e) { + // Sometimes, when somebody is using a new syntax feature that we + // don't yet have transform for, the untransformed line is sent to + // uglify, and it chokes on it. This code tries to print the line + // and the module for easier debugging + var errorMessage = 'Error while minifying JS\n'; + if (e.line) { + errorMessage += 'Transformed code line: "' + + source.split('\n')[e.line - 1] + '"\n'; + } + if (e.pos) { + var fromIndex = source.lastIndexOf('__d(\'', e.pos); + if (fromIndex > -1) { + fromIndex += '__d(\''.length; + var toIndex = source.indexOf('\'', fromIndex); + errorMessage += 'Module name (best guess): ' + + source.substring(fromIndex, toIndex) + '\n'; + } + } + errorMessage += e.toString(); + throw new Error(errorMessage); + } }; Package.prototype.getSourceMap = function(options) { diff --git a/react-packager/src/Packager/__tests__/Package-test.js b/react-packager/src/Packager/__tests__/Package-test.js index 74a2f437..41630fc4 100644 --- a/react-packager/src/Packager/__tests__/Package-test.js +++ b/react-packager/src/Packager/__tests__/Package-test.js @@ -21,7 +21,7 @@ describe('Package', function() { ppackage.addModule('transformed foo;', 'source foo', 'foo path'); ppackage.addModule('transformed bar;', 'source bar', 'bar path'); ppackage.finalize({}); - expect(ppackage.getSource()).toBe([ + expect(ppackage.getSource({inlineSourceMap: true})).toBe([ 'transformed foo;', 'transformed bar;', 'RAW_SOURCE_MAP = "test-source-map";', @@ -34,7 +34,7 @@ describe('Package', function() { ppackage.addModule('transformed bar;', 'source bar', 'bar path'); ppackage.setMainModuleId('foo'); ppackage.finalize({runMainModule: true}); - expect(ppackage.getSource()).toBe([ + expect(ppackage.getSource({inlineSourceMap: true})).toBe([ 'transformed foo;', 'transformed bar;', ';require("foo");', @@ -42,6 +42,21 @@ describe('Package', function() { '\/\/@ sourceMappingURL=test_url', ].join('\n')); }); + + it('should get minified source', function() { + var minified = { + code: 'minified', + map: 'map', + }; + + require('uglify-js').minify = function() { + return minified; + }; + + ppackage.addModule('transformed foo;', 'source foo', 'foo path'); + ppackage.finalize(); + expect(ppackage.getMinifiedSourceAndMap()).toBe(minified); + }); }); describe('sourcemap package', function() { diff --git a/react-packager/src/Packager/__tests__/Packager-test.js b/react-packager/src/Packager/__tests__/Packager-test.js index 2e43e91a..d8348be8 100644 --- a/react-packager/src/Packager/__tests__/Packager-test.js +++ b/react-packager/src/Packager/__tests__/Packager-test.js @@ -6,6 +6,7 @@ jest .dontMock('q') .dontMock('os') .dontMock('underscore') + .setMock('uglify-js') .dontMock('../'); var q = require('q'); diff --git a/react-packager/src/Server/__tests__/Server-test.js b/react-packager/src/Server/__tests__/Server-test.js index e6020c79..5a5d62b0 100644 --- a/react-packager/src/Server/__tests__/Server-test.js +++ b/react-packager/src/Server/__tests__/Server-test.js @@ -10,6 +10,7 @@ jest.setMock('worker-farm', function() { return function() {}; }) return setTimeout(fn, 0); } }) + .setMock('uglify-js') .dontMock('../'); var q = require('q'); diff --git a/react-packager/src/Server/index.js b/react-packager/src/Server/index.js index accce205..83592fea 100644 --- a/react-packager/src/Server/index.js +++ b/react-packager/src/Server/index.js @@ -51,6 +51,7 @@ var validateOpts = declareOpts({ function Server(options) { var opts = validateOpts(options); + this._dev = opts.dev; this._projectRoots = opts.projectRoots; this._packages = Object.create(null); this._packager = new Packager(opts); @@ -72,13 +73,14 @@ Server.prototype._onFileChange = function(type, filepath, root) { }; Server.prototype._rebuildPackages = function() { + var dev = this._dev; var buildPackage = this._buildPackage.bind(this); var packages = this._packages; Object.keys(packages).forEach(function(key) { var options = getOptionsFromPath(url.parse(key).pathname); packages[key] = buildPackage(options).then(function(p) { // Make a throwaway call to getSource to cache the source string. - p.getSource(); + p.getSource({inlineSourceMap: dev}); return p; }); }); @@ -159,10 +161,11 @@ Server.prototype.processRequest = function(req, res, next) { var options = getOptionsFromPath(url.parse(req.url).pathname); var building = this._packages[req.url] || this._buildPackage(options); this._packages[req.url] = building; + var dev = this._dev; building.then( function(p) { if (requestType === 'bundle') { - res.end(p.getSource()); + res.end(p.getSource({inlineSourceMap: dev})); Activity.endEvent(startReqEventId); } else if (requestType === 'map') { res.end(JSON.stringify(p.getSourceMap()));