diff --git a/docs/README.md b/docs/README.md index bca063d699..e0fe7f28f8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -59,7 +59,7 @@ ### Development -Run `npm start` to see your app at `localhost:3000` +Run `npm start` to see your app at `localhost:3000` and on your local network via BrowserSync, with hot reloading. ### Building & Deploying @@ -119,7 +119,7 @@ get a performance check right in your terminal! #### Browser testing -`npm run start:tunnel` makes your locally-running app globally available on the web +`npm run start:tunnel` makes your app available as with `npm start`, as well as on the web via a temporary URL: great for testing on different devices, client demos, etc! #### Unit testing diff --git a/docs/general/commands.md b/docs/general/commands.md index 67eb9d6517..a1763469b3 100644 --- a/docs/general/commands.md +++ b/docs/general/commands.md @@ -19,7 +19,13 @@ history irreversibly by accident. npm run start ``` -Starts the development server running on `http://localhost:3000` +Starts the development server and makes your application accessible with +[BrowserSync](https://www.browsersync.io/) at `localhost:3001` and on a local IP address (check your startup logs). + +This means that your changes will keep in sync on all your local devices, +even when you update the code or you scroll the page on one of them! + +Make sure to check the BrowserSync dashboard at `localhost:3002`. ## Cleaning @@ -53,9 +59,7 @@ generate container`) npm start ``` -Starts the development server and makes your application accessible at -`localhost:3000`. Tunnels that server with `ngrok`, which means the website -accessible anywhere! Changes in the application code will be hot-reloaded. +Starts the development server and makes your hot-reloadable application accessible everywhere with BrowserSync. ### Production @@ -141,8 +145,8 @@ Watches changes to your application and reruns tests whenever a file changes. ```Shell npm run start:tunnel ``` -Starts the development server and tunnels it with `ngrok`, making the website -available on the entire world. Useful for testing on different devices in different locations! +Starts the development server as usual for development, with an extra tunnels to make the website +available to the entire world. Useful for testing on different devices in different locations! ### Performance testing diff --git a/docs/testing/remote-testing.md b/docs/testing/remote-testing.md index d24406afad..360f7a02ce 100644 --- a/docs/testing/remote-testing.md +++ b/docs/testing/remote-testing.md @@ -4,9 +4,8 @@ npm run start:tunnel ``` -This command will start a server and tunnel it with `ngrok`. You'll get a URL -that looks a bit like this: `http://abcdef.ngrok.com` +Starts the development server similarly to `npm start`, with an extra address XXX.localtunnel.me where XXX is +your app name configured in `package.json`. -This URL will show the version of your application that's in the `build` folder, -and it's accessible from the entire world! This is great for testing on different -devices and from different locations! +This means your app is accessible from the entire world! +This is great for testing on different devices and from different locations. \ No newline at end of file diff --git a/internals/config.js b/internals/config.js index 86d56b6819..afaafc4232 100644 --- a/internals/config.js +++ b/internals/config.js @@ -25,7 +25,6 @@ const ReactBoilerplate = { 'compression', 'cross-env', 'express', - 'ip', 'minimist', 'sanitize.css', ], diff --git a/internals/scripts/browserSync.js b/internals/scripts/browserSync.js new file mode 100644 index 0000000000..3369bbb450 --- /dev/null +++ b/internals/scripts/browserSync.js @@ -0,0 +1,30 @@ +const browserSync = require('browser-sync'); +const path = require('path'); +const pkg = require(path.resolve(process.cwd(), 'package.json')); + +const logger = require('../../server/logger'); + +module.exports = function startBrowserSync(port, middleware, cb) { + const bsConfig = { + proxy: { + target: `localhost:${port}`, + middleware, + }, + + // no need to watch '*.js' here, webpack will take care of it for us, + // including full page reloads if HMR won't work + files: ['build/content/**/*.*'], + }; + if (process.env.ENABLE_TUNNEL) { + bsConfig.tunnel = pkg.name.replace(/\W/g,''); + } + const bs = browserSync.create(); + + bs.init(bsConfig, (err, bs) => { + if (err) return cb(err); + logger.browserSyncStarted(); + + // return the BrowserSync URLs, in case someone cares + cb(null, bs.getOption("urls")); + }); +}; diff --git a/package.json b/package.json index a68ada7e34..f03700a9fb 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,6 @@ "compression", "cross-env", "express", - "ip", "minimist", "sanitize.css" ], @@ -198,6 +197,7 @@ }, "dependencies": { "babel-polyfill": "6.13.0", + "browser-sync": "^2.16.0", "chalk": "1.1.3", "cross-env": "2.0.1", "compression": "1.6.2", @@ -206,7 +206,6 @@ "immutable": "3.8.1", "intl": "1.2.4", "invariant": "2.2.1", - "ip": "1.1.3", "lodash": "4.15.0", "minimist": "1.2.0", "react": "15.3.1", diff --git a/server/index.js b/server/index.js index f37711526b..e24f160871 100644 --- a/server/index.js +++ b/server/index.js @@ -2,11 +2,10 @@ const express = require('express'); const logger = require('./logger'); +const browserSync = require('../internals/scripts/browserSync'); const argv = require('minimist')(process.argv.slice(2)); const setup = require('./middlewares/frontendMiddleware'); -const isDev = process.env.NODE_ENV !== 'production'; -const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false; const resolve = require('path').resolve; const app = express(); @@ -14,7 +13,7 @@ const app = express(); // app.use('/api', myApi); // In production we need to pass these values in instead of relying on webpack -setup(app, { +const middleware = setup(app, { outputPath: resolve(process.cwd(), 'build'), publicPath: '/', }); @@ -28,16 +27,15 @@ app.listen(port, (err) => { return logger.error(err.message); } - // Connect to ngrok in dev mode - if (ngrok) { - ngrok.connect(port, (innerErr, url) => { + // Configure browserSync in dev mode + if (process.env.NODE_ENV !== 'production') { + browserSync(port, middleware, (innerErr) => { if (innerErr) { return logger.error(innerErr); } - - logger.appStarted(port, url); + logger.appStarted(); }); } else { - logger.appStarted(port); + logger.appStarted(); } }); diff --git a/server/logger.js b/server/logger.js index 0fe2ec4fb1..1bce390f19 100644 --- a/server/logger.js +++ b/server/logger.js @@ -1,9 +1,6 @@ /* eslint-disable no-console */ const chalk = require('chalk'); -const ip = require('ip'); - -const divider = chalk.gray('\n-----------------------------------'); /** * Logger middleware, you can customize it to make messages more personal @@ -16,21 +13,13 @@ const logger = { }, // Called when express.js app starts on given port w/o errors - appStarted: (port, tunnelStarted) => { + appStarted: () => { console.log(`Server started ${chalk.green('✓')}`); + }, - // If the tunnel started, log that and the URL it's available at - if (tunnelStarted) { - console.log(`Tunnel initialised ${chalk.green('✓')}`); - } - - console.log(` -${chalk.bold('Access URLs:')}${divider} -Localhost: ${chalk.magenta(`http://localhost:${port}`)} - LAN: ${chalk.magenta(`http://${ip.address()}:${port}`) + -(tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '')}${divider} -${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)} - `); + // Called when BrowserSync starts w/o errors + browserSyncStarted: () => { + console.log(`BrowserSync started ${chalk.green('✓')}`); }, }; diff --git a/server/middlewares/frontendMiddleware.js b/server/middlewares/frontendMiddleware.js index 9331e12508..cb4746928b 100644 --- a/server/middlewares/frontendMiddleware.js +++ b/server/middlewares/frontendMiddleware.js @@ -18,7 +18,9 @@ const addDevMiddlewares = (app, webpackConfig) => { }); app.use(middleware); - app.use(webpackHotMiddleware(compiler)); + + const hotMiddleware = webpackHotMiddleware(compiler); + app.use(hotMiddleware); // Since webpackDevMiddleware uses memory-fs internally to store build // artifacts, we use it instead @@ -40,6 +42,8 @@ const addDevMiddlewares = (app, webpackConfig) => { } }); }); + + return [middleware, hotMiddleware]; }; // Production middlewares @@ -54,6 +58,8 @@ const addProdMiddlewares = (app, options) => { app.use(publicPath, express.static(outputPath)); app.get('*', (req, res) => res.sendFile(path.resolve(outputPath, 'index.html'))); + + return []; }; /** @@ -63,11 +69,8 @@ module.exports = (app, options) => { const isProd = process.env.NODE_ENV === 'production'; if (isProd) { - addProdMiddlewares(app, options); - } else { - const webpackConfig = require('../../internals/webpack/webpack.dev.babel'); - addDevMiddlewares(app, webpackConfig); + return addProdMiddlewares(app, options); } - - return app; + const webpackConfig = require('../../internals/webpack/webpack.dev.babel'); + return addDevMiddlewares(app, webpackConfig); };