diff --git a/operations/development-server.js b/operations/development-server.js index 30dff60..0047ae2 100644 --- a/operations/development-server.js +++ b/operations/development-server.js @@ -5,75 +5,23 @@ const defaultValue = require("default-value"); const assureArray = require("assure-array"); const path = require("path"); const entities = require("entities"); -const util = require("util"); - -let reloadClientTag = Buffer.from(""); - -function monkeyPatchEnd(res) { - // NOTE: This is a hack, to auto-inject the Budo livereload client in any outgoing HTML response (but only in development mode). - let end = res.end.bind(res); - - res.end = function monkeyPatchedEnd(... args) { - let originalChunk = args[0]; - let prefix; - - if (originalChunk == null || typeof originalChunk === "string" || Buffer.isBuffer(originalChunk)) { - if (!res.headersSent) { - // If headers have already been sent, we'll just have to hope that the browser will still look at our appended tag, as we can't change the response size anymore... - // TODO: Make this more robust in the future - let typeHeader = res.getHeader("content-type"); - - if (typeHeader != null && typeHeader.startsWith("text/html")) { - let contentLength = res.getHeader("content-length"); - - if (contentLength != null) { - // Compensate for the additional bytes introduced by our injected script tag - res.setHeader("content-length", parseInt(contentLength) + reloadClientTag.length); - } - - prefix = reloadClientTag; - } - } - - // Reset the `end` method back to the original method; we don't need to get in the way anymore - res.end = end; - - let originalChunkAsBuffer = (typeof originalChunk === "string") - ? Buffer.from(originalChunk) - : originalChunk; - - if (prefix != null) { - let chunk = (originalChunkAsBuffer == null) - ? reloadClientTag - : Buffer.concat([ originalChunkAsBuffer, reloadClientTag ]); - - end(chunk, ... args.slice(1)); - } else { - end(... args); - } - } else { - throw new Error(`Expected a buffer or string in 'end', but got something else: ${util.inspect(args[0])}`); - } - }; -} +const stacked = require("stacked"); +const injectLRScript = require("inject-lr-script"); function createExpressMiddleware(app, fullBundlePath) { - return function expressMiddleware(req, res, next) { - try { - monkeyPatchEnd(res); - } catch (error) { - process.nextTick(() => { - throw error; - }); - } + let handler = stacked(); + + handler.use(injectLRScript({ local: true, path: "/budo/livereload.js" })); + handler.use((req, res, next) => { // Ensure that we never intercept budo-served resources. Otherwise, the Express app might have something like a 404 handler or "always require login" middleware that stops requests for budo assets from ever coming out the other end of the Express app. if (req.url !== "/budo/livereload.js" && req.url !== `/${fullBundlePath}`) { app.handle(req, res, (err) => { if (err != null && err instanceof Error) { res .status(500) - .send(`${reloadClientTag}
${entities.escape(err.stack)}
`); + .set("content-type", "text/html") + .send(`
${entities.escape(err.stack)}
`); } else { next(err); } @@ -81,7 +29,7 @@ function createExpressMiddleware(app, fullBundlePath) { } else { next(); } - }; + }); } module.exports = function ({ options, staticPath, staticBasePath, entryPaths, host }) { diff --git a/package.json b/package.json index bc33b5c..41608e6 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "chalk": "^3.0.0", "default-value": "^1.0.0", "entities": "^2.0.0", + "inject-lr-script": "^2.2.0", "is-stream": "^2.0.0", + "stacked": "^1.1.1", "validatem": "^0.2.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index db219ae..f3489bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1492,7 +1492,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -inject-lr-script@^2.1.0: +inject-lr-script@^2.1.0, inject-lr-script@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/inject-lr-script/-/inject-lr-script-2.2.0.tgz#58d91cd99e5de1a3f172aa076f7db8651ee72db2" integrity sha512-lFLjCOg2XP8233AiET5vFePo910vhNIkKHDzUptNhc+4Y7dsp/TNBiusUUpaxzaGd6UDHy0Lozfl9AwmteK6DQ==