From ae6996372b561ae4fa0d83d4e88f8618c636d039 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 21 Feb 2024 14:18:26 -0500 Subject: [PATCH] Bring in helmet and cors support then setup for the nicer configuration and runner commands. --- bando | 3 ++ bin/bando.js | 85 ++++++++++++++++++++++++++++++++++++++++ bin/tester.js | 43 -------------------- {bin => commands}/app.js | 19 ++++++--- commands/tester.js | 53 +++++++++++++++++++++++++ lib/tooling.js | 20 ++++++++++ package-lock.json | 41 +++++++++++++++++++ package.json | 2 + 8 files changed, 218 insertions(+), 48 deletions(-) create mode 100755 bando create mode 100755 bin/bando.js delete mode 100644 bin/tester.js rename {bin => commands}/app.js (77%) create mode 100644 commands/tester.js create mode 100644 lib/tooling.js diff --git a/bando b/bando new file mode 100755 index 0000000..f619e83 --- /dev/null +++ b/bando @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +node ./bin/bando.js "$@" diff --git a/bin/bando.js b/bin/bando.js new file mode 100755 index 0000000..c84e8bd --- /dev/null +++ b/bin/bando.js @@ -0,0 +1,85 @@ +#!/usr/bin/env node + +import { Command } from "commander"; +// NOTE: this changed in node 18 to be node:url for...stupid +import { fileURLToPath } from "url"; +import { glob } from "../lib/tooling.js"; +import path from "path"; +import assert from "assert"; +import fs from "fs"; + +// BUG: this will load the commands but most of them assume the root +const __filename = fileURLToPath(import.meta.url); +const { dir } = path.parse(__filename); +let __dirname = path.resolve(dir); + +// now even exec commands will know where the project root is +process.env['PROJECT_ROOT'] = path.resolve(path.join(__dirname, "..")); + +const program = new Command(); +// remember that glob doesn't like windows \ +const command_dir = path.resolve(path.join(__dirname, "..", "commands")); + +if(!fs.existsSync(command_dir)) { + console.error("ERROR: there is no command directory at", command_dir); + process.exit(1); +} + +program + .name("bando") + .description("Command runner for the bando project.") + .version("0.1.0"); + +// only load the commands modules if any options are given + +if(process.argv.length === 2) { + console.log("COMMANDS:\n"); + const commands = glob(`${command_dir}/*.js`).map(cmd => { + console.log(path.parse(cmd).name); + }); + + + if(process.platform === "win32") { + console.log("\nUse './bando help' for full help."); + } else { + console.log("\nUse './bando.js help' for full help."); + } + +} else { + try { + const name = process.argv[2]; + const command = path.join(command_dir, `${name}.js`); + if(fs.existsSync(command)) { + const mod = await import(`file://${command}`); + + assert(mod.description, `export const description missing in ${command}`); + assert(mod.main, `export const main missing in ${command}`); + + const build = program.command(name) + .description(mod.description) + .action(mod.main); + + if(mod.options) { + mod.options.forEach(opt => build.option(...opt)); + } + + if(mod.required) { + mod.required.forEach(opt => build.requiredOption(...opt)); + } + + if(mod.argument) { + build.argument(...mod.argument); + } + } + } catch(error) { + console.log(error, `Loading file ${process.argv[2]}`); + } +} + +try { + program.parse(); +} catch(error) { + console.log("------------- ERROR, here's the stack trace."); + console.error(error.stack); + process.exit(1); +} diff --git a/bin/tester.js b/bin/tester.js deleted file mode 100644 index c3c6e42..0000000 --- a/bin/tester.js +++ /dev/null @@ -1,43 +0,0 @@ -import glob from "fast-glob"; -import path from "path"; - -const test_files = await glob("./tests/**/*_test?.js"); - -const stats = { - pass: 0, fail: 0 -} - -const errors = {} - -const report_error = (id, error) => { - console.error(error, `Running function ${id}`); -} - -for(let suite_name of test_files) { - const full_path = path.resolve(suite_name); - const test_module = await import(`file://${full_path}`); - - for(let [name, func] of Object.entries(test_module)) { - const id = `${suite_name}:${name}`; - console.log("------- RUNNING", id); - try { - await func(); - stats.pass += 1; - } catch(error) { - stats.fail += 1; - errors[id] = error; - report_error(func, id, error); - } - - console.log("\n"); - } -} - -if(Object.keys(errors).length > 0) { - console.log("!!!!!!!!!!!!!! ERRORS !!!!!!!!!!!!!!!!"); - for(let [id, error] of Object.entries(errors)) { - report_error(id, error); - } -} - -console.log("\nPASS: ", stats.pass, "FAIL: ", stats.fail); diff --git a/bin/app.js b/commands/app.js similarity index 77% rename from bin/app.js rename to commands/app.js index 598bab7..c531244 100644 --- a/bin/app.js +++ b/commands/app.js @@ -5,10 +5,17 @@ import { ToDo } from "../lib/models.js"; import fs from "fs"; import nunjucks from "nunjucks"; +export const description = "Runs your app."; + +export const options = []; + +export const required = []; + const fastify = Fastify({ logger: true }); + fastify.get("/todo", async (req, rep) => { try { const todo_list = await ToDo.all({}); @@ -34,9 +41,11 @@ fastify.register(FastifyStatic, { index: "index.html" }) -try { - await fastify.listen({port: 3000}); -} catch(err) { - fastify.log.error(err); - process.exit(1); +export const main = async (arg, opts) => { + try { + await fastify.listen({port: 3000}); + } catch(err) { + fastify.log.error(err); + process.exit(1); + } } diff --git a/commands/tester.js b/commands/tester.js new file mode 100644 index 0000000..89caa3d --- /dev/null +++ b/commands/tester.js @@ -0,0 +1,53 @@ +import glob from "fast-glob"; +import path from "path"; + +export const description = "Runs tests."; + +export const options = []; + +export const required = [ + ["--glob ", "Directory pattern to find test runners.", "./tests/**/*_test?.js"], +]; + +const report_error = (id, error) => { + console.error(error, `Running function ${id}`); +} + +export const main = async (opts) => { + const test_files = await glob(opts.glob); + + const stats = { + pass: 0, fail: 0 + } + + const errors = {} + + for(let suite_name of test_files) { + const full_path = path.resolve(suite_name); + const test_module = await import(`file://${full_path}`); + + for(let [name, func] of Object.entries(test_module)) { + const id = `${suite_name}:${name}`; + console.log("------- RUNNING", id); + try { + await func(); + stats.pass += 1; + } catch(error) { + stats.fail += 1; + errors[id] = error; + report_error(func, id, error); + } + + console.log("\n"); + } + } + + if(Object.keys(errors).length > 0) { + console.log("!!!!!!!!!!!!!! ERRORS !!!!!!!!!!!!!!!!"); + for(let [id, error] of Object.entries(errors)) { + report_error(id, error); + } + } + + console.log("\nPASS: ", stats.pass, "FAIL: ", stats.fail); +} diff --git a/lib/tooling.js b/lib/tooling.js new file mode 100644 index 0000000..eca1d01 --- /dev/null +++ b/lib/tooling.js @@ -0,0 +1,20 @@ +import fg from "fast-glob"; + +/* + Fixes some common problems with `fast-glob` on windows. It removes + leading "C:\" from paths and replaces all of the `\\` with `/`. + + __BUG__: This obviously won't work if you're on a different drive than C:. + + + `path string` -- The path/glob pattern to use. + */ +export const glob = (path) => { + if(process.platform === "win32") { + // fast-glob doesn't understand \\ or C:\ + const fixed_path = path.replace("C:","").replaceAll('\\', '/') + + return fg.sync(fixed_path); + } else { + return fg.sync(path); + } +} diff --git a/package-lock.json b/package-lock.json index 01e660a..9cda522 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "bandolier2", "version": "0.1.0", "dependencies": { + "@fastify/cors": "^9.0.1", + "@fastify/helmet": "^11.1.1", "@fastify/static": "^7.0.0", "commander": "^12.0.0", "fast-glob": "^3.3.2", @@ -39,6 +41,15 @@ "fast-uri": "^2.0.0" } }, + "node_modules/@fastify/cors": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", + "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==", + "dependencies": { + "fastify-plugin": "^4.0.0", + "mnemonist": "0.39.6" + } + }, "node_modules/@fastify/deepmerge": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", @@ -57,6 +68,15 @@ "fast-json-stringify": "^5.7.0" } }, + "node_modules/@fastify/helmet": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-11.1.1.tgz", + "integrity": "sha512-pjJxjk6SLEimITWadtYIXt6wBMfFC1I6OQyH/jYVCqSAn36sgAIFjeNiibHtifjCd+e25442pObis3Rjtame6A==", + "dependencies": { + "fastify-plugin": "^4.2.1", + "helmet": "^7.0.0" + } + }, "node_modules/@fastify/send": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", @@ -1179,6 +1199,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -1790,6 +1818,14 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/mnemonist": { + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", + "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", + "dependencies": { + "obliterator": "^2.0.1" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1956,6 +1992,11 @@ "node": ">= 6" } }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", diff --git a/package.json b/package.json index 072af0c..f6dced1 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ }, "scripts": {}, "dependencies": { + "@fastify/cors": "^9.0.1", + "@fastify/helmet": "^11.1.1", "@fastify/static": "^7.0.0", "commander": "^12.0.0", "fast-glob": "^3.3.2",