dotfiles

personal configuration files and scripts
git clone https://tongong.net/git/dotfiles.git
Log | Files | Refs | README

serve (2528B)


      1 #!/usr/bin/env node
      2 // local development server with live reload but without bloat
      3 // adapted from https://dev.to/adamcoster/create-a-live-reload-server-for-front-end-development-3gnp
      4 // if one argument is noreload disable live reload
      5 // if one argument is nobrowser do not open localhost
      6 // depends on entr for watching file changes
      7 // uses long polling to inform the client of changes
      8 
      9 const http = require("http");
     10 const fs = require("fs");
     11 const path = require("path");
     12 const { spawn } = require("child_process");
     13 
     14 const HTTP_PORT = 5500;
     15 const CLIENT_INJECT_CODE = `\n<script>
     16 window.fetch("/poll-update").then(() => location.reload());
     17 </script>`
     18 
     19 const noreload = process.argv.includes("noreload");
     20 const nobrowser = process.argv.includes("nobrowser");
     21 
     22 let dir = process.cwd();
     23 if (fs.existsSync(path.join(dir, "dist"))
     24         && fs.statSync(path.join(dir, "dist")).isDirectory())
     25     dir = path.join(dir, "dist");
     26 
     27 // functions to call when a file updates
     28 let updateQueue = [];
     29 let updateFn = () => {
     30     updateQueue.forEach(f => f());
     31     updateQueue = [];
     32 }
     33 
     34 if (!noreload) {
     35     // this is really not optimal but better than using some bloated npm
     36     // package
     37     let startExec = () => {
     38         const p = spawn("sh", ["-c", "find | entr -pd echo"], { cwd: dir });
     39         p.stdout.on("data", updateFn);
     40         p.on("close", startExec);
     41     };
     42     startExec();
     43 }
     44 
     45 // serve a static file or index.html for directories
     46 function servePage(route, res) {
     47     if (fs.existsSync(route) && fs.statSync(route).isDirectory()) {
     48         route = path.join(route, "index.html");
     49     }
     50     if (fs.existsSync(route) && fs.statSync(route).isFile()) {
     51         res.writeHead(200);
     52         let file = fs.readFileSync(route);
     53         if (!noreload && route.endsWith(".html"))
     54             file = file.toString() + CLIENT_INJECT_CODE;
     55         res.end(file);
     56         return true;
     57     }
     58     return false;
     59 }
     60 
     61 const requestHandler = function (req, res) {
     62     const method = req.method.toLowerCase();
     63     if (method == "get") {
     64         if (!noreload && req.url == "/poll-update") {
     65             res.writeHead(200);
     66             updateQueue.push(() => res.end());
     67             return;
     68         }
     69         console.log(req.url);
     70         const route = path.normalize(path.join(dir, req.url));
     71         if (servePage(route,res)) return;
     72     }
     73     res.writeHead(404);
     74     res.end("page not found.");
     75 }
     76 
     77 const server = http.createServer(requestHandler);
     78 server.listen(HTTP_PORT);
     79 
     80 if (!nobrowser) spawn("xdg-open", ["http://localhost:" + HTTP_PORT]);