How the green junior wrote his hot-reloader

Background



A couple of lines about me for a general understanding of the author's level and the problem being solved.
My name is Eugene and I am a web developer green junior frontend developer.

Some other year ago, I worked in a completely different field and only in theory thought about changing my profession, but around December 2018 I found my way and started acting.

After about six months of total training, I get a job as a frontend programmer. Behind the training in fundamental things (I want to think so), js, interaction with the DOM, react + redux. HTML and CSS at least + a general understanding of bootstrap and assembly, working with git, the command line.

In addition to the theory, a couple of training projects have been made, including a chat on react + redux, as well as a couple of attempts to implement some of their ideas.

In general, such a standard modern gentleman's set for a beginner front'a.

The first week and a half I set up a virtual machine, there are a lot of everything and everything is unfamiliar and incomprehensible to me.

In the process, I get acquainted with new tools and technologies: with databases (and put myself another bookmark in the “learn” list), putty, wincsp, etc.

Successfully pass this obstacle course and go to the front.



Foreword



Having already written my reloader and this article, I found analogues, including ones on Habré . However, he decided to publish his bike.



Start



We have a rather large project, inherited, written in angularJS, with all its charms. After React, he seemed like a dinosaur to me, but nothing, I buy angularjs courses, quickly drive in and start to benefit.



A positive impression is that the project was written well, by people with clearly straight arms. Variables with excellent clear naming, the structure is the same everywhere and in general the whole logic is very accessible and simply expressed.



But there are enough minuses.



The first problem: the project is being built by some ancient minimizer, and it is impossible to use the modern js syntax. None () => {}, const res = [... data, subRes], async / await ...



The second problem: there is neither webpack, nor even at least gulp, and accordingly there is no webpack-dev-server familiar to me with its excellent hot reload.



Wrote. Saved. F5. Inconveniently. Pain? Not a direct pain, but very uncomfortable.



The third problem: building the project .bat file, in which part of the project is simply copied, some libraries are collected without minimization, some are minimized into one file, the rest of the project files into another. List of libraries in the third file. List of files to build in the fourth. And so on.



The fourth problem: all libraries are neatly in the libs folder and are connected by a script in index.html. All-all, except for express and proxy for it (they are not involved in the assembly, but only for development).

And far from everywhere there are versions or an indication of a specific library.



I lived in training in a beautiful world of functional programming, full of es6 +, webpack-dev-server, tdd, eslint, automatic build and so on.



And here in the adult world, everything is completely different ...



Tie



But I like to work, I consider obstacles as opportunities for self-development, the company is good, the atmosphere is excellent, my eyes are on fire!



In working hours I carry out work tasks, in my free time I try to improve something.



Mid-June, I start with an attempt to fasten webpack, but the first approach expects a complete failure. I’ve been tormented for a week, I’m very tired of it, temporarily put off.



I decide to start small - I connect the new syntax through babel. I’m adding babel’s initial processing to our magic build.bat, but something breaks the idyll and our old minificator stumbles. I am looking for a problem.



Stumbles on one of the libraries from the neat daddy libs. I look at library files: they are already minified in the old syntax.



I say babel - "you don’t go here ... the head will get the code, it will be really bad." I check: everything works! Hurrah! Now all those nice new stylish trendy youth things are available to me! First victory. Nicely. I think on this occasion to rename the script in e.bat (e-Evgen), but can not decide.



The new syntax is so familiar and enjoyable, but the thought of a crooked build doesn't leave me.



The end of June and the beginning of July. I’m doing the second approach, more thorough, but again I run into errors between webpack and angularjs. Again a week of research.



Once I spend several days and partly nights searching for a solution, I come across extremely interesting speeches from the HolyJS conference, where the guys are already digging pretty deep into webpack. I am advancing in his understanding, but I still do not understand the material to the end.



Interest strengthens.



A colleague says - forget it, to hand over the project in a couple of months, no longer need to do this.

I don’t hammer, but I put it off - a lot of work, it squeezes me everything, there is no time left for extracurricular activities.



Mid-July, I get in my hands similar to our project with a customized assembly. I’m going with him to the third approach and I’m practically setting up webpack with us, but in the end I’m catching new mistakes that I don’t have time to solve, the work rolls with new intensity + it mentally empties me, I put off this business again.



Main part



Mid august. As a result, a friend talks about learning node.js and his desire to write his own hot-reloader. Thoughts about our assembly flare up with renewed vigor.



Task: reload the current page when updating files in the project.

Features: all libraries are connected in index.html, you cannot require, not to mention import. The assembly before reload is not needed yet, only reload. In a development server that proxies requests for our backing, I can use packages, and I can also require!



All this happens on Friday and I decide that in a simplified version for our project, I am quite capable of implementing a technology that will save me and my colleagues from F5.



The thought process goes on and the vision of the solution is formed in the head.

The simplest server (like ours), in it I will go around the entire folder and subfolders and form a tree with the modification dates of each file.



Then, after every n milliseconds, I will bypass again and again and compare the values ​​of the change time. Changed - reload. A friend tells you - "do not reinvent the wheel, there is a watch in node.js". Great, I will use it. In server.js, I’ll configure watch behind the project folder and I will call location.reload () to change something inside.



First iteration:



server.js
var express = require('express'); var app = express(); var server = require('http').Server(app); const port = 9080; server.listen(port); app.use(express.static(__dirname + '/src')); location.reload().
      
      







The first problem is location- this is not a node.js variable (at this moment I gain an understanding of why my attempts to access process.env at the front were also unsuccessful))).



The second problem is how to make the front understand what reload needs to do.



The way out is websocket! The idea is tempting + I worked with them “half-cones” when I wrote a chat, I have a general idea. At the same time, I do a reboot counter per session, add a variable and process the request that gives it.



I try:



server.js
 var express = require('express'); //  express var app = express(); var server = require('http').Server(app); //  http  app var io = require('socket.io')(server); //  socket.io     var fs = require('fs'); const port = 9080; server.listen(port); app.use(express.static(__dirname + '/src')); let count = 0; app.get('/data', (req, res) => { res.data = count; res.send(`${count}`); }) const dir = './src'; fs.watch(dir, () => { io.emit('change', {data: count}); count += 1; })
      
      







At the front I’m doing the simplest app on angularjs



app.js
 angular.module('App', []) .controller('myAppCtrl',['$scope', '$timeout','$http', ($scope, $timeout, $http) => { $scope.title = '       '; $scope.count = 0; $scope.todo = [ '  ,  ', '   node.js watch   ', '     ,         ', ' , codeclimate  travis   ' ] $scope.marks = [ 'watcher      ' ] // var socket = io(); // socket.on('change', (data) => { // console.log(data.data); // $scope.count = data.data; // console.log('scope.count: ', $scope.count); // $scope.$digest();// // location.reload();//agfr // }) $http({method: 'GET',url: 'data'}) .then(response => { $scope.count = response.data;// }); }])
      
      







And a separate module that reloads it. Separate, so that the project does not get too much.



watch.js
 var socket = io(); socket.on('change', () => { location.reload(); })
      
      







Works! He also monitors files other than js (you never know!): Checked .json, .css.

I check the subfolders - it does not work.



I think, okay, now I’ll cut it down recursively. Just in case, google and - voila - is ready

decision.



Add this package.



server.js
 var express = require('express'); //  express var app = express(); var server = require('http').Server(app); //  http  app var io = require('socket.io')(server); //  socket.io     var fs = require('fs'); var watch = require('node-watch'); const port = 9080; server.listen(port); app.use(express.static(__dirname + '/src')); let count = 0; let changed = []; app.get('/data', (req, res) => { res.data = count; res.send({count, changed}); }) const translate = { remove: "", update: "" } watch('./', { recursive: true }, function(evt, name) { io.emit('change', {data: count}); count += 1; changed = [{name, evt}, ...changed]; });
      
      







Hooray, it works!



I recall eslint, codeclimate and travis.

Install the first, add the rest.

I clean up the code, everything is var on const and so on.



Linter swears that angular is not defined, but my features of connecting libraries in the project dictate this behavior, I turn it off. At the same time, I screw the variables from the command line a bit, run it, everything works!



He came to work on Monday and fastened the whole farm to a working draft. I had to change a little, at the same time make changes so that it was possible to change some startup parameters from the command line and exceptions, so that I did not read everything.



As a result, it turned out like this:



server.js
 const express = require('express'), http = require('http'), watch = require('node-watch'), proxy = require('http-proxy-middleware'), app = express(), server = http.createServer(app), io = require('socket.io').listen(server), exeptions = ['git', 'js_babeled', 'node_modules', 'build', 'hotreload'], // ,   ,    backPortObj = { /*  ,   back*/ }, address = process.argv[2] || /*    back*/, localHostPort = process.argv[3] || 9080, backMachinePort = backPortObj[address] || /*   back */, isHotReload = process.argv[4] || "y", // "n" || "y" target = `http://192.168.${address}:${backMachinePort}`, str = `Connected to machine: ${target}, hot reload: ${isHotReload === 'y' ? 'enabled' : 'disabled'}.`, link = `http://localhost:${localHostPort}/`; server.listen(localHostPort); app .use('/bg-portal', proxy({ target, changeOrigin: true, ws: true })) .use(express.static('.')); if (isHotReload === 'y') { watch('./', { recursive: true }, (evt, name) => { let include = false; exeptions.forEach(item => { if (`${name}`.includes(item)) include = true; }) if (!include) { console.log(name); io.emit('change', { evt, name, exeptions }); }; }); }; console.log(str); console.log(link);
      
      







app.js
 var socket = io.connect(); socket.on('change', ({ evt, name, exeptions }) => { location.reload(); });
      
      







the running script in package.json just calls server.js from under node and it starts like this:



 npm start 1.100 8080
      
      





Wrote. Saved. F5



In conclusion, I want to thank Vanya, my friend, in some places the mastermind and the main kicker, as well as Sasha - the man whom I consider my mentor!



Instead of an afterword



And after 2 weeks, on the last day of my trial period, I still screwed webpack and webpack-dev-server onto our project, sending my hot reloader to dust on the history shelf.



However, these 2 weeks we used it every day and he regularly did his job!



All Articles