A tool for conquest of ATProto lexicons. https://jsr.io/@hotsocket/lexiconqueror

Local file inputs, added @lextype doc tag

+72 -29
+7 -1
README.md
··· 4 4 5 5 ## Info 6 6 7 - > To get started, run `deno run -A jsr:@hotsocket/lexiconqueror` 7 + ### Get Started 8 + 9 + - Run `deno run -A jsr:@hotsocket/lexiconqueror setup` in your project root 10 + - Configure imports under `"@/"` to point to your `outputDir` 11 + - Add `jsr:@hotsocket/dhmo` and `jsr:@hotsocket/atproto-common` to your project 12 + - Run `deno run -A jsr:@hotsocket/lexiconqueror download` to retrieve lexicons 13 + - Run `deno run -A jsr:@hotsocket/lexiconqueror convert` to convert your downloaded lexicons to TypeScript files 8 14 9 15 ### Links 10 16
+1 -1
deno.json
··· 1 1 { 2 2 "name": "@hotsocket/lexiconqueror", 3 - "version": "0.1.0", 3 + "version": "0.1.1", 4 4 "license": "GPL-3.0-or-later", 5 5 "tasks": { 6 6 "npm": "deno run -A npm.ts",
+7
info/jsdoc.md
··· 1 + # Additional JSDoc Tags 2 + 3 + This document outlines extra tags used in generated doc comments. 4 + 5 + | Tag | Associated Types | Description | 6 + | ----------------- | ---------------- | ------------------------------------------- | 7 + | `@lextype <type>` | (all) | The original type specified in the lexicon. |
+3 -6
npm.ts
··· 6 6 7 7 import { build, emptyDir } from "@deno/dnt"; 8 8 import { default as denoJson } from "./deno.json" with { type: "json" }; 9 - import { PackageMappedSpecifier, SpecifierMappings } from "@deno/dnt/transform"; 10 9 11 10 const OUT_DIR = "./npm"; 12 11 await emptyDir(OUT_DIR); 13 12 14 - const imports = denoJson.imports as Record<string, string>; 13 + // const imports = denoJson.imports as Record<string, string>; 15 14 16 15 await build({ 17 16 filterDiagnostic(diag) { ··· 46 45 }, 47 46 } as PackageJson, 48 47 compilerOptions: { 49 - lib: [ 50 - "DOM", 51 - "ESNext", 52 - ], 48 + lib: ["DOM", "ESNext"], 53 49 }, 54 50 // don't do commonjs, kids! 55 51 scriptModule: false, ··· 128 124 /** 129 125 * rest of the fields 130 126 */ 127 + //deno-lint-ignore no-explicit-any 131 128 [propertyName: string]: any; 132 129 }
+8 -2
src/config.ts
··· 36 36 z.templateLiteral(["at://", did_z]), 37 37 z.templateLiteral(["at://", z.hostname()]), 38 38 ]); 39 - /** zod union of git and at:// input formats */ 40 - export const anyInput = z.union([atInput, gitInput]); 39 + 40 + /** file: input format */ 41 + export const fileInput = z.union([ 42 + z.string().regex(/^file:.*/), 43 + ]); 44 + 45 + /** zod union of all input formats */ 46 + export const anyInput = z.union([atInput, gitInput, fileInput]); 41 47 42 48 /** lxq.json format */ 43 49 export const config_z = z.object({
+6
src/config_public.ts
··· 1 + /* 2 + * This Source Code Form is subject to the terms of the Mozilla Public 3 + * License, v. 2.0. If a copy of the MPL was not distributed with this 4 + * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 + */ 6 + 1 7 import z from "@zod/zod"; 2 8 import { config_z } from "./config.ts"; 3 9
+4 -3
src/generator/shared.ts
··· 85 85 ) { 86 86 const commentParts: string[] = []; 87 87 if (def.description) commentParts.push(def.description); 88 - if ( 89 - def.type == "query" || def.type == "procedure" || def.type == "subscription" 90 - ) commentParts.push("@" + def.type); 88 + // if ( 89 + // def.type == "query" || def.type == "procedure" || def.type == "subscription" 90 + // ) commentParts.push("@" + def.type); 91 + commentParts.push(`@lextype ${def.type}`); 91 92 if (def.type === "string" && "format" in def && (def as lex.String).format) { 92 93 commentParts.push("@format " + (def as lex.String).format); 93 94 }
+7 -1
src/internaltypes.ts
··· 36 36 did: z.string(), 37 37 }); 38 38 39 + export const resolvedFileInput = anyResolvedInput.safeExtend({ 40 + kind: z.literal("file"), 41 + path: z.string(), 42 + }); 43 + 39 44 export const resolvedGitInput = anyResolvedInput.safeExtend({ 40 45 kind: z.literal("git"), 41 46 url: z.url(), 42 47 dir: z.string(), 43 48 // rev: z.optional(z.string()), 44 - ref: z.optional(z.string()), 49 + ref: z.string(), 45 50 }); 46 51 47 52 export const resolvedInput_z = z.discriminatedUnion("kind", [ 48 53 resolvedAtInput, 49 54 resolvedGitInput, 55 + resolvedFileInput, 50 56 ]); 51 57 export type ResolvedInput = z.infer<typeof resolvedInput_z>;
+29 -15
src/run.ts
··· 46 46 const config = await configHelper(configPath); 47 47 const dataDir = config.dataDir; 48 48 console.log("⏳\tResolving inputs..."); 49 - const resolvedInputs: types.ResolvedInput[] = []; 50 - await Promise.all( 51 - Object.keys(config.inputs).map(async (name) => { 49 + const resolvedInputs: types.ResolvedInput[] = await Promise.all( 50 + Object.keys(config.inputs).map(async (name): Promise<types.ResolvedInput> => { 52 51 const input = cfg.anyInput.parse(config.inputs[name]); 53 52 let info = ""; 53 + const raw = { name: name, input: input }; 54 + let rv: types.ResolvedInput; 54 55 if (input.startsWith("at://")) { // pulls straight from published in repo 55 56 const inner = cfg.atInput.parse(input).substring(5); 56 57 let did: types.DID; ··· 69 70 let pds = svc.serviceEndpoint; 70 71 if (pds.endsWith("/")) pds = pds.substring(0, pds.length - 1); 71 72 info = `DID: '${did}', PDS: '${svc.serviceEndpoint}'`; 72 - resolvedInputs.push(types.resolvedAtInput.parse({ 73 + rv = { 73 74 raw: { name: name, input: input }, 74 75 kind: "at", 75 76 pds: pds, 76 77 did: did, 77 - })); 78 + }; 78 79 } else if (input.startsWith("git+")) { 79 80 const inner = new URL(cfg.gitInput.parse(input).substring(4)); 80 81 const repo = inner.origin + inner.pathname; 81 - resolvedInputs.push(types.resolvedGitInput.parse({ 82 + rv = { 83 + raw: raw, 82 84 kind: "git", 83 - raw: { 84 - name: name, 85 - input: input, 86 - }, 87 - dir: inner.searchParams.get("dir"), 88 - ref: inner.searchParams.get("ref") ?? undefined, 85 + dir: inner.searchParams.get("dir")!, 86 + ref: inner.searchParams.get("ref") ?? "main", 89 87 url: repo, 90 - } as z.infer<typeof types.resolvedGitInput>)); 88 + }; 89 + } else if (input.startsWith("file:")) { 90 + const dir = cfg.fileInput.parse(input).substring(5); 91 + if (!fss.existsSync(dir)) throw new Error(`Directory '${dir}' (input '${name}') does not exist`); 92 + const stat = await fs.stat(dir); 93 + if (!stat.isDirectory()) throw new Error(`'${dir}' (input '${name}') is not a directory`); 94 + rv = { 95 + raw: raw, 96 + kind: "file", 97 + path: path.resolve(dir), 98 + }; 99 + } else { 100 + throw new Error(`Input '${name}' is not valid?!`); 91 101 } 92 102 console.log(`✅\tResolved '${name}' (\`${input}\`)` + (info ? `to [${info}]` : "")); 103 + return rv; 93 104 }), 94 105 ); 95 106 ··· 117 128 // 91270de847fb763b1cb34ac8733c8bbc3991f820\trefs/heads/main => refs/heads/main 118 129 const refs = proc.output[1]!.split("\n").map((line) => line.split("\t")[1]); 119 130 // refs/heads/main => main 120 - const heads = refs.map((ref) => ref.substring(ref.lastIndexOf("/") + 1)); 131 + const heads = refs.map((ref) => ref!.substring(ref!.lastIndexOf("/") + 1)); 121 132 if (!heads.includes(input.ref)) { 122 133 throw new Error(`Could not find ref '${input.ref}' in git input '${input.raw.name}'`); 123 134 } ··· 178 189 if (proc.status != 0) { 179 190 throw new Error(`Error pulling git input '${input.raw.name}': ${proc.output[2]?.split("\n")[0]}`); 180 191 } 181 - if (proc.stdout.split("\n")[0].startsWith("Already up to date.")) { 192 + if (proc.stdout.split("\n")[0]!.startsWith("Already up to date.")) { 182 193 console.log(`ℹ️\tGit input '${input.raw.name}' already up to date`); 183 194 } else { 184 195 shouldCopy = true; ··· 200 211 await fs.cp(src, myPath, { recursive: true }); 201 212 console.log(`✅\tCopied Git input '${input.raw.name}' to '${myPath}'`); 202 213 } 214 + } else if (input.kind == "file") { 215 + await fs.cp(input.path, myPath, { recursive: true, force: true }); 216 + console.log(`✅\tCopied file input '${input.raw.name}' to '${myPath}'`); 203 217 } 204 218 }), 205 219 );