From d9797e2de7725a3a15abf7b45e5544be87377429 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Mon, 3 Oct 2016 17:58:21 -0700 Subject: [PATCH] Move progress bar from Bundler to Server Summary: Context: I'm trying to add support for sending packager progress events to the client that is downloading the bundle over HTTP multipart response. In order to do that I need the server to know about these events. Currently the bundler doesn't expose any hooks for monitoring the progress, so this diff introduces `onProgress` option for that. Reviewed By: davidaurelio Differential Revision: D3926806 fbshipit-source-id: b7d9c649df4f94ddf5082791209844610650325e --- react-packager/src/Bundler/index.js | 41 ++++----------------------- react-packager/src/Server/index.js | 44 +++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/react-packager/src/Bundler/index.js b/react-packager/src/Bundler/index.js index e9ea7352..f6ed1ed1 100644 --- a/react-packager/src/Bundler/index.js +++ b/react-packager/src/Bundler/index.js @@ -8,11 +8,11 @@ */ 'use strict'; +const Promise = require('promise'); + const assert = require('assert'); const fs = require('fs'); const path = require('path'); -const Promise = require('promise'); -const ProgressBar = require('progress'); const Cache = require('../node-haste').Cache; const Transformer = require('../JSTransformer'); const Resolver = require('../Resolver'); @@ -85,10 +85,6 @@ const validateOpts = declareOpts({ type: 'number', required: false, }, - silent: { - type: 'boolean', - default: false, - }, allowBundleUpdates: { type: 'boolean', default: false, @@ -260,6 +256,7 @@ class Bundler { isolateModuleIDs, generateSourceMaps, assetPlugins, + onProgress, }) { const onResolutionResponse = response => { bundle.setMainModuleId(response.getModuleId(getMainModule(response))); @@ -306,6 +303,7 @@ class Bundler { isolateModuleIDs, generateSourceMaps, assetPlugins, + onProgress, }); } @@ -364,6 +362,7 @@ class Bundler { onResolutionResponse = noop, onModuleTransformed = noop, finalizeBundle = noop, + onProgress = noop, }) { const findEventId = Activity.startEvent( 'Transforming modules', @@ -377,17 +376,6 @@ class Bundler { const modulesByName = Object.create(null); if (!resolutionResponse) { - let onProgress = noop; - if (process.stdout.isTTY && !this._opts.silent) { - const bar = new ProgressBar('transformed :current/:total (:percent)', { - complete: '=', - incomplete: ' ', - width: 40, - total: 1, - }); - onProgress = debouncedTick(bar); - } - resolutionResponse = this.getDependencies({ entryFile, dev, @@ -775,25 +763,6 @@ function getMainModule({dependencies, numPrependedDependencies = 0}) { return dependencies[numPrependedDependencies]; } -function debouncedTick(progressBar) { - let n = 0; - let start, total; - - return (_, t) => { - total = t; - n += 1; - if (start) { - if (progressBar.curr + n >= total || Date.now() - start > 200) { - progressBar.total = total; - progressBar.tick(n); - start = n = 0; - } - } else { - start = Date.now(); - } - }; -} - function filterObject(object, blacklist) { const copied = Object.assign({}, object); for (const key of blacklist) { diff --git a/react-packager/src/Server/index.js b/react-packager/src/Server/index.js index 23f69495..c267ac69 100644 --- a/react-packager/src/Server/index.js +++ b/react-packager/src/Server/index.js @@ -13,6 +13,7 @@ const AssetServer = require('../AssetServer'); const FileWatcher = require('../node-haste').FileWatcher; const getPlatformExtension = require('../node-haste').getPlatformExtension; const Bundler = require('../Bundler'); +const ProgressBar = require('progress'); const Promise = require('promise'); const SourceMapConsumer = require('source-map').SourceMapConsumer; @@ -158,6 +159,9 @@ const bundleOpts = declareOpts({ type: 'array', default: [], }, + onProgress: { + type: 'function', + }, }); const dependencyOpts = declareOpts({ @@ -192,7 +196,7 @@ const NODE_MODULES = `${path.sep}node_modules${path.sep}`; class Server { constructor(options) { - const opts = validateOpts(options); + const opts = this._opts = validateOpts(options); this._projectRoots = opts.projectRoots; this._bundles = Object.create(null); @@ -511,8 +515,13 @@ class Server { ).done(() => Activity.endEvent(assetEvent)); } + optionsHash(options) { + // onProgress is a function, can't be serialized + return JSON.stringify(Object.assign({}, options, { onProgress: null })); + } + _useCachedOrUpdateOrCreateBundle(options) { - const optionsJson = JSON.stringify(options); + const optionsJson = this.optionsHash(options); const bundleFromScratch = () => { const building = this.buildBundle(options); this._bundles[optionsJson] = building; @@ -647,6 +656,16 @@ class Server { details: req.url, }, ); + + if (process.stdout.isTTY && !this._opts.silent) { + const bar = new ProgressBar('transformed :current/:total (:percent)', { + complete: '=', + incomplete: ' ', + width: 40, + total: 1, + }); + options.onProgress = debouncedTick(bar); + } debug('Getting bundle for request'); const building = this._useCachedOrUpdateOrCreateBundle(options); building.then( @@ -691,7 +710,7 @@ class Server { Activity.endEvent(startReqEventId); } }, - error => this._handleError(res, JSON.stringify(options), error) + error => this._handleError(res, this.optionsHash(options), error) ).catch(error => { process.nextTick(() => { throw error; @@ -867,4 +886,23 @@ function contentsEqual(array, set) { return array.length === set.size && array.every(set.has, set); } +function debouncedTick(progressBar) { + let n = 0; + let start, total; + + return (_, t) => { + total = t; + n += 1; + if (start) { + if (progressBar.curr + n >= total || Date.now() - start > 200) { + progressBar.total = total; + progressBar.tick(n); + start = n = 0; + } + } else { + start = Date.now(); + } + }; +} + module.exports = Server;