diff --git a/README.md b/README.md index 3b886020c9..8dfa1e6a84 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,13 @@ This is a [Chrome App](https://developer.chrome.com/apps/about_apps) that acts a 1. Run `npm start` (This will bundle the application with webpack and watch for changes. When it stops printing output you can continue to the next step.) 1. Open Chrome. Go to chrome://extensions and turn on Developer mode (checkbox on the top line). 1. Click "Load Unpacked Extension". -1. Choose the directory where you cloned the repository and click OK. +1. Choose the directory where you cloned the repository and click OK.[1](#f1) 1. To run it, you can choose "Launch" from the chrome://extensions page. You can also run it from the Chrome App Launcher, which Chrome may install for you whether you want it or not. 1. To open the JavaScript console/Chrome Dev Tools, click on the `index.html` link in the section of chrome://extensions devoted to the uploader. (Note: this link will only appear after you've launched the uploader.) -1. If you're developing, you may find that the only way it runs properly is to hit the "Reload" link in chrome://extensions after each change to the source. You will definitely need to reload any time you change the manifest. +1. React components and CSS will hot load after changes (this can be confirmed by watching the JavaScript console), but changes to device drivers and other code outside of the React components will not - use 'Reload' from chrome://extensions to reload after such changes. If the compilation/hot reload of a component fails for any reason (e.g. from a syntax error) you may need to reinitialize the hot loader by reloading the extension. You will definitely need to reload any time you change the manifest. +--- +1 You may see a warning from Chrome concerning the inclusion of a key file. (`This extension includes the key file '/node_modules/webpack-dev-server/node_modules/sockjs-client/node_modules/eventsource/test/key.pem`) This is due to the loading of all the `node_modules` and their various internal testing utilities. This isn't a security issue, nor is the associated key used or referenced anywhere in the running code and can safely be ignored. [↩](#a1) ## Config diff --git a/lib/redux/actions/async.js b/lib/redux/actions/async.js index 706cdb3078..d6ff83f273 100644 --- a/lib/redux/actions/async.js +++ b/lib/redux/actions/async.js @@ -41,6 +41,12 @@ let daysForCareLink = null; export function doAppInit(opts, servicesToInit) { return (dispatch, getState) => { + // when we are developing with hot reload, we get into trouble if we try to initialize the app + // when it's already been initialized, so we check the working.initializingApp flag first + if (getState().working.initializingApp === false) { + console.log('App already initialized! Skipping initialization.'); + return; + } services = servicesToInit; versionInfo.semver = opts.version; versionInfo.name = opts.namedVersion; diff --git a/package.json b/package.json index 3cf3d9cd7f..5259a87cab 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "browser-tests": "./node_modules/karma/bin/karma start --browsers PhantomJS,Chrome", "node-tests": "mocha test/node/ && mocha test/node/**/*.js", "karma-watch": "./node_modules/karma/bin/karma start --no-single-run", - "start": "bash ./scripts/config.sh && webpack -d --progress --colors --watch", + "start": "bash ./scripts/config.sh && webpack-dev-server --hot --inline --progress --colors", "build": "bash ./scripts/build.sh", "lint": "./node_modules/.bin/eslint lib test" }, @@ -62,6 +62,7 @@ "phantomjs-prebuilt": "2.1.3", "proxyquire": "1.7.4", "react-addons-test-utils": "0.14.7", + "react-hot-loader": "1.3.0", "redux-devtools": "3.0.2", "redux-devtools-dock-monitor": "1.0.1", "redux-devtools-log-monitor": "1.0.2", @@ -69,6 +70,8 @@ "redux-mock-store": "0.0.6", "salinity": "0.0.8", "style-loader": "0.13.0", - "webpack": "1.12.12" + "webpack": "1.12.12", + "webpack-dev-server": "1.14.1", + "write-file-webpack-plugin": "3.1.8" } } diff --git a/test/browser/redux/actions/async.test.js b/test/browser/redux/actions/async.test.js index 3d928fd44c..59dfe16a12 100644 --- a/test/browser/redux/actions/async.test.js +++ b/test/browser/redux/actions/async.test.js @@ -55,6 +55,18 @@ describe('Asynchronous Actions', () => { asyncActions.__ResetDependency__('services'); }); + describe('doAppInit [hot reload, app already initialized]', () => { + it('should dispatch no actions!', (done) => { + const expectedActions = []; + const store = mockStore({working: {initializingApp: false}}, expectedActions, done.fail); + store.dispatch(asyncActions.doAppInit({}, {})); + // somewhat hacky solution to testing for no actions + // discussed here: https://github.com/arnaudbenard/redux-mock-store/issues/17 + // happy to live with the hack since this is only for hot-reloading anyway + setTimeout(() => done(), 1000); + }); + }); + describe('doAppInit [no session token in local storage]', () => { it('should dispatch SET_VERSION, INIT_APP_REQUEST, SET_OS, HIDE_UNAVAILABLE_DEVICES, SET_FORGOT_PASSWORD_URL, SET_SIGNUP_URL, SET_PAGE, INIT_APP_SUCCESS, VERSION_CHECK_REQUEST, VERSION_CHECK_SUCCESS actions', (done) => { const config = { @@ -126,7 +138,7 @@ describe('Asynchronous Actions', () => { asyncActions.__Rewire__('versionInfo', { semver: config.version }); - const store = mockStore({}, expectedActions, done); + const store = mockStore({working: {initializingApp: true}}, expectedActions, done); store.dispatch(asyncActions.doAppInit(config, servicesToInit)); }); }); @@ -223,7 +235,8 @@ describe('Asynchronous Actions', () => { semver: config.version }); const state = { - uploadTargetUser: pwd.user.userid + uploadTargetUser: pwd.user.userid, + working: {initializingApp: true} }; const store = mockStore(state, expectedActions, done); store.dispatch(asyncActions.doAppInit(config, servicesToInit)); @@ -277,7 +290,7 @@ describe('Asynchronous Actions', () => { asyncActions.__Rewire__('versionInfo', { semver: config.version }); - const store = mockStore({}, expectedActions, done); + const store = mockStore({working: {initializingApp: true}}, expectedActions, done); store.dispatch(asyncActions.doAppInit(config, servicesToInit)); }); }); diff --git a/webpack.config.js b/webpack.config.js index 37e2f65813..ba87ce2bd6 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ var path = require('path'); var _ = require('lodash'); var webpack = require('webpack'); +var WriteFilePlugin = require('write-file-webpack-plugin'); var definePlugin = new webpack.DefinePlugin({ // this first as advised to get the correct production build of redux @@ -30,20 +31,27 @@ if ((!process.env.API_URL || !process.env.UPLOAD_URL || !process.env.BLIP_URL)) var config = { entry: './entry.js', + devtool: '#cheap-module-source-map', + devServer: { + outputPath: path.join(__dirname, '/build'), + publicPath: '/build/' + }, output: { path: path.join(__dirname, '/build'), - filename: 'bundle.js' + filename: 'bundle.js', + publicPath: '/build/', }, module: { loaders: [ - { test: /\.js$/, exclude: /(node_modules)/, loader: 'babel-loader' }, - { test: /\.jsx$/, exclude: /(node_modules)/, loader: 'babel-loader' }, + { test: /\.js$/, exclude: /(node_modules)/, loaders: ['react-hot', 'babel-loader'] }, + { test: /\.jsx$/, exclude: /(node_modules)/, loaders: ['react-hot', 'babel-loader'] }, { test: /\.less$/, loader: 'style!css!less' }, { test: /\.json$/, loader: 'json' } ] }, plugins: [ - definePlugin + definePlugin, + new WriteFilePlugin() ], // to fix the 'broken by design' issue with npm link-ing modules resolve: { fallback: path.join(__dirname, 'node_modules') },