diff --git a/app.tsx b/app.tsx index 2308168..b92be06 100644 --- a/app.tsx +++ b/app.tsx @@ -1 +1,3 @@ -export default ()=>{}; \ No newline at end of file +export default ()=>
+

App!

+
; \ No newline at end of file diff --git a/checker.tsx b/checker.tsx index 3d7cdb4..81b791e 100644 --- a/checker.tsx +++ b/checker.tsx @@ -29,6 +29,7 @@ export async function HuntConfig() path = ".vscode/settings.json"; resp = await fetch(Root + "/" + path); json = await resp.json(); + path = json["deno.config"]; json = undefined; if(path) @@ -44,7 +45,7 @@ export async function HuntConfig() } } - if(text) + if(path) { try { @@ -52,7 +53,6 @@ export async function HuntConfig() } catch(e) { - // malformed config json = undefined; } } @@ -60,18 +60,27 @@ export async function HuntConfig() let imports:ConfigCheck = {}; if(json && json.imports) { + // config.imports imports.json = json; imports.text = JSON.stringify(json); imports.path = path; } else if(json && !json.imports && json.importMap) { + // config.importMap try { imports.path = json.importMap; resp = await fetch(Root + "/" + imports.path); imports.text = await resp.text(); - imports.json = JSONC(text); + try + { + imports.json = JSONC(imports.text); + } + catch(e) + { + imports.json = undefined; + } } catch(e) { @@ -82,7 +91,7 @@ export async function HuntConfig() return [{path, text, json}, imports] as ConfigCheckPair } -export async function Install(file:string, handler:(content:string)=>string = (s)=>s) +export async function Install(file:string, overrideName?:string, handler?:(content:string)=>string) { const pathFile = RootHost + "install__/" + file; @@ -99,135 +108,164 @@ export async function Install(file:string, handler:(content:string)=>string = (s { const resp = await fetch(pathFile); const text = await resp.text(); - await Deno.writeTextFile(Deno.cwd()+"/"+file, handler(text)); + const name = overrideName || file; + await Deno.writeTextFile(Deno.cwd()+"/"+name, handler ? handler(text) : text); } } - - export async function Check() { - console.info(`👷 Checking your project`); try { let [config, imports] = await HuntConfig(); + //console.log(config, imports); if(!config.path) { - if(confirm("🚨🚧 No Deno configuration found. Create a new one?")) - { - await Install("deno.jsonc"); - - Check(); - return; - } - else - { - throw("⛔ Configuration is required."); - } + console.log(`🛠️ No Deno configuration found. Creating "deno.jsonc" now.`); + await Deno.writeTextFile(Deno.cwd()+"/deno.jsonc", `{"imports":{}}`); + Check(); + return; } - - else if(!imports.json || !imports.json?.imports) + else if(!config.json) { - const resp = confirm(`🚨🔧 Configuration found, but has no import map. Fix it now?`); - if(resp) + if(confirm(`🚧 Deno configuration is malformed. Replace "${config.path}" with a new one?.`)) { - const text = config.text||""; - const startBracket = text.indexOf("{"); - - config.text = `{ - "imports": {}${startBracket < 0 ? "\n}\n" : ",\n"}` + text.substring(startBracket+1); - - await Deno.writeTextFile(Deno.cwd()+"/"+config.path, config.text); - + await Deno.writeTextFile(Deno.cwd()+"/"+config.path, `{"imports":{}}`); Check(); return; - } else { - throw("⛔ Import maps are required."); + throw("⛔ Invalid configuration."); } } + else if(!imports.json) + { + if(imports.path != config.path) + { + if(confirm(`🚧 External import map "${imports.path}" is missing or malformed. Replace it with defaults?.`)) + { + await Deno.writeTextFile(Deno.cwd()+"/"+imports.path, `{"imports":{}}`); + Check(); + return; + } + else + { + throw("⛔ Invalid configuration."); + } + } + } + else if(!imports.json?.imports) + { + imports.json.imports = {}; + } - if(config.json && imports.text && imports.json?.imports) + if(config.json && imports.json?.imports) { const importMap = imports.json.imports as Record; - let changes = ``; - - const match = imports.text.search(/(?<=(['"`])imports\1[^{}]*{)/); - const part1 = imports.text.substring(0, match); - const part2 = imports.text.substring(match); - - const bake =async()=> await Deno.writeTextFile(Deno.cwd()+"/"+config.path, part1 + changes + part2); + const bake =async(obj:ConfigCheck)=> await Deno.writeTextFile(Deno.cwd()+"/"+obj.path, JSON.stringify(obj.json, null, "\t")); if(!importMap["react"]) { - const resp = confirm(`🚨🔧 Import map has no specifier for React ("react"). Fix it now? (Will use Preact compat)`); - if(resp) - { - changes += `"react": "https://esm.sh/preact@10.16.0/compat",\n`; - if(!importMap["react/"]) - { - changes += `"react/": "https://esm.sh/preact@10.16.0/compat/",\n`; - } - await bake(); - } - else - { - throw(`⛔ A React import ("react") is required.`); - } + console.log(`🛠️ Adding React import specifier ("react")`); + importMap["react"] = `https://esm.sh/preact@10.16.0/compat`; + importMap["react/"] = `https://esm.sh/preact@10.16.0/compat/`; + await bake(imports); + console.log(`🚦 NOTE: Deno will need to cache "react" for intellisense to work properly.`) + } if(!importMap[">able/"]) { - const resp = confirm(`🚨🔧 Import map has no specifier for Able (">able/"). Fix it now?`); - if(resp) - { - changes += `">able/": "${RootHost}",\n`; - await bake(); - } - else - { - throw(`⛔ The Able import (">able/") is required.`); - } + console.log(`🛠️ Adding Able import specifier (">able/").`); + importMap[">able/"] = `${RootHost}`; + await bake(imports); } + if(!importMap[">able/app.tsx"]) { - const resp = confirm(`🚨🔧 Import map has no specifier for your starter app (">able/app.tsx"). Fix it now?`); + const resp = confirm(`🤔 OPTIONAL: Import map has no specifier for your starter app (">able/app.tsx"). Create one?`); if(resp) { - changes += `">able/app.tsx": "./app.tsx",\n`; - await bake(); + importMap[">able/app.tsx"] = `./app.tsx`; + await bake(imports); await Install("app.tsx"); } - else + } + else + { + try { - throw(`⛔ The "starter app" import (">able/app.tsx") is required.`); + const app = await import(importMap[">able/app.tsx"]); + // @ts-ignore + const result = app.default().$$typeof; + } + catch(e) + { + console.log(e); + if(confirm(`🚧 Your starter app ("${importMap[">able/app.tsx"]}") does not export a default function that returns VDOM nodes. Replace it?`)) + { + await Install("app.tsx", importMap[">able/app.tsx"]); + } + else + { + throw("⛔ Starter app has incorrect export types."); + } } } + if(!importMap[">able/api.tsx"]) { - const resp = confirm(`🚨🔧 OPTIONAL: Import map has no specifier for your backend app (">able/api.tsx"). Fix it now?`); + const resp = confirm(`🤔 OPTIONAL: Import map has no specifier for your starter backend app (">able/api.tsx"). Create one?`); if(resp) { - changes += `">able/api.tsx": "./api.tsx",\n`; - await bake(); + importMap[">able/api.tsx"] = "./api.tsx"; + await bake(imports); await Install("api.tsx"); } } - - - const compOpts = imports.json.compilerOptions as Record; - if(compOpts) + else { - const compJSX = compOpts["jsx"]; - const compJSXImportSource = compOpts["jsxImportSource"] - if(compJSX || compJSXImportSource) + try { - if(!importMap["react/"]) + const api = await import(importMap[">able/api.tsx"]); + const result = api.default(new Request(new URL("https://fake-deno-testing-domain.com/"))); + } + catch(e) + { + if(confirm(`🚧 Your starter backend app ("${importMap[">able/api.tsx"]}") does not export a default function that accepts a Request. Replace it?`)) { - //const resp = await Prompt(` ! Import map has no specifier for React ("react"). Add it now? [y/n]`); + await Install("api.tsx", importMap[">able/api.tsx"]); } - } + else + { + throw("⛔ Starter backend app has incorrect export types."); + } + } + } + + + const options = + { + "lib": ["deno.window", "dom", "dom.asynciterable"], + "jsx": "react-jsx", + "jsxImportSource": "react" + } + + const compOpts = config.json.compilerOptions as Record || {}; + const compJSX = compOpts.jsx == options.jsx; + const compJSXImportSource = compOpts.jsxImportSource == options.jsxImportSource; + const compLib:string[] = compOpts.lib as string[] || []; + let compLibHasAll = true; + options.lib.forEach(item=> !compLib.includes(item) && (compLibHasAll = false)) + + if(!compOpts || !compJSX || !compJSXImportSource || !compLibHasAll) + { + console.log(`🛠️ Adding values to "compilerOptions" configuration.`); + compOpts.jsx = options.jsx; + compOpts.jsxImportSource = options.jsxImportSource; + compOpts.lib = [...compLib, ...options.lib]; + config.json.compilerOptions = compOpts; + await bake(config); } @@ -242,5 +280,4 @@ export async function Check() } - Check(); \ No newline at end of file diff --git a/deno.jsonc b/deno.jsonc index 6c923cd..8b3930c 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,16 @@ { - "imports": {}, - - //imports? - + "imports": { + "react": "https://esm.sh/preact@10.16.0/compat", + "react/": "https://esm.sh/preact@10.16.0/compat/", + ">able/": "file:///C:/Web%20Projects/able-baker/" + }, + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react", + "lib": [ + "deno.window", + "dom", + "dom.asynciterable" + ] + } } \ No newline at end of file diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 2be9e7a..0000000 --- a/deno.lock +++ /dev/null @@ -1,8 +0,0 @@ -{ - "version": "2", - "remote": { - "https://deno.land/x/jsonct@v0.1.0/mod.ts": "dba7e7f3529be6369f5c718e3a18b69f15ffa176006d2a7565073ce6c5bd9f3f", - "https://deno.land/x/jsonct@v0.1.0/src/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/x/jsonct@v0.1.0/src/parse.ts": "a3a016822446b0584b40bae9098df480db5590a9915c9e3c623ba2801cf1b8df" - } -} diff --git a/deno__.json b/deno__.json index 1e16461..cc26f5d 100644 --- a/deno__.json +++ b/deno__.json @@ -8,7 +8,7 @@ "react":"https://esm.sh/preact@10.15.1/compat", "react/":"https://esm.sh/preact@10.15.1/compat/", "react-original":"https://esm.sh/preact@10.15.1/compat", - ">able/": "http://localhost:4507/" + }, "tasks": { diff --git a/install__/app.tsx b/install__/app.tsx index 2308168..b92be06 100644 --- a/install__/app.tsx +++ b/install__/app.tsx @@ -1 +1,3 @@ -export default ()=>{}; \ No newline at end of file +export default ()=>
+

App!

+
; \ No newline at end of file