From 67b5f03a7a8536dae074ab5df465bfd4595b28a9 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Thu, 10 Aug 2023 22:37:53 -0400 Subject: [PATCH] hunt config --- cli.tsx | 307 +++++++++++++++++++++++++++++++++++------------------ deno.jsonc | 2 +- sub.tsx | 7 ++ sup.tsx | 37 +++++++ 4 files changed, 250 insertions(+), 103 deletions(-) create mode 100644 sub.tsx create mode 100644 sup.tsx diff --git a/cli.tsx b/cli.tsx index afce937..667df90 100644 --- a/cli.tsx +++ b/cli.tsx @@ -1,6 +1,41 @@ +import * as Env from "https://deno.land/std@0.194.0/dotenv/mod.ts"; +import * as Arg from "https://deno.land/std@0.194.0/flags/mod.ts"; import { parse as JSONC } from "https://deno.land/x/jsonct@v0.1.0/mod.ts"; const RootFile = new URL(`file://${Deno.cwd().replaceAll("\\", "/")}`).toString(); const RootHost = import.meta.resolve("./"); +let arg = await Arg.parse(Deno.args); +let env = await Env.load(); +const collect =async(inKey:string, inArg:Record, inEnv:Record):Promise=> +{ + const scanArg = inArg[inKey]; + const scanEnvFile = inEnv[inKey]; + const scanEnvDeno = Deno.env.get(inKey); + + if(scanArg) + { + console.log(`Using "${inKey}" from passed argument.`); + return scanArg; + } + if(scanEnvFile) + { + console.log(`Using "${inKey}" from .env file.`); + return scanEnvFile; + } + if(scanEnvDeno) + { + console.log(`Using "${inKey}" from environment variable.`); + return scanEnvDeno; + } + + const scanUser = prompt(`No "${inKey}" found. Enter one here:`); + if(!scanUser || scanUser?.length < 3) + { + console.log("Exiting..."); + Deno.exit(); + } + return scanUser; +}; + type ConfigCheck = {path?:string, text?:string, json?:Record>}; type ConfigCheckPair = [config:ConfigCheck, imports:ConfigCheck]; @@ -130,117 +165,185 @@ export async function Install(file:string, handler:(content:string)=>string = (s } } -console.info(`👷 Checking your project`) -try + + +export async function Check() { - let [config, imports] = await HuntConfig(); - if(!config.path) + console.info(`👷 Checking your project`) + try { - //const resp1 = await Prompt(" ! No Deno configuration found. Create one? [y/n]"); - const resp1 = confirm("🚨🚧 No Deno configuration found. Create one?"); - if(resp1) + let [config, imports] = await HuntConfig(); + if(!config.path) { - const resp2 = confirm("⚠️🚧 Do you also want to add starter files?"); - let replaceApp = "./path/to/app.tsx"; - let replaceApi = "./path/to/api.tsx"; - let replaceCommentApp = "// (required) module with default export ()=>React.JSX.Element"; - let replaceCommentApi = "// (optional) module with default export (req:Request, url:URL)=>Promise"; - if(resp2) + //const resp1 = await Prompt(" ! No Deno configuration found. Create one? [y/n]"); + const resp1 = confirm("🚨🚧 No Deno configuration found. Create one?"); + if(resp1) { - replaceApp = "./app.tsx"; - replaceApi = "./api.tsx"; - replaceCommentApp = ""; - replaceCommentApi = ""; - - await Install("app.tsx"); - await Install("api.tsx"); - } - else - { - // config initialized with no app or api - } - - await Install("deno.jsonc", (s)=>s - .replace("{{server}}", RootHost) - .replace("{{app}}", replaceApp) - .replace("{{api}}", replaceApi) - .replace("{{commentApp}}", replaceCommentApp) - .replace("{{commentApi}}", replaceCommentApi) - ); - - [config, imports] = await HuntConfig(); - } - else - { - throw("⛔ Config is required."); - } - - } - -/* -const inputString = `Some text 'imports' more text { some content }`; -const regex = /(?<=(['"`])imports\1[^{}]*{)/; - -const match = inputString.search(regex); - -if (match !== -1) { - console.log("Index of '{':", match); -} else { - console.log("'{': Not found."); -} -*/ - - if(config.json && imports.json?.imports) - { - const importMap = imports.json.imports as Record; - let changes = ``; - if(!importMap["react"]) - { - const resp = confirm(`🚨🔧 Import map has no specifier for React ("react"). Fix it now? (Will use Preact compat)`); - if(resp) - { - importMap["react"] = "https://esm.sh/preact@10.16.0/compat"; - changes += `"react": "https://esm.sh/preact@10.16.0/compat",\n`; - } - else - { - throw(`⛔ A React import ("react") is required.`); - } - } - if(!importMap[">able/"]) - { - const resp = confirm(`🚨🔧 Import map has no specifier for Able (">able/"). Fix it now?`); - if(resp) - { - importMap[">able/"] = RootHost; - changes += `">able": "${RootHost}",\n`; - } - else - { - throw(`⛔ The Able import (">able/") is required.`); - } - } - - const compOpts = imports.json.compilerOptions as Record; - if(compOpts) - { - const compJSX = compOpts["jsx"]; - const compJSXImportSource = compOpts["jsxImportSource"] - if(compJSX || compJSXImportSource) - { - if(!importMap["react/"]) + const resp2 = confirm("⚠️🚧 Do you also want to add starter files?"); + let replaceApp = "./path/to/app.tsx"; + let replaceApi = "./path/to/api.tsx"; + let replaceCommentApp = "// (required) module with default export ()=>React.JSX.Element"; + let replaceCommentApi = "// (optional) module with default export (req:Request, url:URL)=>Promise"; + if(resp2) { - //const resp = await Prompt(` ! Import map has no specifier for React ("react"). Add it now? [y/n]`); + replaceApp = "./app.tsx"; + replaceApi = "./api.tsx"; + replaceCommentApp = ""; + replaceCommentApi = ""; + + await Install("app.tsx"); + await Install("api.tsx"); } - } + else + { + // config initialized with no app or api + } + + await Install("deno.jsonc", (s)=>s + .replace("{{server}}", RootHost) + .replace("{{app}}", replaceApp) + .replace("{{api}}", replaceApi) + .replace("{{commentApp}}", replaceCommentApp) + .replace("{{commentApi}}", replaceCommentApi) + ); + + [config, imports] = await HuntConfig(); + } + else + { + throw("⛔ Config is required."); + } + + } + + /* + const inputString = `Some text 'imports' more text { some content }`; + const regex = /(?<=(['"`])imports\1[^{}]*{)/; + + const match = inputString.search(regex); + + if (match !== -1) { + console.log("Index of '{':", match); + } else { + console.log("'{': Not found."); + } + */ + + if(!config.json) + { + throw("⛔ Config is malformed."); + } + else if(!imports.json?.imports) + { + const resp = confirm(`🚨🔧 Configuration has no import map. Fix it now?`); + if(resp) + { + + } } + if(config.json && imports.json?.imports) + { + const importMap = imports.json.imports as Record; + let changes = ``; + if(!importMap["react"]) + { + const resp = confirm(`🚨🔧 Import map has no specifier for React ("react"). Fix it now? (Will use Preact compat)`); + if(resp) + { + importMap["react"] = "https://esm.sh/preact@10.16.0/compat"; + changes += `"react": "https://esm.sh/preact@10.16.0/compat",\n`; + } + else + { + throw(`⛔ A React import ("react") is required.`); + } + } + if(!importMap[">able/"]) + { + const resp = confirm(`🚨🔧 Import map has no specifier for Able (">able/"). Fix it now?`); + if(resp) + { + importMap[">able/"] = RootHost; + changes += `">able": "${RootHost}",\n`; + } + else + { + throw(`⛔ The Able import (">able/") is required.`); + } + } + + const compOpts = imports.json.compilerOptions as Record; + if(compOpts) + { + const compJSX = compOpts["jsx"]; + const compJSXImportSource = compOpts["jsxImportSource"] + if(compJSX || compJSXImportSource) + { + if(!importMap["react/"]) + { + //const resp = await Prompt(` ! Import map has no specifier for React ("react"). Add it now? [y/n]`); + } + } + } + + + } } + catch(e) + { + console.log(e, "\n (Able Exiting...)"); + Deno.exit(); + } + console.log(`🚗 Good to go!`); + } -catch(e) + +if(arg._.length) { - console.log(e, "\n (Able Exiting...)"); - Deno.exit(); -} -console.log(`🚗 Good to go!`); + + const [config, imports] = await HuntConfig(); + + switch(arg._[0]) + { + case "work" : + { + await SubProcess(["run", `--config=${config.path}`, RootHost+"run.tsx", "--dev", ...Deno.args]); + } + case "host" : + { + await SubProcess(["run", `--config=${config.path}`, RootHost+"run.tsx", ...Deno.args]); + } + case "push" : + { + let useToken = await collect("DENO_DEPLOY_TOKEN", arg, env); + let useProject = await collect("DENO_DEPLOY_PROJECT", arg, env); + + let scanProd:string[]|string|null = prompt(`Do you want to deploy to *production*?`); + if(scanProd) + { + scanProd = prompt(`Are you sure? This will update the live project at "${useProject}"`); + scanProd = scanProd ? ["--prod"] : []; + } + else + { + scanProd = []; + } + + await SubProcess([ + "run", + "-A", + "--no-lock", + `--config=${config.path}`, + "https://deno.land/x/deploy/deployctl.ts", + "deploy", + `--project=${useProject}`, + `--token=${useToken}`, + `--import-map=${imports.path}`, + RootHost+"run.tsx", + ...scanProd, + ...Deno.args]); + } + } +} \ No newline at end of file diff --git a/deno.jsonc b/deno.jsonc index b2935dc..8b9e02f 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -12,7 +12,7 @@ "local": "deno run -A --reload=http://localhost:4507 --no-lock ./run-local.tsx --port=1234", "serve": "deno run -A --reload=http://localhost:4507 --no-lock ./run-serve.tsx --port=1234", "cloud": "deno run -A --reload=http://localhost:4507 --no-lock ./run-deploy.tsx", - "debug": "deno run -A --reload=http://localhost:4507 --no-lock --inspect-wait ./run-serve.tsx --port=1234" + "debug": "deno run -A --no-lock --inspect-wait ./cli.tsx work --port=1234" }, "compilerOptions": diff --git a/sub.tsx b/sub.tsx new file mode 100644 index 0000000..b11c13a --- /dev/null +++ b/sub.tsx @@ -0,0 +1,7 @@ +import { parse } from "https://deno.land/std@0.194.0/flags/mod.ts"; + +let arg = parse(Deno.args); + +console.log(arg); + +console.log(Deno.env.get("super")) diff --git a/sup.tsx b/sup.tsx new file mode 100644 index 0000000..beef02b --- /dev/null +++ b/sup.tsx @@ -0,0 +1,37 @@ +import * as Env from "https://deno.land/std@0.194.0/dotenv/mod.ts"; +import * as Arg from "https://deno.land/std@0.194.0/flags/mod.ts"; + +let arg = Arg.parse(Deno.args); +let env = await Env.load(); + +Deno.env.set("super", "its super"); + +export async function SubProcess(args:string[]) +{ + const command = new Deno.Command( + `deno`, + { + args, + stdin: "piped", + stdout: "piped" + } + ); + + const child = command.spawn(); + + // open a file and pipe the subprocess output to it. + const writableStream = new WritableStream({ + write(chunk: Uint8Array): Promise { + Deno.stdout.write(chunk); + return Promise.resolve(); + }, + }); + child.stdout.pipeTo(writableStream); + + // manually close stdin + child.stdin.close(); + const status = await child.status; +} + + +SubProcess(["run", "-A", "sub.tsx", "keyword!", "--passed=yep"]); \ No newline at end of file