UNPKG

@codama/renderers-js-umi

Version:

JavaScript renderer compatible with the Umi framework

1,228 lines (1,218 loc) 77.4 kB
// src/ImportMap.ts var DEFAULT_MODULE_MAP = { errors: "../errors", shared: "../shared", types: "../types", umi: "@metaplex-foundation/umi", umiSerializers: "@metaplex-foundation/umi/serializers" }; var ImportMap = class { _imports = /* @__PURE__ */ new Map(); _aliases = /* @__PURE__ */ new Map(); add(module, imports) { const currentImports = this._imports.get(module) ?? /* @__PURE__ */ new Set(); const newImports = typeof imports === "string" ? [imports] : imports; newImports.forEach((i) => currentImports.add(i)); this._imports.set(module, currentImports); return this; } remove(module, imports) { const currentImports = this._imports.get(module) ?? /* @__PURE__ */ new Set(); const importsToRemove = typeof imports === "string" ? [imports] : imports; importsToRemove.forEach((i) => currentImports.delete(i)); if (currentImports.size === 0) { this._imports.delete(module); } else { this._imports.set(module, currentImports); } return this; } mergeWith(...others) { others.forEach((other) => { other._imports.forEach((imports, module) => { this.add(module, imports); }); other._aliases.forEach((aliases, module) => { Object.entries(aliases).forEach(([name, alias]) => { this.addAlias(module, name, alias); }); }); }); return this; } mergeWithManifest(manifest) { return this.mergeWith(manifest.strictImports, manifest.looseImports, manifest.serializerImports); } addAlias(module, name, alias) { const currentAliases = this._aliases.get(module) ?? {}; currentAliases[name] = alias; this._aliases.set(module, currentAliases); return this; } isEmpty() { return this._imports.size === 0; } toString(dependencies) { const dependencyMap = { ...DEFAULT_MODULE_MAP, ...dependencies }; const importStatements = [...this._imports.entries()].map(([module, imports]) => { const mappedModule = dependencyMap[module] ?? module; return [mappedModule, module, imports]; }).sort(([a], [b]) => { const aIsRelative = a.startsWith("."); const bIsRelative = b.startsWith("."); if (aIsRelative && !bIsRelative) return 1; if (!aIsRelative && bIsRelative) return -1; return a.localeCompare(b); }).map(([mappedModule, module, imports]) => { const aliasMap = this._aliases.get(module) ?? {}; const joinedImports = [...imports].sort().map((i) => aliasMap[i] ? `${i} as ${aliasMap[i]}` : i).join(", "); return `import { ${joinedImports} } from '${mappedModule}';`; }); return importStatements.join("\n"); } }; // src/getRenderMapVisitor.ts import { logWarn } from "@codama/errors"; import { camelCase as camelCase5, definedTypeNode as definedTypeNode2, getAllAccounts, getAllDefinedTypes, getAllInstructionArguments, getAllInstructionsWithSubs, getAllPrograms, isDataEnum, isNode as isNode4, isNodeFilter, parseOptionalAccountStrategy, pascalCase as pascalCase4, resolveNestedTypeNode as resolveNestedTypeNode3, structTypeNodeFromInstructionArgumentNodes as structTypeNodeFromInstructionArgumentNodes3, VALUE_NODES } from "@codama/nodes"; import { addToRenderMap, createRenderMap, mergeRenderMaps } from "@codama/renderers-core"; import { extendVisitor as extendVisitor2, getByteSizeVisitor, getResolvedInstructionInputsVisitor, LinkableDictionary as LinkableDictionary2, NodeStack as NodeStack2, pipe as pipe2, recordLinkablesOnFirstVisitVisitor, recordNodeStackVisitor as recordNodeStackVisitor2, staticVisitor as staticVisitor2, visit as visit4 } from "@codama/visitors-core"; // src/ContextMap.ts var ContextMap = class { _interfaces = /* @__PURE__ */ new Set(); add(contextInterface) { if (Array.isArray(contextInterface)) { contextInterface.forEach((i) => this._interfaces.add(i)); } else { this._interfaces.add(contextInterface); } return this; } remove(contextInterface) { if (Array.isArray(contextInterface)) { contextInterface.forEach((i) => this._interfaces.delete(i)); } else { this._interfaces.delete(contextInterface); } return this; } mergeWith(...others) { others.forEach((other) => this.add([...other._interfaces])); return this; } isEmpty() { return this._interfaces.size === 0; } toString() { const contextInterfaces = [...this._interfaces].sort().map((i) => `"${i}"`).join(" | "); return `Pick<Context, ${contextInterfaces}>`; } }; // src/getTypeManifestVisitor.ts import { CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, CodamaError as CodamaError2 } from "@codama/errors"; import { camelCase as camelCase3, isInteger, isNode as isNode2, isScalarEnum, isUnsignedInteger, parseDocs, pascalCase as pascalCase2, REGISTERED_TYPE_NODE_KINDS, REGISTERED_VALUE_NODE_KINDS, resolveNestedTypeNode as resolveNestedTypeNode2, structFieldTypeNode, structTypeNode, structTypeNodeFromInstructionArgumentNodes as structTypeNodeFromInstructionArgumentNodes2 } from "@codama/nodes"; import { extendVisitor, NodeStack, pipe, recordNodeStackVisitor, staticVisitor, visit as visit2 } from "@codama/visitors-core"; // src/utils/codecs.ts import { getBase16Encoder, getBase58Encoder, getBase64Encoder, getUtf8Encoder } from "@solana/codecs-strings"; function getBytesFromBytesValueNode(node) { switch (node.encoding) { case "utf8": return getUtf8Encoder().encode(node.data); case "base16": return getBase16Encoder().encode(node.data); case "base58": return getBase58Encoder().encode(node.data); case "base64": default: return getBase64Encoder().encode(node.data); } } // src/utils/customData.ts import { camelCase, definedTypeLinkNode, definedTypeNode, isNode, structTypeNodeFromInstructionArgumentNodes } from "@codama/nodes"; var parseCustomDataOptions = (customDataOptions, defaultSuffix) => new Map( customDataOptions.map((o) => { const options = typeof o === "string" ? { name: o } : o; const importAs = camelCase(options.importAs ?? `${options.name}${defaultSuffix}`); const importFrom = options.importFrom ?? "hooked"; return [ camelCase(options.name), { extract: options.extract ?? false, extractAs: options.extractAs ? camelCase(options.extractAs) : importAs, importAs, importFrom, linkNode: definedTypeLinkNode(importAs) } ]; }) ); var getDefinedTypeNodesToExtract = (nodes, parsedCustomDataOptions) => nodes.flatMap((node) => { const options = parsedCustomDataOptions.get(node.name); if (!options || !options.extract) return []; if (isNode(node, "accountNode")) { return [definedTypeNode({ name: options.extractAs, type: { ...node.data } })]; } return [ definedTypeNode({ name: options.extractAs, type: structTypeNodeFromInstructionArgumentNodes(node.arguments) }) ]; }); // src/utils/gpaField.ts import { resolveNestedTypeNode } from "@codama/nodes"; import { visit } from "@codama/visitors-core"; function getGpaFieldsFromAccount(node, sizeVisitor) { let offset = 0; const struct = resolveNestedTypeNode(node.data); return struct.fields.map((field) => { const fieldOffset = offset; if (offset !== null) { const newOffset = visit(field.type, sizeVisitor); offset = newOffset !== null ? offset + newOffset : null; } return { name: field.name, offset: fieldOffset, type: field.type }; }); } // src/utils/linkOverrides.ts import { CODAMA_ERROR__UNEXPECTED_NODE_KIND, CodamaError } from "@codama/errors"; function getImportFromFactory(overrides, customAccountData, customInstructionData) { const customDataOverrides = Object.fromEntries( [...customAccountData.values(), ...customInstructionData.values()].map(({ importFrom, importAs }) => [ importAs, importFrom ]) ); const linkOverrides = { accounts: overrides.accounts ?? {}, definedTypes: { ...customDataOverrides, ...overrides.definedTypes }, instructions: overrides.instructions ?? {}, pdas: overrides.pdas ?? {}, programs: overrides.programs ?? {}, resolvers: overrides.resolvers ?? {} }; return (node) => { const kind = node.kind; switch (kind) { case "accountLinkNode": return linkOverrides.accounts[node.name] ?? "generatedAccounts"; case "definedTypeLinkNode": return linkOverrides.definedTypes[node.name] ?? "generatedTypes"; case "instructionLinkNode": return linkOverrides.instructions[node.name] ?? "generatedInstructions"; case "pdaLinkNode": return linkOverrides.pdas[node.name] ?? "generatedAccounts"; case "programLinkNode": return linkOverrides.programs[node.name] ?? "generatedPrograms"; case "resolverValueNode": return linkOverrides.resolvers[node.name] ?? "hooked"; default: throw new CodamaError(CODAMA_ERROR__UNEXPECTED_NODE_KIND, { expectedKinds: [ "AccountLinkNode", "DefinedTypeLinkNode", "InstructionLinkNode", "PdaLinkNode", "ProgramLinkNode", "resolverValueNode" ], kind, node }); } }; } // src/utils/render.ts import { dirname as pathDirname, join } from "path"; import { fileURLToPath } from "url"; import { camelCase as camelCase2, kebabCase, pascalCase, snakeCase, titleCase } from "@codama/nodes"; import nunjucks from "nunjucks"; function jsDocblock(docs) { if (docs.length <= 0) return ""; if (docs.length === 1) return `/** ${docs[0]} */ `; const lines = docs.map((doc) => ` * ${doc}`); return `/** ${lines.join("\n")} */ `; } var render = (template, context, options) => { const dirname = true ? pathDirname(fileURLToPath(import.meta.url)) : __dirname; const templates = false ? join(dirname, "..", "..", "public", "templates") : join(dirname, "templates"); const env = nunjucks.configure(templates, { autoescape: false, trimBlocks: true, ...options }); env.addFilter("pascalCase", pascalCase); env.addFilter("camelCase", camelCase2); env.addFilter("snakeCase", snakeCase); env.addFilter("kebabCase", kebabCase); env.addFilter("titleCase", titleCase); env.addFilter("jsDocblock", jsDocblock); return env.render(template, context); }; // src/getTypeManifestVisitor.ts function typeManifest() { return { isEnum: false, looseImports: new ImportMap(), looseType: "", serializer: "", serializerImports: new ImportMap(), strictImports: new ImportMap(), strictType: "", value: "", valueImports: new ImportMap() }; } function getTypeManifestVisitor(input) { const { linkables, nonScalarEnums, customAccountData, customInstructionData, getImportFrom } = input; const stack = input.stack ?? new NodeStack(); let parentName = null; let parentSize = null; return pipe( staticVisitor( () => ({ isEnum: false, looseImports: new ImportMap(), looseType: "", serializer: "", serializerImports: new ImportMap(), strictImports: new ImportMap(), strictType: "", value: "", valueImports: new ImportMap() }), { keys: [ ...REGISTERED_TYPE_NODE_KINDS, ...REGISTERED_VALUE_NODE_KINDS, "definedTypeLinkNode", "definedTypeNode", "accountNode", "instructionNode" ] } ), (v) => extendVisitor(v, { visitAccount(account, { self }) { parentName = { loose: `${pascalCase2(account.name)}AccountDataArgs`, strict: `${pascalCase2(account.name)}AccountData` }; const link = customAccountData.get(account.name)?.linkNode; const manifest = link ? visit2(link, self) : visit2(account.data, self); parentName = null; return manifest; }, visitAmountType(amountType, { self }) { const numberManifest = visit2(amountType.number, self); const resolvedNode = resolveNestedTypeNode2(amountType.number); if (!isUnsignedInteger(resolvedNode)) { throw new Error( `Amount wrappers can only be applied to unsigned integer types. Got format [${resolvedNode.format}].` ); } const { unit, decimals } = amountType; const idAndDecimals = `'${unit ?? "Unknown"}', ${decimals}`; const isSolAmount = unit === "SOL" && decimals === 9; const amountTypeString = isSolAmount ? "SolAmount" : `Amount<${idAndDecimals}>`; const amountImport = isSolAmount ? "SolAmount" : "Amount"; numberManifest.strictImports.add("umi", amountImport); numberManifest.looseImports.add("umi", amountImport); numberManifest.serializerImports.add("umi", "mapAmountSerializer"); return { ...numberManifest, looseType: amountTypeString, serializer: `mapAmountSerializer(${numberManifest.serializer}, ${idAndDecimals})`, strictType: amountTypeString }; }, visitArrayType(arrayType, { self }) { const childManifest = visit2(arrayType.item, self); childManifest.serializerImports.add("umiSerializers", "array"); const sizeOption = getArrayLikeSizeOption(arrayType.count, childManifest, self); const options = sizeOption ? `, { ${sizeOption} }` : ""; return { ...childManifest, looseType: `Array<${childManifest.looseType}>`, serializer: `array(${childManifest.serializer + options})`, strictType: `Array<${childManifest.strictType}>` }; }, visitArrayValue(node, { self }) { const list = node.items.map((value) => visit2(value, self)); return { ...typeManifest(), value: `[${list.map((c) => c.value).join(", ")}]`, valueImports: new ImportMap().mergeWith(...list.map((c) => c.valueImports)) }; }, visitBooleanType(booleanType, { self }) { const looseImports = new ImportMap(); const strictImports = new ImportMap(); const serializerImports = new ImportMap().add("umiSerializers", "bool"); let sizeSerializer = ""; const resolvedSize = resolveNestedTypeNode2(booleanType.size); if (resolvedSize.format !== "u8" || resolvedSize.endian !== "le") { const size = visit2(booleanType.size, self); looseImports.mergeWith(size.looseImports); strictImports.mergeWith(size.strictImports); serializerImports.mergeWith(size.serializerImports); sizeSerializer = `{ size: ${size.serializer} }`; } return { isEnum: false, looseImports, looseType: "boolean", serializer: `bool(${sizeSerializer})`, serializerImports, strictImports, strictType: "boolean", value: "", valueImports: new ImportMap() }; }, visitBooleanValue(node) { return { ...typeManifest(), value: JSON.stringify(node.boolean) }; }, visitBytesType(_bytesType, { self }) { const strictImports = new ImportMap(); const looseImports = new ImportMap(); const serializerImports = new ImportMap().add("umiSerializers", "bytes"); const options = []; if (typeof parentSize === "number") { options.push(`size: ${parentSize}`); } else if (parentSize) { const prefix = visit2(parentSize, self); strictImports.mergeWith(prefix.strictImports); looseImports.mergeWith(prefix.looseImports); serializerImports.mergeWith(prefix.serializerImports); options.push(`size: ${prefix.serializer}`); } const optionsAsString = options.length > 0 ? `{ ${options.join(", ")} }` : ""; return { isEnum: false, looseImports, looseType: "Uint8Array", serializer: `bytes(${optionsAsString})`, serializerImports, strictImports, strictType: "Uint8Array", value: "", valueImports: new ImportMap() }; }, visitBytesValue(node) { const bytes = getBytesFromBytesValueNode(node); return { ...typeManifest(), value: `new Uint8Array([${Array.from(bytes).join(", ")}])` }; }, visitConstantValue(node, { self }) { if (isNode2(node.type, "bytesTypeNode") && isNode2(node.value, "bytesValueNode")) { return visit2(node.value, self); } const imports = new ImportMap(); const value = visit2(node.value, self); imports.mergeWith(value.valueImports); const type = visit2(node.type, self); imports.mergeWith(type.serializerImports); return { ...typeManifest(), value: `${type.serializer}.serialize(${value.value})`, valueImports: imports }; }, visitDateTimeType(dateTimeType, { self }) { const numberManifest = visit2(dateTimeType.number, self); const dateTimeNumber = resolveNestedTypeNode2(dateTimeType.number); if (!isInteger(dateTimeNumber)) { throw new Error( `DateTime wrappers can only be applied to integer types. Got format [${dateTimeNumber.format}].` ); } numberManifest.strictImports.add("umi", "DateTime"); numberManifest.looseImports.add("umi", "DateTimeInput"); numberManifest.serializerImports.add("umi", "mapDateTimeSerializer"); return { ...numberManifest, looseType: `DateTimeInput`, serializer: `mapDateTimeSerializer(${numberManifest.serializer})`, strictType: `DateTime` }; }, visitDefinedType(definedType, { self }) { parentName = { loose: `${pascalCase2(definedType.name)}Args`, strict: pascalCase2(definedType.name) }; const manifest = visit2(definedType.type, self); parentName = null; return manifest; }, visitDefinedTypeLink(node) { const pascalCaseDefinedType = pascalCase2(node.name); const serializerName = `get${pascalCaseDefinedType}Serializer`; const importFrom = getImportFrom(node); return { isEnum: false, looseImports: new ImportMap().add(importFrom, `${pascalCaseDefinedType}Args`), looseType: `${pascalCaseDefinedType}Args`, serializer: `${serializerName}()`, serializerImports: new ImportMap().add(importFrom, serializerName), strictImports: new ImportMap().add(importFrom, pascalCaseDefinedType), strictType: pascalCaseDefinedType, value: "", valueImports: new ImportMap() }; }, visitEnumEmptyVariantType(enumEmptyVariantType) { const name = pascalCase2(enumEmptyVariantType.name); const kindAttribute = `__kind: "${name}"`; return { isEnum: false, looseImports: new ImportMap(), looseType: `{ ${kindAttribute} }`, serializer: `['${name}', unit()]`, serializerImports: new ImportMap().add("umiSerializers", "unit"), strictImports: new ImportMap(), strictType: `{ ${kindAttribute} }`, value: "", valueImports: new ImportMap() }; }, visitEnumStructVariantType(enumStructVariantType, { self }) { const name = pascalCase2(enumStructVariantType.name); const kindAttribute = `__kind: "${name}"`; const type = visit2(enumStructVariantType.struct, self); return { ...type, looseType: `{ ${kindAttribute},${type.looseType.slice(1, -1)}}`, serializer: `['${name}', ${type.serializer}]`, strictType: `{ ${kindAttribute},${type.strictType.slice(1, -1)}}` }; }, visitEnumTupleVariantType(enumTupleVariantType, { self }) { const name = pascalCase2(enumTupleVariantType.name); const kindAttribute = `__kind: "${name}"`; const struct = structTypeNode([ structFieldTypeNode({ name: "fields", type: enumTupleVariantType.tuple }) ]); const type = visit2(struct, self); return { ...type, looseType: `{ ${kindAttribute},${type.looseType.slice(1, -1)}}`, serializer: `['${name}', ${type.serializer}]`, strictType: `{ ${kindAttribute},${type.strictType.slice(1, -1)}}`, value: "", valueImports: new ImportMap() }; }, visitEnumType(enumType, { self }) { const strictImports = new ImportMap(); const looseImports = new ImportMap(); const serializerImports = new ImportMap(); const variantNames = enumType.variants.map((variant) => pascalCase2(variant.name)); const currentParentName = { ...parentName }; parentName = null; const options = []; const enumSize = resolveNestedTypeNode2(enumType.size); if (enumSize.format !== "u8" || enumSize.endian !== "le") { const sizeManifest = visit2(enumType.size, self); strictImports.mergeWith(sizeManifest.strictImports); looseImports.mergeWith(sizeManifest.looseImports); serializerImports.mergeWith(sizeManifest.serializerImports); options.push(`size: ${sizeManifest.serializer}`); } if (isScalarEnum(enumType)) { if (currentParentName === null) { throw new Error( "Scalar enums cannot be inlined and must be introduced via a defined type. Ensure you are not inlining a defined type that is a scalar enum through a visitor." ); } options.push(`description: '${currentParentName.strict}'`); const optionsAsString2 = options.length > 0 ? `, { ${options.join(", ")} }` : ""; return { isEnum: true, looseImports, looseType: `{ ${variantNames.join(", ")} }`, serializer: `scalarEnum<${currentParentName.strict}>(${currentParentName.strict + optionsAsString2})`, serializerImports: serializerImports.add("umiSerializers", "scalarEnum"), strictImports, strictType: `{ ${variantNames.join(", ")} }`, value: "", valueImports: new ImportMap() }; } const variants = enumType.variants.map((variant) => { const variantName = pascalCase2(variant.name); parentName = currentParentName ? { loose: `GetDataEnumKindContent<${currentParentName.loose}, '${variantName}'>`, strict: `GetDataEnumKindContent<${currentParentName.strict}, '${variantName}'>` } : null; const variantManifest = visit2(variant, self); parentName = null; return variantManifest; }); const mergedManifest = mergeManifests(variants); mergedManifest.strictImports.mergeWith(strictImports); mergedManifest.looseImports.mergeWith(looseImports); mergedManifest.serializerImports.mergeWith(serializerImports); const variantSerializers = variants.map((variant) => variant.serializer).join(", "); const serializerTypeParams = currentParentName ? currentParentName.strict : "any"; if (currentParentName?.strict) { options.push(`description: '${pascalCase2(currentParentName.strict)}'`); } const optionsAsString = options.length > 0 ? `, { ${options.join(", ")} }` : ""; return { ...mergedManifest, looseType: variants.map((variant) => variant.looseType).join(" | "), serializer: `dataEnum<${serializerTypeParams}>([${variantSerializers}]${optionsAsString})`, serializerImports: mergedManifest.serializerImports.add("umiSerializers", [ "GetDataEnumKindContent", "GetDataEnumKind", "dataEnum" ]), strictType: variants.map((variant) => variant.strictType).join(" | "), value: "", valueImports: new ImportMap() }; }, visitEnumValue(node, { self }) { const imports = new ImportMap(); const enumName = pascalCase2(node.enum.name); const variantName = pascalCase2(node.variant); const importFrom = getImportFrom(node.enum); const enumNode = linkables.get([...stack.getPath(), node.enum])?.type; const isScalar = enumNode && isNode2(enumNode, "enumTypeNode") ? isScalarEnum(enumNode) : !nonScalarEnums.includes(node.enum.name); if (!node.value && isScalar) { return { ...typeManifest(), value: `${enumName}.${variantName}`, valueImports: imports.add(importFrom, enumName) }; } const enumFn = camelCase3(node.enum.name); imports.add(importFrom, enumFn); if (!node.value) { return { ...typeManifest(), value: `${enumFn}('${variantName}')`, valueImports: imports }; } const enumValue = visit2(node.value, self); const fields = enumValue.value; imports.mergeWith(enumValue.valueImports); return { ...typeManifest(), value: `${enumFn}('${variantName}', ${fields})`, valueImports: imports }; }, visitFixedSizeType(fixedSizeType, { self }) { parentSize = fixedSizeType.size; const manifest = visit2(fixedSizeType.type, self); parentSize = null; return manifest; }, visitInstruction(instruction, { self }) { parentName = { loose: `${pascalCase2(instruction.name)}InstructionDataArgs`, strict: `${pascalCase2(instruction.name)}InstructionData` }; const link = customInstructionData.get(instruction.name)?.linkNode; const struct = structTypeNodeFromInstructionArgumentNodes2(instruction.arguments); const manifest = link ? visit2(link, self) : visit2(struct, self); parentName = null; return manifest; }, visitMapEntryValue(node, { self }) { const mapKey = visit2(node.key, self); const mapValue = visit2(node.value, self); return { ...typeManifest(), imports: mapKey.valueImports.mergeWith(mapValue.valueImports), render: `[${mapKey.value}, ${mapValue.value}]` }; }, visitMapType(mapType, { self }) { const key = visit2(mapType.key, self); const value = visit2(mapType.value, self); const mergedManifest = mergeManifests([key, value]); mergedManifest.serializerImports.add("umiSerializers", "map"); const sizeOption = getArrayLikeSizeOption(mapType.count, mergedManifest, self); const options = sizeOption ? `, { ${sizeOption} }` : ""; return { ...mergedManifest, looseType: `Map<${key.looseType}, ${value.looseType}>`, serializer: `map(${key.serializer}, ${value.serializer}${options})`, strictType: `Map<${key.strictType}, ${value.strictType}>`, value: "", valueImports: new ImportMap() }; }, visitMapValue(node, { self }) { const map = node.entries.map((entry) => visit2(entry, self)); return { ...typeManifest(), value: `new Map([${map.map((c) => c.value).join(", ")}])`, valueImports: new ImportMap().mergeWith(...map.map((c) => c.valueImports)) }; }, visitNoneValue() { return { ...typeManifest(), value: "none()", valueImports: new ImportMap().add("umi", "none") }; }, visitNumberType(numberType) { const isBigNumber = ["u64", "u128", "i64", "i128"].includes(numberType.format); const serializerImports = new ImportMap().add("umiSerializers", numberType.format); let endianness = ""; if (numberType.endian === "be") { serializerImports.add("umiSerializers", "Endian"); endianness = "{ endian: Endian.Big }"; } return { isEnum: false, looseImports: new ImportMap(), looseType: isBigNumber ? "number | bigint" : "number", serializer: `${numberType.format}(${endianness})`, serializerImports, strictImports: new ImportMap(), strictType: isBigNumber ? "bigint" : "number", value: "", valueImports: new ImportMap() }; }, visitNumberValue(node) { return { ...typeManifest(), value: JSON.stringify(node.number) }; }, visitOptionType(optionType, { self }) { const childManifest = visit2(optionType.item, self); childManifest.strictImports.add("umi", "Option"); childManifest.looseImports.add("umi", "OptionOrNullable"); childManifest.serializerImports.add("umiSerializers", "option"); const options = []; const optionPrefix = resolveNestedTypeNode2(optionType.prefix); if (optionPrefix.format !== "u8" || optionPrefix.endian !== "le") { const prefixManifest = visit2(optionType.prefix, self); childManifest.strictImports.mergeWith(prefixManifest.strictImports); childManifest.looseImports.mergeWith(prefixManifest.looseImports); childManifest.serializerImports.mergeWith(prefixManifest.serializerImports); options.push(`prefix: ${prefixManifest.serializer}`); } if (optionType.fixed) { options.push(`fixed: true`); } const optionsAsString = options.length > 0 ? `, { ${options.join(", ")} }` : ""; return { ...childManifest, looseType: `OptionOrNullable<${childManifest.looseType}>`, serializer: `option(${childManifest.serializer}${optionsAsString})`, strictType: `Option<${childManifest.strictType}>` }; }, visitPublicKeyType() { const imports = new ImportMap().add("umi", "PublicKey"); return { isEnum: false, looseImports: imports, looseType: "PublicKey", serializer: `publicKeySerializer()`, serializerImports: new ImportMap().add("umiSerializers", "publicKey").addAlias("umiSerializers", "publicKey", "publicKeySerializer"), strictImports: imports, strictType: "PublicKey", value: "", valueImports: new ImportMap() }; }, visitPublicKeyValue(node) { return { ...typeManifest(), value: `publicKey("${node.publicKey}")`, valueImports: new ImportMap().add("umi", "publicKey") }; }, visitRemainderOptionType(node) { throw new CodamaError2(CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node }); }, visitSetType(setType, { self }) { const childManifest = visit2(setType.item, self); childManifest.serializerImports.add("umiSerializers", "set"); const sizeOption = getArrayLikeSizeOption(setType.count, childManifest, self); const options = sizeOption ? `, { ${sizeOption} }` : ""; return { ...childManifest, looseType: `Set<${childManifest.looseType}>`, serializer: `set(${childManifest.serializer + options})`, strictType: `Set<${childManifest.strictType}>`, value: "", valueImports: new ImportMap() }; }, visitSetValue(node, { self }) { const set = node.items.map((value) => visit2(value, self)); return { ...typeManifest(), value: `new Set([${set.map((c) => c.value).join(", ")}])`, valueImports: new ImportMap().mergeWith(...set.map((c) => c.valueImports)) }; }, visitSizePrefixType(sizePrefixType, { self }) { parentSize = resolveNestedTypeNode2(sizePrefixType.prefix); const manifest = visit2(sizePrefixType.type, self); parentSize = null; return manifest; }, visitSolAmountType(solAmountType, { self }) { const numberManifest = visit2(solAmountType.number, self); const nestedNumber = resolveNestedTypeNode2(solAmountType.number); if (!isUnsignedInteger(nestedNumber)) { throw new Error( `Amount wrappers can only be applied to unsigned integer types. Got format [${nestedNumber.format}].` ); } const idAndDecimals = `'SOL', 9`; numberManifest.strictImports.add("umi", "SolAmount"); numberManifest.looseImports.add("umi", "SolAmount"); numberManifest.serializerImports.add("umi", "mapAmountSerializer"); return { ...numberManifest, looseType: "SolAmount", serializer: `mapAmountSerializer(${numberManifest.serializer}, ${idAndDecimals})`, strictType: "SolAmount" }; }, visitSomeValue(node, { self }) { const child = visit2(node.value, self); return { ...typeManifest(), value: `some(${child.value})`, valueImports: child.valueImports.add("umi", "some") }; }, visitStringType(stringType, { self }) { const looseImports = new ImportMap(); const strictImports = new ImportMap(); const serializerImports = new ImportMap().add("umiSerializers", "string"); const options = []; if (stringType.encoding !== "utf8") { looseImports.add("umiSerializers", stringType.encoding); strictImports.add("umiSerializers", stringType.encoding); options.push(`encoding: ${stringType.encoding}`); } if (!parentSize) { options.push(`size: 'variable'`); } else if (typeof parentSize === "number") { options.push(`size: ${parentSize}`); } else if (parentSize.format !== "u32" || parentSize.endian !== "le") { const prefix = visit2(parentSize, self); looseImports.mergeWith(prefix.looseImports); strictImports.mergeWith(prefix.strictImports); serializerImports.mergeWith(prefix.serializerImports); options.push(`size: ${prefix.serializer}`); } const optionsAsString = options.length > 0 ? `{ ${options.join(", ")} }` : ""; return { isEnum: false, looseImports, looseType: "string", serializer: `string(${optionsAsString})`, serializerImports, strictImports, strictType: "string", value: "", valueImports: new ImportMap() }; }, visitStringValue(node) { return { ...typeManifest(), value: JSON.stringify(node.string) }; }, visitStructFieldType(structFieldType, { self }) { const name = camelCase3(structFieldType.name); const fieldChild = visit2(structFieldType.type, self); const structFieldDocs = parseDocs(structFieldType.docs); const docblock = structFieldDocs.length > 0 ? ` ${jsDocblock(structFieldDocs)}` : ""; const baseField = { ...fieldChild, looseType: `${docblock}${name}: ${fieldChild.looseType}; `, serializer: `['${name}', ${fieldChild.serializer}]`, strictType: `${docblock}${name}: ${fieldChild.strictType}; ` }; if (!structFieldType.defaultValue) { return baseField; } if (structFieldType.defaultValueStrategy !== "omitted") { return { ...baseField, looseType: `${docblock}${name}?: ${fieldChild.looseType}; ` }; } return { ...baseField, looseImports: new ImportMap(), looseType: "" }; }, visitStructFieldValue(node, { self }) { const structValue = visit2(node.value, self); return { ...structValue, value: `${node.name}: ${structValue.value}` }; }, visitStructType(structType, { self }) { const currentParentName = parentName; parentName = null; const fields = structType.fields.map((field) => visit2(field, self)); const mergedManifest = mergeManifests(fields); mergedManifest.serializerImports.add("umiSerializers", "struct"); const fieldSerializers = fields.map((field) => field.serializer).join(", "); const structDescription = currentParentName?.strict && !currentParentName.strict.match(/['"<>]/) ? `, { description: '${pascalCase2(currentParentName.strict)}' }` : ""; const serializerTypeParams = currentParentName ? currentParentName.strict : "any"; const baseManifest = { ...mergedManifest, looseType: `{ ${fields.map((field) => field.looseType).join("")} }`, serializer: `struct<${serializerTypeParams}>([${fieldSerializers}]${structDescription})`, strictType: `{ ${fields.map((field) => field.strictType).join("")} }`, value: "", valueImports: new ImportMap() }; const optionalFields = structType.fields.filter((f) => !!f.defaultValue); if (optionalFields.length === 0) { return baseManifest; } const defaultValues = optionalFields.map((f) => { const key = camelCase3(f.name); const defaultValue = f.defaultValue; const { value: renderedValue, valueImports } = visit2(defaultValue, self); baseManifest.serializerImports.mergeWith(valueImports); if (f.defaultValueStrategy === "omitted") { return `${key}: ${renderedValue}`; } return `${key}: value.${key} ?? ${renderedValue}`; }).join(", "); const mapSerializerTypeParams = currentParentName ? `${currentParentName.loose}, any, ${currentParentName.strict}` : "any, any, any"; const mappedSerializer = `mapSerializer<${mapSerializerTypeParams}>(${baseManifest.serializer}, (value) => ({ ...value, ${defaultValues} }) )`; baseManifest.serializerImports.add("umiSerializers", "mapSerializer"); return { ...baseManifest, serializer: mappedSerializer }; }, visitStructValue(node, { self }) { const struct = node.fields.map((field) => visit2(field, self)); return { ...typeManifest(), value: `{ ${struct.map((c) => c.value).join(", ")} }`, valueImports: new ImportMap().mergeWith(...struct.map((c) => c.valueImports)) }; }, visitTupleType(tupleType, { self }) { const items = tupleType.items.map((item) => visit2(item, self)); const mergedManifest = mergeManifests(items); mergedManifest.serializerImports.add("umiSerializers", "tuple"); const itemSerializers = items.map((child) => child.serializer).join(", "); return { ...mergedManifest, looseType: `[${items.map((item) => item.looseType).join(", ")}]`, serializer: `tuple([${itemSerializers}])`, strictType: `[${items.map((item) => item.strictType).join(", ")}]`, value: "" }; }, visitTupleValue(node, { self }) { const list = node.items.map((value) => visit2(value, self)); return { ...typeManifest(), value: `[${list.map((c) => c.value).join(", ")}]`, valueImports: new ImportMap().mergeWith(...list.map((c) => c.valueImports)) }; }, visitZeroableOptionType(node) { throw new CodamaError2(CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node }); } }), (v) => recordNodeStackVisitor(v, stack) ); } function mergeManifests(manifests) { return { isEnum: false, looseImports: new ImportMap().mergeWith(...manifests.map((td) => td.looseImports)), serializerImports: new ImportMap().mergeWith(...manifests.map((td) => td.serializerImports)), strictImports: new ImportMap().mergeWith(...manifests.map((td) => td.strictImports)), valueImports: new ImportMap().mergeWith(...manifests.map((td) => td.valueImports)) }; } function getArrayLikeSizeOption(count, manifest, self) { if (isNode2(count, "fixedCountNode")) return `size: ${count.value}`; if (isNode2(count, "remainderCountNode")) return `size: 'remainder'`; const prefixManifest = visit2(count.prefix, self); if (prefixManifest.serializer === "u32()") return null; manifest.strictImports.mergeWith(prefixManifest.strictImports); manifest.looseImports.mergeWith(prefixManifest.looseImports); manifest.serializerImports.mergeWith(prefixManifest.serializerImports); return `size: ${prefixManifest.serializer}`; } // src/renderInstructionDefaults.ts import { camelCase as camelCase4, isNode as isNode3, pascalCase as pascalCase3 } from "@codama/nodes"; import { visit as visit3 } from "@codama/visitors-core"; function renderInstructionDefaults(input, typeManifestVisitor, optionalAccountStrategy, argObject, getImportFrom) { const imports = new ImportMap(); const interfaces = new ContextMap(); if (!input.defaultValue) { return { imports, interfaces, render: "" }; } const { defaultValue } = input; const render2 = (renderedValue, isWritable) => { const inputName = camelCase4(input.name); if (input.kind === "instructionAccountNode" && isNode3(defaultValue, "resolverValueNode")) { return { imports, interfaces, render: `resolvedAccounts.${inputName} = { ...resolvedAccounts.${inputName}, ...${renderedValue} };` }; } if (input.kind === "instructionAccountNode" && isWritable === void 0) { return { imports, interfaces, render: `resolvedAccounts.${inputName}.value = ${renderedValue};` }; } if (input.kind === "instructionAccountNode") { return { imports, interfaces, render: `resolvedAccounts.${inputName}.value = ${renderedValue}; resolvedAccounts.${inputName}.isWritable = ${isWritable ? "true" : "false"}` }; } return { imports, interfaces, render: `${argObject}.${inputName} = ${renderedValue};` }; }; switch (defaultValue.kind) { case "accountValueNode": const name = camelCase4(defaultValue.name); if (input.kind === "instructionAccountNode") { imports.add("shared", "expectSome"); if (input.resolvedIsSigner && !input.isSigner) { return render2(`expectSome(resolvedAccounts.${name}.value).publicKey`); } return render2(`expectSome(resolvedAccounts.${name}.value)`); } imports.add("shared", "expectPublicKey"); return render2(`expectPublicKey(resolvedAccounts.${name}.value)`); case "pdaValueNode": if (isNode3(defaultValue.pda, "pdaNode")) { const pdaProgram = defaultValue.pda.programId ? `context.programs.getPublicKey('${defaultValue.pda.programId}', '${defaultValue.pda.programId}')` : "programId"; const pdaSeeds2 = defaultValue.pda.seeds.flatMap((seed) => { if (isNode3(seed, "constantPdaSeedNode") && isNode3(seed.value, "programIdValueNode")) { imports.add("umiSerializers", "publicKey").addAlias("umiSerializers", "publicKey", "publicKeySerializer"); return [`publicKeySerializer().serialize(${pdaProgram})`]; } if (isNode3(seed, "constantPdaSeedNode") && !isNode3(seed.value, "programIdValueNode")) { const typeManifest2 = visit3(seed.type, typeManifestVisitor); const valueManifest2 = visit3(seed.value, typeManifestVisitor); imports.mergeWith(typeManifest2.serializerImports); imports.mergeWith(valueManifest2.valueImports); return [`${typeManifest2.serializer}.serialize(${valueManifest2.value})`]; } if (isNode3(seed, "variablePdaSeedNode")) { const typeManifest2 = visit3(seed.type, typeManifestVisitor); const valueSeed = defaultValue.seeds.find((s) => s.name === seed.name)?.value; if (!valueSeed) return []; if (isNode3(valueSeed, "accountValueNode")) { imports.mergeWith(typeManifest2.serializerImports); imports.add("shared", "expectPublicKey"); return [ `${typeManifest2.serializer}.serialize(expectPublicKey(resolvedAccounts.${camelCase4(valueSeed.name)}.value))` ]; } if (isNode3(valueSeed, "argumentValueNode")) { imports.mergeWith(typeManifest2.serializerImports); imports.add("shared", "expectSome"); return [ `${typeManifest2.serializer}.serialize(expectSome(${argObject}.${camelCase4(valueSeed.name)}))` ]; } const valueManifest2 = visit3(valueSeed, typeManifestVisitor); imports.mergeWith(typeManifest2.serializerImports); imports.mergeWith(valueManifest2.valueImports); return [`${typeManifest2.serializer}.serialize(${valueManifest2.value})`]; } return []; }); interfaces.add("eddsa"); return render2(`context.eddsa.findPda(${pdaProgram}, [${pdaSeeds2.join(", ")}])`); } const pdaFunction = `find${pascalCase3(defaultValue.pda.name)}Pda`; imports.add(getImportFrom(defaultValue.pda), pdaFunction); interfaces.add("eddsa"); const pdaArgs = ["context"]; const pdaSeeds = defaultValue.seeds.map((seed) => { if (isNode3(seed.value, "accountValueNode")) { imports.add("shared", "expectPublicKey"); return `${seed.name}: expectPublicKey(resolvedAccounts.${camelCase4(seed.value.name)}.value)`; } if (isNode3(seed.value, "argumentValueNode")) { imports.add("shared", "expectSome"); return `${seed.name}: expectSome(${argObject}.${camelCase4(seed.value.name)})`; } const valueManifest2 = visit3(seed.value, typeManifestVisitor); imports.mergeWith(valueManifest2.valueImports); return `${seed.name}: ${valueManifest2.value}`; }); if (pdaSeeds.length > 0) { pdaArgs.push(`{ ${pdaSeeds.join(", ")} }`); } return render2(`${pdaFunction}(${pdaArgs.join(", ")})`); case "publicKeyValueNode": if (!defaultValue.identifier) { imports.add("umi", "publicKey"); return render2(`publicKey('${defaultValue.publicKey}')`); } interfaces.add("programs"); return render2( `context.programs.getPublicKey('${defaultValue.identifier}', '${defaultValue.publicKey}')`, false ); case "programLinkNode": const functionName = `get${pascalCase3(defaultValue.name)}ProgramId`; imports.add(getImportFrom(defaultValue), functionName); return render2(`${functionName}(context)`, false); case "programIdValueNode": if (optionalAccountStrategy === "programId" && input.kind === "instructionAccountNode" && input.isOptional) { return { imports, interfaces, render: "" }; } return render2("programId", false); case "identityValueNode": interfaces.add("identity"); if (input.kind === "instructionAccountNode" && input.isSigner !== false) { return render2("context.identity"); } return render2("context.identity.publicKey"); case "payerValueNode": interfaces.add("payer"); if (input.kind === "instructionAccountNode" && input.isSigner !== false) { return render2("context.payer"); } return render2("context.payer.publicKey"); case "accountBumpValueNode": imports.add("shared", "expectPda"); return render2(`expectPda(resolvedAccounts.${camelCase4(defaultValue.name)}.value)[1]`); case "argumentValueNode": imports.add("shared", "expectSome"); return render2(`expectSome(${argObject}.${camelCase4(defaultValue.name)})`); case "resolverValueNode": const resolverName = camelCase4(defaultValue.name); const isWritable = input.kind === "instructionAccountNode" && input.isWritable ? "true" : "false"; imports.add(getImportFrom(defaultValue), resolverName); interfaces.add(["eddsa", "identity", "payer"]); return render2(`${resolverName}(context, resolvedAccounts, ${argObject}, programId, ${isWritable})`); case "conditionalValueNode": const ifTrueRenderer = renderNestedInstructionDefault( input, typeManifestVisitor, optionalAccountStrategy, defaultValue.ifTrue, argObject, getImportFrom ); const ifFalseRenderer = renderNestedInstructionDefault( input, typeManifestVisitor, optionalAccountStrategy, defaultValue.ifFalse, argObject, getImportFrom ); if (!ifTrueRenderer && !ifFalseRenderer) { return { imports, interfaces, render: "" }; } if (ifTrueRenderer) { imports.mergeWith(ifTrueRenderer.imports); interfaces.mergeWith(ifTrueRenderer.interfaces); } if (ifFalseRenderer) { imports.mergeWith(ifFalseRenderer.imports); interfaces.mergeWith(ifFalseRenderer.interfaces); } const negatedCondition = !ifTrueRenderer; let condition = "true"; if (isNode3(defaultValue.condition, "resolverValueNode")) { const conditionalResolverName = camelCase4(defaultValue.condition.name); const conditionalIsWritable = input.kind === "instructionAccountNode" && input.isWritable ? "true" : "false"; imports.add(getImportFrom(defaultValue.condition), conditionalResolverName); interfaces.add(["eddsa", "identity", "payer"]); condition = `${conditionalResolverName}(context, resolvedAccounts, ${argObject}, programId