From 5d4c1366fff9fd132a0186f8509fd0fd93bcd0a5 Mon Sep 17 00:00:00 2001 From: PC Drew Date: Tue, 24 Jan 2017 16:38:56 -0700 Subject: [PATCH 1/9] Add compileOnStartup option. --- .../react-server-cli/src/commands/start.js | 63 ++++++++++++------- .../react-server-cli/src/defaultOptions.js | 1 + packages/react-server-cli/src/mergeOptions.js | 2 +- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/packages/react-server-cli/src/commands/start.js b/packages/react-server-cli/src/commands/start.js index c5af4cf47..1af7151a6 100644 --- a/packages/react-server-cli/src/commands/start.js +++ b/packages/react-server-cli/src/commands/start.js @@ -127,7 +127,13 @@ const startHtmlServer = (serverRoutes, port, bindIp, httpsOptions, customMiddlew // files and start up a web server at http://host:port/ that serves the // static compiled JavaScript. returns an object with two properties, started and stop; // see the default function doc for explanation. -const startStaticJsServer = (compiler, port, bindIp, longTermCaching, httpsOptions) => { +const startStaticJsServer = (compiler, options) => { + const { + jsPort, + bindIp, + longTermCaching, + httpsOptions, + } = options; const server = express(); const httpServer = httpsOptions ? https.createServer(httpsOptions, server) : http.createServer(server); return { @@ -152,13 +158,13 @@ const startStaticJsServer = (compiler, port, bindIp, longTermCaching, httpsOptio console.error(e); reject(e) }); - httpServer.listen(port, bindIp, (e) => { + httpServer.listen(jsPort, bindIp, (e) => { if (e) { reject(e); return; } - logger.info(`Started static JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${port}`); + logger.info(`Started static JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${jsPort}`); resolve(); }); }); @@ -170,7 +176,13 @@ const startStaticJsServer = (compiler, port, bindIp, longTermCaching, httpsOptio // for hot reloading at http://localhost:port/. note that the webpack compiler // must have been configured correctly for hot reloading. returns an object with // two properties, started and stop; see the default function doc for explanation. -const startHotLoadJsServer = (compiler, port, bindIp, longTermCaching, httpsOptions) => { +const startHotLoadJsServer = (compiler, options) => { + const { + jsPort, + bindIp, + httpsOptions, + } = options; + logger.info("Starting hot reload JavaScript server..."); const compiledPromise = new Promise((resolve) => compiler.plugin("done", () => resolve())); const jsServer = new WebpackDevServer(compiler, { @@ -183,7 +195,7 @@ const startHotLoadJsServer = (compiler, port, bindIp, longTermCaching, httpsOpti ca: httpsOptions ? httpsOptions.ca : undefined, }); const serverStartedPromise = new Promise((resolve, reject) => { - jsServer.listen(port, bindIp, (e) => { + jsServer.listen(jsPort, bindIp, (e) => { if (e) { reject(e); return; @@ -194,7 +206,7 @@ const startHotLoadJsServer = (compiler, port, bindIp, longTermCaching, httpsOpti return { stop: serverToStopPromise(jsServer), started: Promise.all([compiledPromise, serverStartedPromise]) - .then(() => logger.info(`Started hot reload JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${port}`)), + .then(() => logger.info(`Started hot reload JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${jsPort}`)), }; }; @@ -202,21 +214,31 @@ const startHotLoadJsServer = (compiler, port, bindIp, longTermCaching, httpsOpti // names for the server routes file) but don't really want to actually up a JavaScript // server. Supports the same signature as startStaticJsServer and startHotLoadJsServer, // returning the same {stop, started} object. -const startDummyJsServer = (compiler /*, port, longTermCaching, httpsOptions*/) => { +const startDummyJsServer = (compiler, options) => { + const { + compileOnStartup, + } = options; + return { stop: () => Promise.resolve(), - started: new Promise((resolve, reject) => compiler.run((err, stats)=> { - // even though we aren't using the compiled code (we're pointing at jsUrl), - // we still need to run the compilation to get the chunk file names. - try { - handleCompilationErrors(err, stats); - } catch (e) { - logger.emergency("Failed to compile the local code.", e.stack); - reject(e); - return; + started: new Promise((resolve, reject) => { + if (compileOnStartup) { + compiler.run((err, stats) => { + // even though we aren't using the compiled code (we're pointing at jsUrl), + // we still need to run the compilation to get the chunk file names. + try { + handleCompilationErrors(err, stats); + } catch (e) { + logger.emergency("Failed to compile the local code.", e.stack); + reject(e); + return; + } + resolve(); + }) + } else { + resolve(); } - resolve(); - })), + }), }; }; @@ -232,11 +254,9 @@ export default function start(options){ const { port, bindIp, - jsPort, hot, jsUrl, httpsOptions, - longTermCaching, customMiddlewarePath, } = options; @@ -255,7 +275,8 @@ export default function start(options){ logger.notice("Starting servers..."); - const jsServer = startJsServer(compiler, jsPort, bindIp, longTermCaching, httpsOptions); + //const jsServer = startJsServer(compiler, jsPort, bindIp, longTermCaching, httpsOptions); + const jsServer = startJsServer(compiler, options); const htmlServerPromise = serverRoutes.then(serverRoutesFile => startHtmlServer(serverRoutesFile, port, bindIp, httpsOptions, customMiddlewarePath)); return { diff --git a/packages/react-server-cli/src/defaultOptions.js b/packages/react-server-cli/src/defaultOptions.js index d6b6f39f1..d2d78ad9a 100644 --- a/packages/react-server-cli/src/defaultOptions.js +++ b/packages/react-server-cli/src/defaultOptions.js @@ -19,6 +19,7 @@ export default { hot: true, minify: false, compileOnly: false, + compileOnStartup: true, logLevel: "debug", timingLogLevel: "fast", gaugeLogLevel: "ok", diff --git a/packages/react-server-cli/src/mergeOptions.js b/packages/react-server-cli/src/mergeOptions.js index 470fc3c53..130cb3ab3 100644 --- a/packages/react-server-cli/src/mergeOptions.js +++ b/packages/react-server-cli/src/mergeOptions.js @@ -1,4 +1,4 @@ -// takes in an arrray of options objects for `startServer.js`, and returns a merged +// takes in an array of options objects for `startServer.js`, and returns a merged // version of them. it essentially acts like Object.assign(), so options objects // later in the argument list override the properties of ones earlier in the list. // like Object.assign, the merging is done on a property-by-property basis; From 589c6a0ecc5f25669abe6beb8e3d1008569e3927 Mon Sep 17 00:00:00 2001 From: PC Drew Date: Tue, 24 Jan 2017 20:21:24 -0700 Subject: [PATCH 2/9] Add a compileOnStartup option to allow people to precompile code and ensure the server uses that code without recompiling it each time the server starts. --- .../react-server-cli/src/commands/start.js | 37 ++++++++++++++----- packages/react-server-cli/src/parseCliArgs.js | 5 +++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/packages/react-server-cli/src/commands/start.js b/packages/react-server-cli/src/commands/start.js index 1af7151a6..293bc1121 100644 --- a/packages/react-server-cli/src/commands/start.js +++ b/packages/react-server-cli/src/commands/start.js @@ -13,6 +13,7 @@ import setupLogging from "../setupLogging"; import logProductionWarnings from "../logProductionWarnings"; import expressState from 'express-state'; import cookieParser from 'cookie-parser'; +import fs from 'fs'; const logger = reactServer.logging.getLogger(__LOGGER__); @@ -214,7 +215,7 @@ const startHotLoadJsServer = (compiler, options) => { // names for the server routes file) but don't really want to actually up a JavaScript // server. Supports the same signature as startStaticJsServer and startHotLoadJsServer, // returning the same {stop, started} object. -const startDummyJsServer = (compiler, options) => { +const startDummyJsServer = (compiler, options, routesFile) => { const { compileOnStartup, } = options; @@ -222,7 +223,16 @@ const startDummyJsServer = (compiler, options) => { return { stop: () => Promise.resolve(), started: new Promise((resolve, reject) => { - if (compileOnStartup) { + if (compileOnStartup === false || compileOnStartup === "false") { + fs.access(routesFile, fs.constants.R_OK, (err) => { + if (err) { + logger.emergency("You must first compile your application when compileOnStartup is set to false."); + reject(err.message); + return; + } + resolve(); + }); + } else { compiler.run((err, stats) => { // even though we aren't using the compiled code (we're pointing at jsUrl), // we still need to run the compilation to get the chunk file names. @@ -235,8 +245,6 @@ const startDummyJsServer = (compiler, options) => { } resolve(); }) - } else { - resolve(); } }), }; @@ -258,16 +266,26 @@ export default function start(options){ jsUrl, httpsOptions, customMiddlewarePath, + compileOnStartup, } = options; + const routesFileLocation = path.join(process.cwd(), '__clientTemp/routes_server.js'); const {serverRoutes, compiler} = compileClient(options); const startServers = () => { // if jsUrl is set, we need to run the compiler, but we don't want to start a JS // server. - let startJsServer = startDummyJsServer; - - if (!jsUrl) { + let startJsServer; + let serverRoutesPromise = serverRoutes; + + if (jsUrl) { + startJsServer = startDummyJsServer; + if (compileOnStartup === false || compileOnStartup === "false") { + // We need to replace the promise returned by the compiler with an already-resolved promise with the path + // of the compiled routes file. + serverRoutesPromise = Promise.resolve(routesFileLocation); + } + } else { // if jsUrl is not set, we need to start up a JS server, either hot load // or static. startJsServer = hot ? startHotLoadJsServer : startStaticJsServer; @@ -275,9 +293,8 @@ export default function start(options){ logger.notice("Starting servers..."); - //const jsServer = startJsServer(compiler, jsPort, bindIp, longTermCaching, httpsOptions); - const jsServer = startJsServer(compiler, options); - const htmlServerPromise = serverRoutes.then(serverRoutesFile => startHtmlServer(serverRoutesFile, port, bindIp, httpsOptions, customMiddlewarePath)); + const jsServer = startJsServer(compiler, options, routesFileLocation); + const htmlServerPromise = serverRoutesPromise.then(serverRoutesFile => startHtmlServer(serverRoutesFile, port, bindIp, httpsOptions, customMiddlewarePath)); return { stop: () => Promise.all([jsServer.stop(), htmlServerPromise.then(server => server.stop())]), diff --git a/packages/react-server-cli/src/parseCliArgs.js b/packages/react-server-cli/src/parseCliArgs.js index a4860e05c..4f4703672 100644 --- a/packages/react-server-cli/src/parseCliArgs.js +++ b/packages/react-server-cli/src/parseCliArgs.js @@ -107,6 +107,11 @@ export default (args = process.argv) => { default: false, type: "boolean", }) + .option("compile-on-startup", { + describe: "Enable/disable compiling on startup. Recommended for use in production when files have been pre-compiled.", + default: true, + type: "boolean", + }) .version(function() { return require('../package').version; }) From e5f49158e77b8f4feb017cad91539d97e9031131 Mon Sep 17 00:00:00 2001 From: PC Drew Date: Sun, 5 Feb 2017 11:13:20 -0700 Subject: [PATCH 3/9] Refactor the compileOnStartup methodology. Now it's more seamless because it mocks the compile.run function so that it applies more uniformly to each of the JS servers. Also, fixed an issue where the run command wasn't returning a promise when starting the server, instead it was returning an object with the started promise inside it. --- .../react-server-cli/src/commands/start.js | 116 +++++++++--------- .../src/handleCompilationErrors.js | 4 +- packages/react-server-cli/src/parseCliArgs.js | 1 - packages/react-server-cli/src/run.js | 3 +- 4 files changed, 59 insertions(+), 65 deletions(-) diff --git a/packages/react-server-cli/src/commands/start.js b/packages/react-server-cli/src/commands/start.js index 293bc1121..01ea617c3 100644 --- a/packages/react-server-cli/src/commands/start.js +++ b/packages/react-server-cli/src/commands/start.js @@ -128,13 +128,7 @@ const startHtmlServer = (serverRoutes, port, bindIp, httpsOptions, customMiddlew // files and start up a web server at http://host:port/ that serves the // static compiled JavaScript. returns an object with two properties, started and stop; // see the default function doc for explanation. -const startStaticJsServer = (compiler, options) => { - const { - jsPort, - bindIp, - longTermCaching, - httpsOptions, - } = options; +const startStaticJsServer = (compiler, port, bindIp, longTermCaching, httpsOptions) => { const server = express(); const httpServer = httpsOptions ? https.createServer(httpsOptions, server) : http.createServer(server); return { @@ -147,7 +141,10 @@ const startStaticJsServer = (compiler, options) => { return; } - logger.debug("Successfully compiled static JavaScript."); + if (stats) { + logger.debug("Successfully compiled static JavaScript."); + } + // TODO: make this parameterized based on what is returned from compileClient server.use('/', compression(), express.static(`__clientTemp/build`, { maxage: longTermCaching ? '365d' : '0s', @@ -159,13 +156,13 @@ const startStaticJsServer = (compiler, options) => { console.error(e); reject(e) }); - httpServer.listen(jsPort, bindIp, (e) => { + httpServer.listen(port, bindIp, (e) => { if (e) { reject(e); return; } - logger.info(`Started static JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${jsPort}`); + logger.info(`Started static JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${port}`); resolve(); }); }); @@ -177,13 +174,7 @@ const startStaticJsServer = (compiler, options) => { // for hot reloading at http://localhost:port/. note that the webpack compiler // must have been configured correctly for hot reloading. returns an object with // two properties, started and stop; see the default function doc for explanation. -const startHotLoadJsServer = (compiler, options) => { - const { - jsPort, - bindIp, - httpsOptions, - } = options; - +const startHotLoadJsServer = (compiler, port, bindIp, longTermCaching, httpsOptions) => { logger.info("Starting hot reload JavaScript server..."); const compiledPromise = new Promise((resolve) => compiler.plugin("done", () => resolve())); const jsServer = new WebpackDevServer(compiler, { @@ -196,7 +187,7 @@ const startHotLoadJsServer = (compiler, options) => { ca: httpsOptions ? httpsOptions.ca : undefined, }); const serverStartedPromise = new Promise((resolve, reject) => { - jsServer.listen(jsPort, bindIp, (e) => { + jsServer.listen(port, bindIp, (e) => { if (e) { reject(e); return; @@ -207,7 +198,7 @@ const startHotLoadJsServer = (compiler, options) => { return { stop: serverToStopPromise(jsServer), started: Promise.all([compiledPromise, serverStartedPromise]) - .then(() => logger.info(`Started hot reload JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${jsPort}`)), + .then(() => logger.info(`Started hot reload JavaScript server over ${httpsOptions ? "HTTPS" : "HTTP"} on ${bindIp}:${port}`)), }; }; @@ -215,38 +206,21 @@ const startHotLoadJsServer = (compiler, options) => { // names for the server routes file) but don't really want to actually up a JavaScript // server. Supports the same signature as startStaticJsServer and startHotLoadJsServer, // returning the same {stop, started} object. -const startDummyJsServer = (compiler, options, routesFile) => { - const { - compileOnStartup, - } = options; - +const startDummyJsServer = (compiler /*, port, longTermCaching, httpsOptions*/) => { return { stop: () => Promise.resolve(), - started: new Promise((resolve, reject) => { - if (compileOnStartup === false || compileOnStartup === "false") { - fs.access(routesFile, fs.constants.R_OK, (err) => { - if (err) { - logger.emergency("You must first compile your application when compileOnStartup is set to false."); - reject(err.message); - return; - } - resolve(); - }); - } else { - compiler.run((err, stats) => { - // even though we aren't using the compiled code (we're pointing at jsUrl), - // we still need to run the compilation to get the chunk file names. - try { - handleCompilationErrors(err, stats); - } catch (e) { - logger.emergency("Failed to compile the local code.", e.stack); - reject(e); - return; - } - resolve(); - }) + started: new Promise((resolve, reject) => compiler.run((err, stats)=> { + // even though we aren't using the compiled code (we're pointing at jsUrl), + // we still need to run the compilation to get the chunk file names. + try { + handleCompilationErrors(err, stats); + } catch (e) { + logger.emergency("Failed to compile the local code.", e.stack); + reject(e); + return; } - }), + resolve(); + })), }; }; @@ -262,30 +236,48 @@ export default function start(options){ const { port, bindIp, + jsPort, hot, jsUrl, httpsOptions, + longTermCaching, customMiddlewarePath, compileOnStartup, } = options; const routesFileLocation = path.join(process.cwd(), '__clientTemp/routes_server.js'); - const {serverRoutes, compiler} = compileClient(options); + let serverRoutes, + compiler; const startServers = () => { // if jsUrl is set, we need to run the compiler, but we don't want to start a JS // server. - let startJsServer; - let serverRoutesPromise = serverRoutes; - - if (jsUrl) { - startJsServer = startDummyJsServer; - if (compileOnStartup === false || compileOnStartup === "false") { - // We need to replace the promise returned by the compiler with an already-resolved promise with the path - // of the compiled routes file. - serverRoutesPromise = Promise.resolve(routesFileLocation); - } + let startJsServer = startDummyJsServer; + + if ((hot === false || hot === "false") && (compileOnStartup === false || compileOnStartup === "false")) { + serverRoutes = new Promise((resolve, reject) => { + fs.access(routesFileLocation, fs.constants.R_OK, (err) => { + if (err) { + reject("You must manually compile your application when compileOnStartup is set to false."); + } else { + // We need to replace the promise returned by the compiler with an already-resolved promise with the path + // of the compiled routes file. + resolve(routesFileLocation); + } + }); + }); + + // mock the compiler object used by the various JS servers so that the compiler.run function always succeeds. + // This will allow the JS servers to work properly, thinking that the compiler actually ran. + compiler = {}; + compiler.run = (cb) => { + cb(null, null); + }; } else { + ({serverRoutes, compiler} = compileClient(options)); + } + + if (!jsUrl) { // if jsUrl is not set, we need to start up a JS server, either hot load // or static. startJsServer = hot ? startHotLoadJsServer : startStaticJsServer; @@ -293,8 +285,10 @@ export default function start(options){ logger.notice("Starting servers..."); - const jsServer = startJsServer(compiler, options, routesFileLocation); - const htmlServerPromise = serverRoutesPromise.then(serverRoutesFile => startHtmlServer(serverRoutesFile, port, bindIp, httpsOptions, customMiddlewarePath)); + const jsServer = startJsServer(compiler, jsPort, bindIp, longTermCaching, httpsOptions); + const htmlServerPromise = serverRoutes + .then(serverRoutesFile => startHtmlServer(serverRoutesFile, port, bindIp, httpsOptions, customMiddlewarePath)) + .catch((e) => { throw e; }); return { stop: () => Promise.all([jsServer.stop(), htmlServerPromise.then(server => server.stop())]), diff --git a/packages/react-server-cli/src/handleCompilationErrors.js b/packages/react-server-cli/src/handleCompilationErrors.js index 929b14fe7..3f5d88cb1 100644 --- a/packages/react-server-cli/src/handleCompilationErrors.js +++ b/packages/react-server-cli/src/handleCompilationErrors.js @@ -10,13 +10,13 @@ export default function handleCompilationErrors (err, stats){ logger.error(err); return new Error(err); // TODO: inspect stats to see if there are errors -sra. - } else if (stats.hasErrors()) { + } else if (stats && stats.hasErrors()) { console.error("There were errors in the JavaScript compilation."); stats.toJson().errors.forEach((error) => { console.error(error); }); return new Error("There were errors in the JavaScript compilation."); - } else if (stats.hasWarnings()) { + } else if (stats && stats.hasWarnings()) { logger.warning("There were warnings in the JavaScript compilation. Note that this is normal if you are minifying your code."); // for now, don't enumerate warnings; they are absolutely useless in minification mode. // TODO: handle this more intelligently, perhaps with a --reportwarnings flag or with different diff --git a/packages/react-server-cli/src/parseCliArgs.js b/packages/react-server-cli/src/parseCliArgs.js index 4f4703672..c030560da 100644 --- a/packages/react-server-cli/src/parseCliArgs.js +++ b/packages/react-server-cli/src/parseCliArgs.js @@ -109,7 +109,6 @@ export default (args = process.argv) => { }) .option("compile-on-startup", { describe: "Enable/disable compiling on startup. Recommended for use in production when files have been pre-compiled.", - default: true, type: "boolean", }) .version(function() { diff --git a/packages/react-server-cli/src/run.js b/packages/react-server-cli/src/run.js index 64d77dd21..d6e324dd0 100644 --- a/packages/react-server-cli/src/run.js +++ b/packages/react-server-cli/src/run.js @@ -43,7 +43,8 @@ export default function run(options = {}) { options.outputUrl = jsUrl || `//${host}:${jsPort}/`; try { - return require("./" + path.join("commands", options.command))(options); + const commandResult = require("./" + path.join("commands", options.command))(options); + return (options.command === "start") ? commandResult.started : commandResult; } catch (e) { if (e instanceof ConfigurationError) { console.error(chalk.red(e.message)); From e63d12b253ce333bf3c1829fac7d349098f8a8de Mon Sep 17 00:00:00 2001 From: PC Drew Date: Sun, 5 Feb 2017 11:18:41 -0700 Subject: [PATCH 4/9] Remove compileOnly options as it is unnecessary and not implemented. Add initial documentation for compileOnStartup option. --- packages/react-server-cli/src/defaultOptions.js | 4 ++-- packages/react-server-cli/src/parseCliArgs.js | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/react-server-cli/src/defaultOptions.js b/packages/react-server-cli/src/defaultOptions.js index d2d78ad9a..32723a398 100644 --- a/packages/react-server-cli/src/defaultOptions.js +++ b/packages/react-server-cli/src/defaultOptions.js @@ -4,7 +4,8 @@ // jsPort: the port number for JavaScript and CSS on the server. // hot: enable hot reloading. do not use in production. // minify: minify the client-side JavaScript. -// compileOnly: just compile the client-side JavaScript; don't start up a server. +// compileOnStartup: start up a server without compiling the client-side JavaScript; this expects that you have already +// run the compile command prior to starting (i.e. in a production environment where build and run times are separated). // jsUrl: serve up the HTML, but don't serve up the JavaScript and CSS; instead point the // JS & CSS URLs at this URL. // logLevel: the level of logging to use. @@ -18,7 +19,6 @@ export default { bindIp: "0.0.0.0", hot: true, minify: false, - compileOnly: false, compileOnStartup: true, logLevel: "debug", timingLogLevel: "fast", diff --git a/packages/react-server-cli/src/parseCliArgs.js b/packages/react-server-cli/src/parseCliArgs.js index c030560da..4a10f129a 100644 --- a/packages/react-server-cli/src/parseCliArgs.js +++ b/packages/react-server-cli/src/parseCliArgs.js @@ -93,11 +93,6 @@ export default (args = process.argv) => { describe: "Set the severity level for the gauge logs. Values are, in ascending order of severity: 'no', 'hi', 'lo', 'ok'. Default is 'no' in production mode, 'ok' otherwise.", type: "string", }) - .option("compile-only", { - describe: "Compile the client JavaScript only, and don't start any servers. This is what you want to do if you are building the client JavaScript to be hosted on a CDN. Unless you have a very specific reason, it's almost alway a good idea to only do this in production mode. Defaults to false.", - default: undefined, - type: "boolean", - }) .option("js-url", { describe: "A URL base for the pre-compiled client JavaScript. Setting a value for jsurl means that react-server-cli will not compile the client JavaScript at all, and it will not serve up any JavaScript. Obviously, this means that --jsurl overrides all of the options related to JavaScript compilation: --hot, --minify, and --bundleperroute.", type: "string", From 940a96ace27d8bf4a18be911b77c90414dec637b Mon Sep 17 00:00:00 2001 From: PC Drew Date: Sun, 5 Feb 2017 11:22:17 -0700 Subject: [PATCH 5/9] Remove compileOnly option in the documentation. --- docs/guides/react-server-cli.md | 1 - .../generators/app/templates/_reactserverrc | 1 - packages/react-server-examples/redux-basic/.reactserverrc | 1 - packages/react-server-examples/styled-components/.reactserverrc | 1 - 4 files changed, 4 deletions(-) diff --git a/docs/guides/react-server-cli.md b/docs/guides/react-server-cli.md index b118e312d..188420be1 100755 --- a/docs/guides/react-server-cli.md +++ b/docs/guides/react-server-cli.md @@ -383,7 +383,6 @@ run({ jsPort : 3001, hot : true, minify : false, - compileOnly : false, jsUrl : null, longTermCaching : true, }); diff --git a/packages/generator-react-server/generators/app/templates/_reactserverrc b/packages/generator-react-server/generators/app/templates/_reactserverrc index cd705ae5b..8a1965b29 100644 --- a/packages/generator-react-server/generators/app/templates/_reactserverrc +++ b/packages/generator-react-server/generators/app/templates/_reactserverrc @@ -6,7 +6,6 @@ "hot": false, "minify": false, "longTermCaching": false, - "compileOnly": false, "https": false, "logLevel": "debug", "env": { diff --git a/packages/react-server-examples/redux-basic/.reactserverrc b/packages/react-server-examples/redux-basic/.reactserverrc index 628254db3..a15e15596 100644 --- a/packages/react-server-examples/redux-basic/.reactserverrc +++ b/packages/react-server-examples/redux-basic/.reactserverrc @@ -6,7 +6,6 @@ "hot": true, "minify": false, "long-term-caching": false, - "compile-only": false, "https": false, "log-level": "debug", "env": { diff --git a/packages/react-server-examples/styled-components/.reactserverrc b/packages/react-server-examples/styled-components/.reactserverrc index 192acdc5c..d234eb29d 100644 --- a/packages/react-server-examples/styled-components/.reactserverrc +++ b/packages/react-server-examples/styled-components/.reactserverrc @@ -6,7 +6,6 @@ "hot": false, "minify": false, "longTermCaching": false, - "compileOnly": false, "https": false, "logLevel": "debug", "env": { From 2b1f31b392c5d9227160c01cca05e127356206ca Mon Sep 17 00:00:00 2001 From: PC Drew Date: Sun, 5 Feb 2017 11:47:44 -0700 Subject: [PATCH 6/9] Add new compileOnStartup option to the tests. --- packages/react-server-cli/src/parseCliArgs.js | 1 + .../react-server-cli/test/parseCliArgs.js | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/react-server-cli/src/parseCliArgs.js b/packages/react-server-cli/src/parseCliArgs.js index 4a10f129a..52649cf5f 100644 --- a/packages/react-server-cli/src/parseCliArgs.js +++ b/packages/react-server-cli/src/parseCliArgs.js @@ -104,6 +104,7 @@ export default (args = process.argv) => { }) .option("compile-on-startup", { describe: "Enable/disable compiling on startup. Recommended for use in production when files have been pre-compiled.", + default: undefined, type: "boolean", }) .version(function() { diff --git a/packages/react-server-cli/test/parseCliArgs.js b/packages/react-server-cli/test/parseCliArgs.js index 7fb6389b7..e18917763 100644 --- a/packages/react-server-cli/test/parseCliArgs.js +++ b/packages/react-server-cli/test/parseCliArgs.js @@ -308,3 +308,40 @@ test('react-server-cli:parseCliArgs::longTermCaching option can be turned on usi t.is(parsedArgs.longTermCaching, true, 'longTermCaching is true'); t.true(Object.keys(defaultOptions).indexOf('longTermCaching') > -1, 'longTermCaching key exists in defaultOptions'); }); + +// **** compileOnStartup **** +// compileOnStartup will be undefined if no argument is provided +test('react-server-cli:parseCliArgs::compileOnStartup will be undefined if no argument is provided', async t => { + const args = [ + ...defaultArgs, + 'compile' + ]; + const parsedArgs = await parseCliArgs(args); + t.is(parsedArgs.compileOnStartup, undefined, 'compileOnStartup is undefined if no argument is provided'); +}); + +// compileOnStartup option can be turned on using --compileOnStartup argument +test('react-server-cli:parseCliArgs::compileOnStartup option can be turned on using --compileOnStartup argument', async t => { + const args = [ + ...defaultArgs, + 'compile', + '--compileOnStartup' + ]; + const parsedArgs = await parseCliArgs(args); + t.is(typeof parsedArgs.compileOnStartup, 'boolean', 'compileOnStartup is true'); + t.is(parsedArgs.compileOnStartup, true, 'compileOnStartup is true'); + t.true(Object.keys(defaultOptions).indexOf('compileOnStartup') > -1, 'compileOnStartup key exists in defaultOptions'); +}); + +// compileOnStartup option can be turned on using --compile-on-startup argument +test('react-server-cli:parseCliArgs::compileOnStartup option can be turned on using --compile-on-startup argument', async t => { + const args = [ + ...defaultArgs, + 'compile', + '--compile-on-startup' + ]; + const parsedArgs = await parseCliArgs(args); + t.is(typeof parsedArgs.compileOnStartup, 'boolean', 'compileOnStartup is true'); + t.is(parsedArgs.compileOnStartup, true, 'compileOnStartup is true'); + t.true(Object.keys(defaultOptions).indexOf('compileOnStartup') > -1, 'compileOnStartup key exists in defaultOptions'); +}); From 93088f3611e9744c147f95cb8283806125fcacb9 Mon Sep 17 00:00:00 2001 From: PC Drew Date: Sun, 5 Feb 2017 12:03:57 -0700 Subject: [PATCH 7/9] Added compileOnStartup documentation. --- docs/guides/production.md | 11 +++++++++++ docs/guides/react-server-cli.md | 11 ++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/guides/production.md b/docs/guides/production.md index 87c64e7fe..df31c732c 100644 --- a/docs/guides/production.md +++ b/docs/guides/production.md @@ -27,6 +27,7 @@ It helps to setup your project with per-environment settings that are appropriat "jsUrl": "/assets/" }, "production": { + "compileOnStartup": false, "jsUrl": "/assets/", "logLevel": "error", "minify": true @@ -186,6 +187,16 @@ and 3001. This is probably not what you want in production. A referenced asset would be something that your application links to, but `react-server` doesn't know about. This might be a logo image that you link to with an `` tag. +## Compiling Static Assets +See the [`react-server-cli`](https://react-server.io/docs/guides/react-server-cli) documentation to understand how to +compile static assets. After you have compiled the assets, you can decrease the startup time on a server by setting the +`compileOnStartup` option to `false`. This tells `react-server` that you previously compiled the assets and it doesn't +need to compile anything at run time. Integrating this into your deployment pipeline can greatly improve server startup +time, especially with immutable infrastructure. As a simple example of this pipeline, you can compile the assets when +building a server image then, when starting many servers using the same image, each server has the already-compiled version +of the software and can reliably startup. If you use this option without previously compiling your application, `react-server` +will fail to start. + ## Served through a Fronting Server The example fronting server configurations show how to configure a fronting HTTP/HTTPS server to serve the static assets generated by `react-server` after compilation. Simply point the fronting server to the appropriate `__clientTemp/build` diff --git a/docs/guides/react-server-cli.md b/docs/guides/react-server-cli.md index 188420be1..01689e1bc 100755 --- a/docs/guides/react-server-cli.md +++ b/docs/guides/react-server-cli.md @@ -157,7 +157,8 @@ export default (server, reactServerMiddleware) => { server.use(compression()); server.use(bodyParser.urlencoded({ extended: false })); server.use(bodyParser.json()); - server.use(session(options)); + server.use(helmet()); + server.use(session(options)); // Custom express middleware being added reactServerMiddleware(); // Must be called once or server will not start } ``` @@ -302,6 +303,14 @@ is incompatible with --hot. Defaults to **false** in development mode and **true** in production. +#### --compile-on-startup +Tells `react-server` to compile the client code during the startup procedure. +In many cases, such as in production, the code has already been compiled and +the server shouldn't take the time to compile it again. +This option is incompatible with --hot and will be ignored if hot is `true`. + +Defaults to **true**. + #### --js-url A URL base for the pre-compiled client JavaScript; usually this is a base URL on a CDN or separate server. Setting a value for js-url means that From a2578fcac50708390a7d53861d460c9ec8a4d628 Mon Sep 17 00:00:00 2001 From: PC Drew Date: Sun, 5 Feb 2017 12:19:40 -0700 Subject: [PATCH 8/9] Shift the proper handling of the return object from 'start' from the run script to the cli script. --- packages/react-server-cli/src/cli.js | 5 ++++- packages/react-server-cli/src/run.js | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-server-cli/src/cli.js b/packages/react-server-cli/src/cli.js index 5e661fa71..c632e9b03 100755 --- a/packages/react-server-cli/src/cli.js +++ b/packages/react-server-cli/src/cli.js @@ -1,4 +1,7 @@ require("babel-core/register"); const cli = require("."); -cli.parseCliArgs().then(cli.run).catch(console.error); +cli.parseCliArgs().then((options) => { + const commandResult = cli.run(options); + return (options.command === "start") ? commandResult.started : commandResult; +}).catch(console.error); diff --git a/packages/react-server-cli/src/run.js b/packages/react-server-cli/src/run.js index d6e324dd0..64d77dd21 100644 --- a/packages/react-server-cli/src/run.js +++ b/packages/react-server-cli/src/run.js @@ -43,8 +43,7 @@ export default function run(options = {}) { options.outputUrl = jsUrl || `//${host}:${jsPort}/`; try { - const commandResult = require("./" + path.join("commands", options.command))(options); - return (options.command === "start") ? commandResult.started : commandResult; + return require("./" + path.join("commands", options.command))(options); } catch (e) { if (e instanceof ConfigurationError) { console.error(chalk.red(e.message)); From 0a145fc2fd26cd33ad570e69d3a09e3d62b31f90 Mon Sep 17 00:00:00 2001 From: PC Drew Date: Mon, 6 Feb 2017 14:21:13 -0700 Subject: [PATCH 9/9] Minor documentation of an ES6 destructuring constraint. --- packages/react-server-cli/src/commands/start.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-server-cli/src/commands/start.js b/packages/react-server-cli/src/commands/start.js index 01ea617c3..33f8550e8 100644 --- a/packages/react-server-cli/src/commands/start.js +++ b/packages/react-server-cli/src/commands/start.js @@ -274,6 +274,9 @@ export default function start(options){ cb(null, null); }; } else { + // ES6 destructuring without a preceding `let` or `const` results in a syntax error. Therefore, the below + // statement must be wrapped in parentheses to work properly. + // http://exploringjs.com/es6/ch_destructuring.html#sec_leading-curly-brace-destructuring ({serverRoutes, compiler} = compileClient(options)); }