Basic prototype of hot reloading mostly working with one controller.

main
Zed A. Shaw 9 months ago
parent 334fb1ff77
commit 9a730cd5d6
  1. 68
      commands/app.js
  2. 106
      package-lock.json
  3. 4
      package.json
  4. 1
      templates/header.html
  5. 23
      templates/todo.html

@ -1,8 +1,9 @@
import Fastify from "fastify"; import Fastify from "fastify";
import { restartable } from "@fastify/restartable";
import FastifyStatic from "@fastify/static"; import FastifyStatic from "@fastify/static";
import path from "path"; import path from "path";
import { ToDo } from "../lib/models.js";
import fs from "fs"; import fs from "fs";
import chokidar from "chokidar";
import nunjucks from "nunjucks"; import nunjucks from "nunjucks";
export const description = "Runs your app."; export const description = "Runs your app.";
@ -11,39 +12,72 @@ export const options = [];
export const required = []; export const required = [];
const fastify = Fastify({ let CONFIGURED = false;
logger: true
});
// nunjucks does it's own reload so let it do that
nunjucks.configure("templates", { watch: true });
fastify.get("/todo", async (req, rep) => { const configure = async (fastify, opts) => {
try {
const todo_list = await ToDo.all({}); // MAGIC: this is how you trick the importer to reload modules
// that have changed. Since it uses a URL you can add a query
// to it, but that isn't parsed as the file. Using a date then
// tags the module as being "new" when it's still the same file
// TODO: maybe use fs timestamps instead?
// BUG: sometimes reload is too fast for vim and crashes
const controller = await import(`../controllers/todo.js?update=${new Date()}`);
// this is for exporing the problem of getting good error messages from templates const handler = new controller.Todo();
const result = nunjucks.render("templates/todo.html", const app = fastify(opts);
{todo_list, your_todos: "Your Todos"});
rep.code(200) // this is a sample that uses the handler we dynamic load
.type("text/html") // to handle the /todo but not sure how to work the actual
.send(result); // URL mappings for it. Also not sure about using classes
app.get("/todo", async (req, rep) => {
try {
await handler.get(req, rep);
} catch(error) { } catch(error) {
console.error(error); console.error(error);
console.error(error.stack); console.error(error.stack);
console.error(error.source); console.error(error.source);
} }
}); });
fastify.register(FastifyStatic, { app.register(FastifyStatic, {
root: path.join(path.resolve("."), 'static'), root: path.join(path.resolve("."), 'static'),
prefix: '/', // optional: default '/' prefix: '/', // optional: default '/'
constraints: {}, // optional: default {} constraints: {}, // optional: default {}
index: "index.html" index: "index.html"
}) });
// this is from fastify/restartable
app.addHook('onClose', async () => {
if(!app.closingRestartable) {
console.log('closing the app because of restart')
} else{
console.log('closing the app because server is stopping')
}
})
return app;
}
const app = await await restartable(configure, { logger: true});
const host = await app.listen({port: 3000});
const reload = () => {
if(CONFIGURED) {
app.restart();
}
}
export const main = async (arg, opts) => { export const main = async (arg, opts) => {
try { try {
await fastify.listen({port: 3000}); chokidar.watch(["lib","commands","controllers","static","migrations","tests"])
.on("add", path => reload())
.on("change", path => reload())
.on("unlink", path => reload())
.on("ready", () => CONFIGURED = true);
} catch(err) { } catch(err) {
fastify.log.error(err); fastify.log.error(err);
process.exit(1); process.exit(1);

106
package-lock.json generated

@ -21,6 +21,10 @@
}, },
"bin": { "bin": {
"bando": "bando.js" "bando": "bando.js"
},
"devDependencies": {
"@fastify/restartable": "github:fastify/restartable",
"chokidar": "^3.6.0"
} }
}, },
"node_modules/@fastify/accept-negotiator": { "node_modules/@fastify/accept-negotiator": {
@ -77,6 +81,15 @@
"helmet": "^7.0.0" "helmet": "^7.0.0"
} }
}, },
"node_modules/@fastify/restartable": {
"version": "2.2.0",
"resolved": "git+ssh://git@github.com/fastify/restartable.git#cd9c628df92283b8da2e5cff102d645c4c154a1b",
"dev": true,
"license": "MIT",
"dependencies": {
"fastify": "^4.16.3"
}
},
"node_modules/@fastify/send": { "node_modules/@fastify/send": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz",
@ -346,6 +359,19 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"devOptional": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/aproba": { "node_modules/aproba": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
@ -431,6 +457,15 @@
} }
] ]
}, },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"devOptional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/bindings": { "node_modules/bindings": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -610,6 +645,30 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"devOptional": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chownr": { "node_modules/chownr": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
@ -1052,6 +1111,20 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"optional": true "optional": true
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -1353,6 +1426,18 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"devOptional": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.13.1", "version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
@ -1945,6 +2030,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"devOptional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/npmlog": { "node_modules/npmlog": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
@ -2277,6 +2371,18 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
}, },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"devOptional": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/real-require": { "node_modules/real-require": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",

@ -18,5 +18,9 @@
"knex-paginate": "^3.1.1", "knex-paginate": "^3.1.1",
"nunjucks": "^3.2.4", "nunjucks": "^3.2.4",
"sqlite3": "^5.1.7" "sqlite3": "^5.1.7"
},
"devDependencies": {
"@fastify/restartable": "github:fastify/restartable",
"chokidar": "^3.6.0"
} }
} }

@ -3,7 +3,6 @@
<html> <html>
<head> <head>
<script src="/alpine.js"></script> <script src="/alpine.js"></script>
<script src="/fsm.js"></script>
<title>Bandolier2</title> <title>Bandolier2</title>
<style> <style>

@ -1,14 +1,7 @@
{% include "./header.html" %} {% include "./header.html" %}
<h1>Your TODOs</h1> <script>
</script>
<ol>
{% for todo in todo_list %}
<li>{{ todo.task }}</li>
{% else %}
<li>No TODO Items</li>
{% endfor %}
</ol>
<style> <style>
.disabled { .disabled {
@ -20,5 +13,17 @@
} }
</style> </style>
<h1>Your TODOs</h1>
<p>Welcome {{ name }}, here's your TODOs</p>
<ol>
{% for todo in todo_list %}
<li>{{ todo.task }}</li>
{% else %}
<li>No TODO Items</li>
{% endfor %}
</ol>
{% include "./footer.html" %} {% include "./footer.html" %}

Loading…
Cancel
Save