diff --git a/docs/front-end-build-process.md b/docs/front-end-build-process.md index ec21e6d284..6ae7c50b54 100644 --- a/docs/front-end-build-process.md +++ b/docs/front-end-build-process.md @@ -43,13 +43,15 @@ add it to the appropriate place under `static/`. static/ts; CSS lives under `static/styles`. Portico JavaScript ("portico" means for logged-out pages) lives under `static/js/portico`. -After you add a new JavaScript file, it needs to be specified in the -`entries` dictionary defined in `tools/webpack.assets.json` to be included -in the concatenated file; this will magically ensure it is available -both in development and production. CSS should be added to -the `STYLESHEETS` section of `PIPELINE` in `zproject/settings.py`. A -few notes on doing this: +After you add a new JavaScript file, it needs to be imported by +another file or specified in the `entries` dictionary defined in +`tools/webpack.assets.json` to be included in the concatenated file; +this will magically ensure it is available both in development and +production. CSS should be added to the `STYLESHEETS` section of +`PIPELINE` in `zproject/settings.py`. A few notes on doing this: +* For new files you should generally import it from another file rather + than adding it to `tools/webpack.assets.json` * If you plan to only use the JS/CSS within the app proper, and not on the login page or other standalone pages, put it in the `app` bundle. @@ -58,6 +60,8 @@ few notes on doing this: it its own bundle. To load a bundle in the relevant Jinja2 template for that page, use `render_bundle` and `stylesheet` for JS and CSS, respectively. +* If you modify `tools/webpack.assets.json` you will need to restart + the server. If you want to test minified files in development, look for the `PIPELINE_ENABLED =` line in `zproject/settings.py` and set it to `True` @@ -106,6 +110,21 @@ All JavaScript we provide will eventually be migrated to Typescript, which will make refactoring the frontend code easier and allow static analyzers to reason about our code more easily. -Declare entry points in webpack.assets.json. Any modules you add will -need to be required or imported from this file (or one of its -dependencies) in order to be included in the script bundle. +Declare entry points in `webpack.assets.json`. Any modules you add +will need to be imported from this file (or one of its dependencies) +in order to be included in the script bundle. + +### Hot Reloading + +Webpack support hot reloading. To enable it you will need to add + +``` +// This reloads the module in development rather than refreshing the page +if (module.hot) { + module.hot.accept(); +} + +``` + +To the entry point of any JavaScript file you want to hot reload +rather than refeshing the page on a change. diff --git a/frontend_tests/zjsunit/index.js b/frontend_tests/zjsunit/index.js index c1dffb7fa2..7bff0a3398 100644 --- a/frontend_tests/zjsunit/index.js +++ b/frontend_tests/zjsunit/index.js @@ -47,6 +47,13 @@ global.stub_i18n = require('./i18n.js'); var noop = function () {}; +// Set up fake module.hot +// eslint-disable-next-line no-native-reassign +module = require('module'); +module.prototype.hot = { + accept: noop, +}; + output.start_writing(); files.forEach(function (file) { diff --git a/static/js/common.js b/static/js/common.js index c062a5d26a..46d2cd9267 100644 --- a/static/js/common.js +++ b/static/js/common.js @@ -1,3 +1,8 @@ +// This reloads the module in development rather than refreshing the page +if (module.hot) { + module.hot.accept(); +} + var common = (function () { var exports = {}; diff --git a/tools/run-dev.py b/tools/run-dev.py index 603ead1aff..cb61ebb0d5 100755 --- a/tools/run-dev.py +++ b/tools/run-dev.py @@ -187,6 +187,10 @@ else: webpack_cmd = ['./tools/webpack', '--watch', '--port', str(webpack_port)] if options.minify: webpack_cmd.append('--minify') + if options.interface: + webpack_cmd += ["--host", options.interface] + else: + webpack_cmd += ["--host", "0.0.0.0"] cmds.append(webpack_cmd) for cmd in cmds: subprocess.Popen(cmd) @@ -380,7 +384,6 @@ class Application(web.Application): (r"/json/events.*", TornadoHandler), (r"/api/v1/events.*", TornadoHandler), (r"/webpack.*", WebPackHandler), - (r"/sockjs-node.*", WebPackHandler), (r"/sockjs.*", TornadoHandler), (r"/.*", DjangoHandler) ] diff --git a/tools/webpack b/tools/webpack index 4a4302b6b5..b0b0bfead3 100755 --- a/tools/webpack +++ b/tools/webpack @@ -25,11 +25,11 @@ def run(): subprocess.check_call(['node', 'node_modules/.bin/webpack'] + ['--config', 'tools/webpack.production.config.js', '-p']) -def run_watch(port, minify): - # type: (str, bool) -> None +def run_watch(host, port, minify): + # type: (str, str, bool) -> None """watches and rebuilds on changes, serving files from memory via webpack-dev-server""" webpack_args = ['node', 'node_modules/.bin/webpack-dev-server'] - webpack_args += ['--config', 'tools/webpack.dev.config.js', '--watch-poll', '--port', port] + webpack_args += ['--config', 'tools/webpack.dev.config.js', '--watch-poll', '--port', port, "--host", host] if minify: webpack_args.append('--optimize-minimize') subprocess.Popen(webpack_args) @@ -63,6 +63,9 @@ parser.add_argument('--test', parser.add_argument('--watch', action='store_true', dest='watch', default=False, help='watch for changes to source files (for development)') +parser.add_argument('--host', + action='store', dest='host', + default='127.0.0.1', help='set the host for the webpack server to run on') parser.add_argument('--port', action='store', dest='port', default='9994', help='set the port for the webpack server to run on') @@ -74,6 +77,6 @@ args = parser.parse_args() if args.test: run_test() elif args.watch: - run_watch(args.port, args.minify) + run_watch(args.host, args.port, args.minify) else: run() diff --git a/tools/webpack.dev.config.js b/tools/webpack.dev.config.js index 9c98c76b78..7bbd0af9d6 100644 --- a/tools/webpack.dev.config.js +++ b/tools/webpack.dev.config.js @@ -1,17 +1,27 @@ var config = require('./webpack.config.js'); var BundleTracker = require('webpack-bundle-tracker'); +var webpack = require('webpack'); // Built webpack dev asset reloader -config.entry.common.unshift('webpack-dev-server/client?/sockjs-node'); +config.entry.common.unshift('webpack/hot/dev-server'); +// Use 0.0.0.0 so that we can set a port but still use the host +// the browser is connected to. +config.entry.common.unshift('webpack-dev-server/client?http://0.0.0.0:9994'); + // Out JS debugging tools config.entry.common.push('./static/js/debug.js'); config.devtool = 'eval'; config.output.publicPath = '/webpack/'; config.plugins.push(new BundleTracker({filename: 'static/webpack-bundles/webpack-stats-dev.json'})); +// Hot Reload of code in development +config.plugins.push(new webpack.HotModuleReplacementPlugin()); +// Better logging from console for hot reload +config.plugins.push(new webpack.NamedModulesPlugin()); config.devServer = { - port: 9994, + clientLogLevel: "warning", + hot: true, inline: false, stats: "errors-only", watchOptions: {