@codama/renderers-js-umi
Version:
JavaScript renderer compatible with the Umi framework
1,228 lines (1,218 loc) • 77.4 kB
JavaScript
// 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