From 3df8fdae752ed27618dbde26ec83d6bd4f6e3841 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Wed, 11 Feb 2015 10:24:51 +0700 Subject: [PATCH 1/2] feat(boot): implement chunk loading progress --- config/webpack.js | 11 +++++++---- src/boot/boot.js | 17 ++++++++++++++-- src/boot/index.js | 13 +++++++++--- src/boot/loading-context.js | 37 +++++++++++++++++++++++++++++++++++ src/webpack-progress/index.js | 15 ++++++++++++++ 5 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 src/boot/loading-context.js create mode 100644 src/webpack-progress/index.js diff --git a/config/webpack.js b/config/webpack.js index 096a8dc09..b90047929 100644 --- a/config/webpack.js +++ b/config/webpack.js @@ -1,7 +1,8 @@ -import path from './path' -import NODE_ENV from 'node-env' -import webpack from 'webpack' +import path from './path' +import NODE_ENV from 'node-env' +import webpack from 'webpack' +import ProgressPlugin from '../src/webpack-progress' let config = { context: path('src'), @@ -50,7 +51,9 @@ let config = { ], postLoaders: [], }, - plugins: [], + plugins: [ + new ProgressPlugin(), + ], } if (NODE_ENV === 'test' || process.env.COV === 'true') { diff --git a/src/boot/boot.js b/src/boot/boot.js index 672828c98..e078b0152 100644 --- a/src/boot/boot.js +++ b/src/boot/boot.js @@ -3,11 +3,14 @@ import './boot.scss' import template from './boot.jade' import version from 'val!./version.js' -var boot = document.createElement('div') +let boot = document.createElement('div') boot.id = 'boot' boot.className = 'bemuse-boot' boot.innerHTML = template() -boot.querySelector('.js-progress-bar').classList.add('is-indeterminate') + +let bar = boot.querySelector('.js-progress-bar') +bar.classList.add('is-indeterminate') + boot.querySelector('.js-version').appendChild( document.createTextNode(`v${version}`)) @@ -17,3 +20,13 @@ export function hide() { boot.style.display = 'none' } +export function setProgress(progress) { + if (progress === null) { + bar.classList.add('is-indeterminate') + bar.style.width = '' + } else { + bar.classList.remove('is-indeterminate') + bar.style.width = (progress * 100).toFixed(2) + '%' + } +} + diff --git a/src/boot/index.js b/src/boot/index.js index def368adc..6cf019631 100644 --- a/src/boot/index.js +++ b/src/boot/index.js @@ -9,6 +9,7 @@ import querystring from 'querystring' import loadModule from 'val!./loader.js' +import LoadingContext from './loading-context' import * as boot from './boot' import * as ErrorDialog from './error-dialog' @@ -24,10 +25,16 @@ let mode = data.mode || 'comingSoon' /* istanbul ignore else - we can check that by functional tests */ if (loadModule[mode]) { - loadModule[mode](function(loadedModule) { - boot.hide() - loadedModule.main() + let context = new LoadingContext() + context.use(function() { + loadModule[mode](function(loadedModule) { + boot.hide() + loadedModule.main() + }) }) + context.onprogress = function(loaded, total) { + boot.setProgress(loaded / total) + } } else { console.error('Invalid mode:', mode) } diff --git a/src/boot/loading-context.js b/src/boot/loading-context.js new file mode 100644 index 000000000..e1980d015 --- /dev/null +++ b/src/boot/loading-context.js @@ -0,0 +1,37 @@ + +export class LoadingContext { + load(script, head) { + let src = script.src + let xh = new XMLHttpRequest() + xh.open('GET', src, true) + xh.responseType = 'blob' + xh.onprogress = (e) => { + if (e.total && e.lengthComputable) { + this.onprogress(e.loaded, e.total) + } + } + xh.onload = () => { + let parts = [xh.response, '\n//# sourceURL=' + src] + let type = 'text/javascript' + let url = URL.createObjectURL(new Blob(parts, { type })) + script.src = url + head.appendChild(script) + } + xh.send(null) + } + onprogress() { + } + use(callback) { + let old = window.WebpackLoadingContext + try { + window.WebpackLoadingContext = this + callback() + } finally { + window.WebpackLoadingContext = old + } + } +} + +export default LoadingContext + + diff --git a/src/webpack-progress/index.js b/src/webpack-progress/index.js new file mode 100644 index 000000000..a31f2660b --- /dev/null +++ b/src/webpack-progress/index.js @@ -0,0 +1,15 @@ + +module.exports = function ProgressPlugin() { + return function ProgressPluginInstance() { + this.plugin('compilation', function(compilation) { + compilation.mainTemplate.plugin('require-ensure', function(result) { + result = result.replace('head.appendChild(script);', function() { + return 'window.WebpackLoadingContext ? ' + + 'window.WebpackLoadingContext.load(script, head) : ' + + 'head.appendChild(script);' + }) + return result + }) + }) + } +} From 145467523a3c8d6468f1ae7461134030a1053b15 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Wed, 11 Feb 2015 10:55:43 +0700 Subject: [PATCH 2/2] test(boot): set the progress to null at the start Increasing test coverage --- src/boot/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/boot/index.js b/src/boot/index.js index 6cf019631..01c438def 100644 --- a/src/boot/index.js +++ b/src/boot/index.js @@ -26,15 +26,16 @@ let mode = data.mode || 'comingSoon' /* istanbul ignore else - we can check that by functional tests */ if (loadModule[mode]) { let context = new LoadingContext() + boot.setProgress(null) + context.onprogress = function(loaded, total) { + boot.setProgress(loaded / total) + } context.use(function() { loadModule[mode](function(loadedModule) { boot.hide() loadedModule.main() }) }) - context.onprogress = function(loaded, total) { - boot.setProgress(loaded / total) - } } else { console.error('Invalid mode:', mode) }