{"id":17749526,"url":"https://github.com/oresoftware/hr4r","last_synced_at":"2025-11-01T10:07:50.493Z","repository":{"id":95712358,"uuid":"42683990","full_name":"ORESoftware/hr4R","owner":"ORESoftware","description":"Example project - \"Hot Reloading 4 RequireJS\" front-end web applications \u0026 some extra code demonstrating hot-reloading for Node.js Express servers","archived":false,"fork":false,"pushed_at":"2016-03-09T03:24:39.000Z","size":10139,"stargazers_count":28,"open_issues_count":3,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-13T01:39:15.686Z","etag":null,"topics":["code-splitting","dynamic","hot-reload","hot-reloading","jit","lazy-loading","nodejs","requirejs","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ORESoftware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-09-17T21:53:48.000Z","updated_at":"2025-02-25T03:19:06.000Z","dependencies_parsed_at":"2023-05-04T17:47:57.392Z","dependency_job_id":null,"html_url":"https://github.com/ORESoftware/hr4R","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ORESoftware/hr4R","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ORESoftware%2Fhr4R","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ORESoftware%2Fhr4R/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ORESoftware%2Fhr4R/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ORESoftware%2Fhr4R/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ORESoftware","download_url":"https://codeload.github.com/ORESoftware/hr4R/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ORESoftware%2Fhr4R/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260838661,"owners_count":23070613,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-splitting","dynamic","hot-reload","hot-reloading","jit","lazy-loading","nodejs","requirejs","typescript"],"created_at":"2024-10-26T11:23:36.661Z","updated_at":"2025-11-01T10:07:50.458Z","avatar_url":"https://github.com/ORESoftware.png","language":"JavaScript","readme":"#hr4R \n\n\nclient-side hot-reloading is much more useful than server-side hot-reloading - both can save you time - but client hot-reloading can save you lots more time,\nand make designers' lives much better.\n\n######the steps for clientside hot-reloading are:\n\n1.  gulp.js watchers listen for filesystem changes\n2.  socket.io server in gulpfile sends a message to all browser clients with the path of the file that changed\n3.  client deletes cache representing that file/module, and re-requires it (using AJAX to pull it from the server filesystem)\n4.  front-end app is configured / designed to re-evaluate all references to the modules that it wishes to hot-reload, in this case, only JS views, templates and CSS are \n     available to hot reload -  the router, controllers, datastores (Backbone Collections and Models) are not configured yet. I do suspect all files could be hot reloaded\n     with the only exception being data stores. \n\n\n```RequireJS``` makes this all very easy to do since it's an asynchronous module loading system - you don't need to run a build or an incremental build to get the client code in\nthe air - and you may easily require a nominal file on the fly. In production, you can use r.js to build optimized (concatenated, minified) deployment files, and your requires \nessentially become synchronous instead of asynchronous. RequireJS is the real deal. Thanks RequireJS.\n\n\n####Clientside hot-reloading\n\nto run this Express server, you should have a local MongoDB instance running with this command\n\n```ulimit -n 1024 \u0026\u0026 mongod --dbpath /some/path/anywhere/you/want/mongodb_dev_data --replSet rs0```\n\nstart the node.js Express server with\n\n```gulp nodemon```\n\nthis runs Nodemon, after running some other important gulp tasks. Nodemon is a must-have, and it should be configured to ignore changes to your client code - in this\ncase, nodemon only looks for server changes and ignores changes that we make to the client code in the public directory. On the other hand - our front-end hot-reloading process\nwill listen exclusively to files in the public directory.\n\nwe run a couple tasks before starting nodemon - we transpile JSX and then run a metadata generator that I wrote that allows RequireJS to require entire directories, this is very\nuseful for controllers and views, especially controllers that follow some path convention. Without a controller path convention I know of no possible way from keeping \nyour front-end router files from getting enormous.\n\nnext up, let's talk about the gulpfile.js at the root of the project\n\n\ngulpfile.js contains this code at the bottom of the file:\n\n```javascript\n\ngulp.task('metagen:all', ['transpile-jsx'], function (done) {\n    runAllMetagens(done);\n});\n\n\ngulp.task('nodemon', ['metagen:all', 'watch:hot-reload-front-end', 'watch:hot-reload-back-end'], function () {\n\n    nodemon({\n\n        script: 'bin/www.js',\n        ext: 'js',\n        ignore: ['public/*', '*.git/*', '*.idea/*', 'routes/*', 'gulpfile.js'],     //nodemon monitors our server for changes, but ignores changes to client code in public directory\n        args: ['--use_socket_server', '--use_hot_reloader'],\n        nodeArgs: [],\n        env: {'NODE_ENV': 'development'}\n\n    }).on('restart', []);\n\n});\n```\n\nyou should look into what the  'watch:hot-reload-front-end' task does - it sends a socket.io message to the browser, however many browsers\nyou have open on your development machine, sometimes I have 2 or 3 and I need all browser windows to receive an update, of course they all do, \nthanks to websockets, thanks Socket.io.\n\n\nin the client code (all client code is in the ./public directory) we have a hotReloadHandler, which is a socket.io client\n\n\nthere's some extra ugly code in there that resolves paths that I wish to simplify but essentially the code looks like this\n\n\n\n```javascript\n\nsocketHotReload.on('hot-reload (.jsx)', function (data) {\n\n    updateProgressBar(40);\n\n    hotReloader.hotReload(data,function(err,result){              // deletes the cached module reference\n\n        if(err){\n            alert(err);\n            return;\n        }\n\n        updateProgressBar(60);\n\n        var filename = deCapitalizeFirstLetter(reconcilePath1(data,'jsx'));\n\n        require(['#allViews'],function(allViews){                  // AJAX is used to re-require the file\n            allViews[filename] = result;                           // new file is loaded and we update the singleton \"allViews\" that holds the references to every view in the app\n            updateProgressBar(80);\n            Backbone.history.loadUrl(Backbone.history.fragment);   // we re-render all views in sight, via the Backbone router (this does not refresh the page!)\n            updateProgressBar(100);\n        });\n    });\n});\n\n```\n\n\nthe above calls the following module, which does all the reloading, for .js, templates and CSS\n\n\n```javascript\n\n  define(function () {\n\n        var hotReloadSimple = function (item, callback) {\n            require.undef(item);\n            require([item], function (file) {\n                callback(null, file);\n            });\n        };\n\n        return {\n            hotReload:hotReloadSimple\n        }\n\n    });\n```\n\n\nthat's pretty much it for client-side hot-reloading\n\u003cbr\u003e\n...next up we have serverside hot-reloading\n\n\n#### hot-reloading Node.js Express server code\n\n\nI debated whether to include this portion of serverside reloading, but I think it's actually easier to reload servercode and then when you get the idea, \nyou can try the clientside second.\n\n\nin our canonical app.js, at the root of the project, we have this:\n\n\n```javascript\n\nvar runRoute = null;\n\nif (app.get('env') === 'development') {\n    runRoute = function (path) {   //this hot reloads all file in the routes dir (serverside hot-reloading - less time waiting for server to restart with nodemon)\n        return function (req, res, next) {\n            require(path)(req, res, next);\n        };\n    }\n}\nelse {\n    runRoute = function (path) {  //for production and test environments, runRoute function resolves modules immediately\n        return require(path);\n    }\n}\n\n//ROUTES\napp.use('/', runRoute('./routes/index'));\n\n\n```\n\n as you can see, if we are in the development environment, ```runRoute``` is function that returns a function (known as functor for all you academics),\n which means that every time a route is hit, it re-evaluates the require statement - so if we reload a file, it will re-evaluate the require pulling in the new file \n and avoiding re-referencing the old file. DON'T WORRY. If the file is already cached it will not slow down your development server, at all. If you delete the cache, only\n then will it have to do the extra work of re-requiring the file. It does not have to re-require everytime. Just to be sure you get that.\n \n Got it? pretty straightforward\n \n \n we don't need socket.io for serverside reloading - because we already have an HTTP server listening for stuff\n \n \n so in development mode we just need to add a route that can handle hot-reload requests like so:\n \n ```javascript \n \n if (app.get('env') === 'development') {\n     app.post('/hot-reload', function (req, res, next) {  //route to handle serverside hot-reloading of files in our /routes dir\n         var path = req.body.path;\n         path = require.resolve(path);\n         if (path.indexOf('node_modules') \u003c 0 \u0026\u0026 path.indexOf('routes') \u003e 0) {\n             try {\n                 delete require.cache[path];\n                 res.send({success: 'successfully deleted cache with keyname: ' + path});\n             }\n             catch (err) {\n                 res.send({error: String(err)});\n             }\n         }\n     });\n }\n ```\n \n as you can see, I configured it to ignore anything in /node_modules/ directory and made sure only the /routes directory was being listened to;\n I could have just used '/routes/' but just in case there was some node_module with '/routes/' in the path, I added that clause. Not as fail proof\n as I would like, but should work for now.\n \n \n the final part of the equation is similar to front-end reloading - we use gulp to listen to the filesystem - like so:\n \n \n ```javascript\n \n gulp.task('watch:hot-reload-back-end', function () {\n \n     //if route file changes, we just reload that one route, it works\n \n     gulp.watch('./routes/**/*.js').on('change', function (file) {\n \n         request({\n                 method: 'POST',\n                 json: {\n                     path: file.path\n                 },\n                 uri: 'http://localhost:3000/hot-reload'\n             },\n             function (err, response, body) {\n                 if (err) {\n                     console.log(colors.red(ijson.parse(err).error));\n                 }\n                 else {\n                     console.log(colors.blue(ijson.parse(body).success));\n                 }\n             });\n     });\n });\n\n```\n\nif you have any questions you can open an issue, thanks!\n \n \n \n \n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foresoftware%2Fhr4r","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foresoftware%2Fhr4r","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foresoftware%2Fhr4r/lists"}