-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server side hot reload #809
Server side hot reload #809
Conversation
…ilation of webpack client code.
…equire.cache entries!
659816d
to
2d0b894
Compare
server.use((req, res, next) => { | ||
reactServer.middleware(req, res, next, require(serverRoutes)); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there's an extra level of indentation here?
Surprised eslint didn't catch that. 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. I don't think that eslint is working properly. asini run lint
blows up and there's no script in the root level package.json to run lint. I'll look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(at least in my setup it's blowing up)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... we just run asini run test
in CI. So each package is responsible for making sure that linting is part of its test script. It looks like the CLI's package.json should be running tests as part of the gulp test
phase of its test script.
Wonder where that's breaking... 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so...lint
doesn't exist anywhere else. There's a weird mix of gulp tasks and package.json run scripts though. And it really bothers me that they're not named the same. In some packages, it's gulp build
in others it's gulp
, and in other cases, you have to do npm run build
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, nice catch on the missing dep. Yeah, some modules (like react-server-cli
) use the eslint gulp plugin instead of the executable. It's all kind of a weird mishmash right now. 😖
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll clean it up and submit a PR. I already have the previous issue fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the offender: https://github.com/redfin/react-server/blame/master/packages/react-server-cli/gulpfile.babel.js#L7 Need to use globs...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR #818
@@ -324,8 +336,7 @@ module.exports = { | |||
const routesContent = routesOutput.join(""); | |||
// make a unique file name so that when it is required, there are no collisions | |||
// in the module loader between different invocations. | |||
const routesMD5 = crypto.createHash('md5').update(routesContent).digest("hex"); | |||
const routesFilePath = `${workingDirAbsolute}/routes_${isClient ? "client" : "server_" + routesMD5}.js`; | |||
const routesFilePath = `${workingDirAbsolute}/routes_${isClient ? "client" : "server"}.js`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to get rid of this.
Object.keys(require.cache) | ||
.filter((key) => /(__clientTemp|test-temp)/.test(key)) | ||
.forEach((key) => delete require.cache[key]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, interesting. Glad you caught this... could have been a real source of frustration. 😬
This is a cool solution. Thanks for working it out @drewpc! LGTM 👍 |
Thanks for splitting up the pull-request @drewpc. This makes reviewing it a lot easier 👍 The solution is very simple and works as long as you stay within the page lifecycle. However, this solution will probably leak memory over time in more advanced use-cases, since cleaning the To me, a cleaner solution is to have a separate application server process that receives a KILL signal when a new bundle has been generated. This will make sure that the server cleans up everything before it is restarted. A downside of this approach is that restarting a process takes longer than cleaning the |
@jhnns Great point! I hadn't looked deeply into this until you mentioned it. I did some basic testing on memory leaks with regard to deleting require.cache entries (modifying this code) and came to the following conclusion: my server side patch HMR solution doesn't do anything good or bad to resolve those memory leaks. In other words, if memory leaks exist in the code being required The topic of memory leaks would be a great documentation item: patterns to avoid, ways to detect memory leaks, etc. The page cycle of |
Added |
Of course, you're right. These memory leaks exists in the code because you don't write tear-down code if you don't need it. But usually you don't just delete But besides the memory leak I rather worry about the unknown application state after modules have been swapped out without actually tearing down everything. Your application may behave strangely in development after a while (because of stale connections, etc.). I'd rather prefer to restart the server cleanly than having to deal with an unknown application state. That's why create-react-app is also using HMR just for CSS, because CSS can be replaced without introducing side-effects. I've explained some details of HMR in this SO answer. |
# Conflicts: # packages/react-server-cli/src/commands/start.js
…to patch-server-side-hot-reload
…er serverSideHotModuleReload function from PR redfin#809 so that things are more in sync.
This patch enables server-side hot module reload on the existing master branch. Note: this does not include any Webpack changes nor does it include any changes to ExpressJS unless required for HMR to work. It simply reloads the existing changes when Webpack successfully recompiles the client code. This is a refactor of the code from #791 to only include server side HMR changes. Discussed in #773.
UPDATE: this branch was rebased and changes from #808 were removed. It is now a standalone patch.