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]);