diff --git a/codegen/codecVisitor.ts b/codegen/codecVisitor.ts index 585dcaed6..884b8b7a7 100644 --- a/codegen/codecVisitor.ts +++ b/codegen/codecVisitor.ts @@ -1,7 +1,29 @@ import * as M from "../frame_metadata/mod.ts"; -import { Decl, getCodecPath, getName, getRawCodecPath, S } from "./utils.ts"; +import { Decl, Files, getCodecPath, getName, getRawCodecPath, importSource, S } from "./utils.ts"; + +export function createCodecVisitor( + tys: M.Ty[], + decls: Decl[], + typeVisitor: M.TyVisitor, + files: Files, +) { + ["import { $, $null, $era } from", S.string(importSource)]; + const namespaceImports = new Set(); + const codecs: S[] = []; + + files.set("codecs.ts", { + getContent: () => [ + "\n", + ["import { ChainError, BitSequence, Era, $, $era, $null } from", S.string(importSource)], + [`import type * as t from "./mod.ts"`], + ...codecs, + [ + "export const _all: $.AnyCodec[] =", + S.array(tys.map((ty) => getName(getRawCodecPath(ty)))), + ], + ], + }); -export function createCodecVisitor(tys: M.Ty[], decls: Decl[], typeVisitor: M.TyVisitor) { const compactCodecVisitor = new M.TyVisitor(tys, { unitStruct: () => "$null", wrapperStruct(_, inner) { @@ -59,7 +81,9 @@ export function createCodecVisitor(tys: M.Ty[], decls: Decl[], typeVisitor: M.Ty }, result(ty, ok, err) { return addCodecDecl(ty, ["$.result(", this.visit(ok), ",", [ - `$.instance(ChainError<${typeVisitor.visit(err)}>, ["value", `, + "$.instance(ChainError<", + fixType(typeVisitor.visit(err)), + `>, ["value", `, this.visit(err), "])", ], ")"]); @@ -141,22 +165,22 @@ export function createCodecVisitor(tys: M.Ty[], decls: Decl[], typeVisitor: M.Ty return addCodecDecl(ty, "$era"); }, circular(ty) { - return ["$.deferred(() =>", getRawCodecPath(ty), ")"]; + return ["$.deferred(() =>", getName(getRawCodecPath(ty)), ")"]; }, }); function addCodecDecl(ty: M.Ty, value: S) { const rawPath = getRawCodecPath(ty); - decls.push({ - path: "_codec.", // no sorting - code: [ - ["export const", getName(rawPath)], - ": $.Codec<", - typeVisitor.visit(ty), - "> =", - value, - ], - }); + if (ty.path.length > 1) { + namespaceImports.add(ty.path[0]!); + } + codecs.push([ + ["export const", getName(rawPath)], + ": $.Codec<", + fixType(typeVisitor.visit(ty)), + "> =", + value, + ]); const path = getCodecPath(tys, ty); if (path !== rawPath && path !== value && !decls.some((x) => x.path === path)) { decls.push({ @@ -170,6 +194,13 @@ export function createCodecVisitor(tys: M.Ty[], decls: Decl[], typeVisitor: M.Ty ], }); } - return rawPath; + return getName(rawPath); + } + + function fixType(type: S) { + return S.toString(type).replace( + /\b([\w\$]+\.[\w\.$]+|u\d+|Compact)\b/g, + (x) => "t." + x, + ); } } diff --git a/codegen/codegen.ts b/codegen/codegen.ts index 203f702af..0c3d4bae8 100644 --- a/codegen/codegen.ts +++ b/codegen/codegen.ts @@ -1,55 +1,69 @@ import { tsFormatter } from "../deps/dprint.ts"; import { parse } from "../deps/std/flags.ts"; +import * as path from "../deps/std/path.ts"; import * as M from "../frame_metadata/mod.ts"; import { createCodecVisitor } from "./codecVisitor.ts"; import { genMetadata } from "./genMetadata.ts"; import { createTypeVisitor } from "./typeVisitor.ts"; -import { Decl, printDecls, S } from "./utils.ts"; - -const importSource = new URL("../mod.ts", import.meta.url).toString(); +import { Decl, Files, importSource, printDecls, S } from "./utils.ts"; if (import.meta.main) { const args = parse(Deno.args, { string: ["metadata", "output"], }); if (args.help || args["?"]) { - console.log("Usage: codegen --metadata --output "); + console.log("Usage: codegen --metadata --output "); Deno.exit(0); } if (!args.metadata) throw new Error("Must specify metadata file"); - if (!args.output) throw new Error("Must specify output file"); - const { metadata: metadataFile, output: outputFile } = args; - const metadata = M.fromPrefixedHex(await Deno.readTextFile(metadataFile!)); + if (!args.output) throw new Error("Must specify output dir"); + const { metadata: metadataFile, output: outputDir } = args; + const metadata = M.fromPrefixedHex(await Deno.readTextFile(metadataFile)); const output = codegen(metadata); - try { - const formatted = tsFormatter.formatText("gen.ts", output); - await Deno.writeTextFile(outputFile!, formatted); - } catch (e) { - await Deno.writeTextFile(outputFile!, output); - throw e; + const errors = []; + await Deno.mkdir(outputDir, { recursive: true }); + for (const [relativePath, file] of output.entries()) { + const outputPath = path.join(outputDir, relativePath); + const content = S.toString(file.getContent()); + try { + const formatted = tsFormatter.formatText("gen.ts", content); + await Deno.writeTextFile(outputPath, formatted); + } catch (e) { + await Deno.writeTextFile(outputPath, content); + errors.push(e); + } + } + if (errors.length) { + console.error(errors); + Deno.exit(1); } } -export function codegen(metadata: M.Metadata) { +export function codegen(metadata: M.Metadata): Files { const decls: Decl[] = []; const { tys } = metadata; + const files: Files = new Map(); + decls.push({ path: "_", code: [ - "import { ChainError, BitSequence, Era, $, $null, $era } from", - S.string(importSource), + "\n", + ["import { ChainError, BitSequence, Era, $ } from", S.string(importSource)], + [`import * as _codec from "./codecs.ts"`], ], }); const typeVisitor = createTypeVisitor(tys, decls); - const codecVisitor = createCodecVisitor(tys, decls, typeVisitor); + const codecVisitor = createCodecVisitor(tys, decls, typeVisitor, files); typeVisitor.tys.map((x) => typeVisitor.visit(x)); codecVisitor.tys.map((x) => codecVisitor.visit(x)); - genMetadata(metadata, decls, codecVisitor); + genMetadata(metadata, decls); + + files.set("mod.ts", { getContent: () => printDecls(decls) }); - return printDecls(decls); + return files; } diff --git a/codegen/genMetadata.ts b/codegen/genMetadata.ts index 8bbdd1d8f..96764db44 100644 --- a/codegen/genMetadata.ts +++ b/codegen/genMetadata.ts @@ -1,7 +1,7 @@ import * as M from "../frame_metadata/mod.ts"; import { Decl, getPath, getRawCodecPath, makeDocComment, S } from "./utils.ts"; -export function genMetadata(metadata: M.Metadata, decls: Decl[], codecVisitor: M.TyVisitor) { +export function genMetadata(metadata: M.Metadata, decls: Decl[]) { const { tys, extrinsic, pallets } = metadata; const isUnitVisitor = new M.TyVisitor(tys, { @@ -56,11 +56,11 @@ export function genMetadata(metadata: M.Metadata, decls: Decl[], codecVisitor: M "key", entry.type === "Map" ? entry.hashers.length === 1 - ? ["$.tuple(", codecVisitor.visit(entry.key), ")"] - : codecVisitor.visit(entry.key) + ? ["$.tuple(", getRawCodecPath(tys[entry.key]!), ")"] + : getRawCodecPath(tys[entry.key]!) : "[]", ], - ["value", codecVisitor.visit(entry.value)], + ["value", getRawCodecPath(tys[entry.value]!)], ), ], }); @@ -96,15 +96,12 @@ export function genMetadata(metadata: M.Metadata, decls: Decl[], codecVisitor: M decls.push({ path: "_metadata.types", - code: [ - "export const types: $.AnyCodec[] =", - S.array(codecVisitor.tys.map((ty) => getRawCodecPath(ty))), - ], + code: "export const types = _codec._all", }); function getExtrasCodec(xs: [string, number][]) { return S.array( - xs.filter((x) => !isUnitVisitor.visit(x[1])).map((x) => codecVisitor.visit(x[1])), + xs.filter((x) => !isUnitVisitor.visit(x[1])).map((x) => getRawCodecPath(tys[x[1]]!)), ); } } diff --git a/codegen/utils.ts b/codegen/utils.ts index 2c5e81ce8..7b940d26b 100644 --- a/codegen/utils.ts +++ b/codegen/utils.ts @@ -1,4 +1,10 @@ import * as M from "../frame_metadata/mod.ts"; + +export const importSource = new URL("../mod.ts", import.meta.url).toString(); + +export type File = { getContent: () => S }; +export type Files = Map; + export type S = string | number | S[]; export namespace S {