'use strict'; var errors = require('@codama/errors'); var nodes = require('@codama/nodes'); var renderersCore = require('@codama/renderers-core'); var visitorsCore = require('@codama/visitors-core'); var codecsStrings = require('@solana/codecs-strings'); var path = require('path'); require('url'); var nunjucks = require('nunjucks'); var validators = require('@codama/validators'); var estreePlugin = require('prettier/plugins/estree'); var typeScriptPlugin = require('prettier/plugins/typescript'); var standalone = require('prettier/standalone'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var nunjucks__default = /*#__PURE__*/_interopDefault(nunjucks); var estreePlugin__namespace = /*#__PURE__*/_interopNamespace(estreePlugin); var typeScriptPlugin__namespace = /*#__PURE__*/_interopNamespace(typeScriptPlugin); // 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/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`; } }; function getBytesFromBytesValueNode(node) { switch (node.encoding) { case "utf8": return codecsStrings.getUtf8Encoder().encode(node.data); case "base16": return codecsStrings.getBase16Encoder().encode(node.data); case "base58": return codecsStrings.getBase58Encoder().encode(node.data); case "base64": default: return codecsStrings.getBase64Encoder().encode(node.data); } } var parseCustomDataOptions = (customDataOptions, defaultSuffix) => new Map( customDataOptions.map((o) => { const options = typeof o === "string" ? { name: o } : o; const importAs = nodes.camelCase(options.importAs ?? `${options.name}${defaultSuffix}`); const importFrom = options.importFrom ?? "hooked"; return [ nodes.camelCase(options.name), { extract: options.extract ?? false, extractAs: options.extractAs ? nodes.camelCase(options.extractAs) : importAs, importAs, importFrom, linkNode: nodes.definedTypeLinkNode(importAs) } ]; }) ); var getDefinedTypeNodesToExtract = (nodes$1, parsedCustomDataOptions) => nodes$1.flatMap((node) => { const options = parsedCustomDataOptions.get(node.name); if (!options || !options.extract) return []; if (nodes.isNode(node, "accountNode")) { return [nodes.definedTypeNode({ name: options.extractAs, type: { ...node.data } })]; } return [ nodes.definedTypeNode({ name: options.extractAs, type: nodes.structTypeNodeFromInstructionArgumentNodes(node.arguments) }) ]; }); function getGpaFieldsFromAccount(node, sizeVisitor) { let offset = 0; const struct = nodes.resolveNestedTypeNode(node.data); return struct.fields.map((field) => { const fieldOffset = offset; if (offset !== null) { const newOffset = visitorsCore.visit(field.type, sizeVisitor); offset = newOffset !== null ? offset + newOffset : null; } return { name: field.name, offset: fieldOffset, type: field.type }; }); } 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 errors.CodamaError(errors.CODAMA_ERROR__UNEXPECTED_NODE_KIND, { expectedKinds: [ "AccountLinkNode", "DefinedTypeLinkNode", "InstructionLinkNode", "PdaLinkNode", "ProgramLinkNode", "resolverValueNode" ], kind, node }); } }; } 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 = __dirname; const templates = path.join(dirname, "templates"); const env = nunjucks__default.default.configure(templates, { autoescape: false, trimBlocks: true, ...options }); env.addFilter("pascalCase", nodes.pascalCase); env.addFilter("camelCase", nodes.camelCase); env.addFilter("snakeCase", nodes.snakeCase); env.addFilter("kebabCase", nodes.kebabCase); env.addFilter("titleCase", nodes.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 visitorsCore.NodeStack(); let parentName = null; let parentSize = null; return visitorsCore.pipe( visitorsCore.staticVisitor( () => ({ isEnum: false, looseImports: new ImportMap(), looseType: "", serializer: "", serializerImports: new ImportMap(), strictImports: new ImportMap(), strictType: "", value: "", valueImports: new ImportMap() }), { keys: [ ...nodes.REGISTERED_TYPE_NODE_KINDS, ...nodes.REGISTERED_VALUE_NODE_KINDS, "definedTypeLinkNode", "definedTypeNode", "accountNode", "instructionNode" ] } ), (v) => visitorsCore.extendVisitor(v, { visitAccount(account, { self }) { parentName = { loose: `${nodes.pascalCase(account.name)}AccountDataArgs`, strict: `${nodes.pascalCase(account.name)}AccountData` }; const link = customAccountData.get(account.name)?.linkNode; const manifest = link ? visitorsCore.visit(link, self) : visitorsCore.visit(account.data, self); parentName = null; return manifest; }, visitAmountType(amountType, { self }) { const numberManifest = visitorsCore.visit(amountType.number, self); const resolvedNode = nodes.resolveNestedTypeNode(amountType.number); if (!nodes.isUnsignedInteger(resolvedNode)) { throw new Error( `Amount wrappers can only be applied to unsigned integer types. Got type [${amountType.number.toString()}].` ); } 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 = visitorsCore.visit(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) => visitorsCore.visit(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 = nodes.resolveNestedTypeNode(booleanType.size); if (resolvedSize.format !== "u8" || resolvedSize.endian !== "le") { const size = visitorsCore.visit(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 = visitorsCore.visit(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 (nodes.isNode(node.type, "bytesTypeNode") && nodes.isNode(node.value, "bytesValueNode")) { return visitorsCore.visit(node.value, self); } const imports = new ImportMap(); const value = visitorsCore.visit(node.value, self); imports.mergeWith(value.valueImports); const type = visitorsCore.visit(node.type, self); imports.mergeWith(type.serializerImports); return { ...typeManifest(), value: `${type.serializer}.serialize(${value.value})`, valueImports: imports }; }, visitDateTimeType(dateTimeType, { self }) { const numberManifest = visitorsCore.visit(dateTimeType.number, self); const dateTimeNumber = nodes.resolveNestedTypeNode(dateTimeType.number); if (!nodes.isInteger(dateTimeNumber)) { throw new Error( `DateTime wrappers can only be applied to integer types. Got type [${dateTimeNumber.toString()}].` ); } 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: `${nodes.pascalCase(definedType.name)}Args`, strict: nodes.pascalCase(definedType.name) }; const manifest = visitorsCore.visit(definedType.type, self); parentName = null; return manifest; }, visitDefinedTypeLink(node) { const pascalCaseDefinedType = nodes.pascalCase(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 = nodes.pascalCase(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 = nodes.pascalCase(enumStructVariantType.name); const kindAttribute = `__kind: "${name}"`; const type = visitorsCore.visit(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 = nodes.pascalCase(enumTupleVariantType.name); const kindAttribute = `__kind: "${name}"`; const struct = nodes.structTypeNode([ nodes.structFieldTypeNode({ name: "fields", type: enumTupleVariantType.tuple }) ]); const type = visitorsCore.visit(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) => nodes.pascalCase(variant.name)); const currentParentName = { ...parentName }; parentName = null; const options = []; const enumSize = nodes.resolveNestedTypeNode(enumType.size); if (enumSize.format !== "u8" || enumSize.endian !== "le") { const sizeManifest = visitorsCore.visit(enumType.size, self); strictImports.mergeWith(sizeManifest.strictImports); looseImports.mergeWith(sizeManifest.looseImports); serializerImports.mergeWith(sizeManifest.serializerImports); options.push(`size: ${sizeManifest.serializer}`); } if (nodes.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 = nodes.pascalCase(variant.name); parentName = currentParentName ? { loose: `GetDataEnumKindContent<${currentParentName.loose}, '${variantName}'>`, strict: `GetDataEnumKindContent<${currentParentName.strict}, '${variantName}'>` } : null; const variantManifest = visitorsCore.visit(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: '${nodes.pascalCase(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 = nodes.pascalCase(node.enum.name); const variantName = nodes.pascalCase(node.variant); const importFrom = getImportFrom(node.enum); const enumNode = linkables.get([...stack.getPath(), node.enum])?.type; const isScalar = enumNode && nodes.isNode(enumNode, "enumTypeNode") ? nodes.isScalarEnum(enumNode) : !nonScalarEnums.includes(node.enum.name); if (!node.value && isScalar) { return { ...typeManifest(), value: `${enumName}.${variantName}`, valueImports: imports.add(importFrom, enumName) }; } const enumFn = nodes.camelCase(node.enum.name); imports.add(importFrom, enumFn); if (!node.value) { return { ...typeManifest(), value: `${enumFn}('${variantName}')`, valueImports: imports }; } const enumValue = visitorsCore.visit(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 = visitorsCore.visit(fixedSizeType.type, self); parentSize = null; return manifest; }, visitInstruction(instruction, { self }) { parentName = { loose: `${nodes.pascalCase(instruction.name)}InstructionDataArgs`, strict: `${nodes.pascalCase(instruction.name)}InstructionData` }; const link = customInstructionData.get(instruction.name)?.linkNode; const struct = nodes.structTypeNodeFromInstructionArgumentNodes(instruction.arguments); const manifest = link ? visitorsCore.visit(link, self) : visitorsCore.visit(struct, self); parentName = null; return manifest; }, visitMapEntryValue(node, { self }) { const mapKey = visitorsCore.visit(node.key, self); const mapValue = visitorsCore.visit(node.value, self); return { ...typeManifest(), imports: mapKey.valueImports.mergeWith(mapValue.valueImports), render: `[${mapKey.value}, ${mapValue.value}]` }; }, visitMapType(mapType, { self }) { const key = visitorsCore.visit(mapType.key, self); const value = visitorsCore.visit(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) => visitorsCore.visit(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 = visitorsCore.visit(optionType.item, self); childManifest.strictImports.add("umi", "Option"); childManifest.looseImports.add("umi", "OptionOrNullable"); childManifest.serializerImports.add("umiSerializers", "option"); const options = []; const optionPrefix = nodes.resolveNestedTypeNode(optionType.prefix); if (optionPrefix.format !== "u8" || optionPrefix.endian !== "le") { const prefixManifest = visitorsCore.visit(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 errors.CodamaError(errors.CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node }); }, visitSetType(setType, { self }) { const childManifest = visitorsCore.visit(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) => visitorsCore.visit(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 = nodes.resolveNestedTypeNode(sizePrefixType.prefix); const manifest = visitorsCore.visit(sizePrefixType.type, self); parentSize = null; return manifest; }, visitSolAmountType(solAmountType, { self }) { const numberManifest = visitorsCore.visit(solAmountType.number, self); const nestedNumber = nodes.resolveNestedTypeNode(solAmountType.number); if (!nodes.isUnsignedInteger(nestedNumber)) { throw new Error( `Amount wrappers can only be applied to unsigned integer types. Got type [${nestedNumber.toString()}].` ); } 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 = visitorsCore.visit(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 = visitorsCore.visit(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 = nodes.camelCase(structFieldType.name); const fieldChild = visitorsCore.visit(structFieldType.type, self); const docblock = structFieldType.docs.length > 0 ? ` ${jsDocblock(structFieldType.docs)}` : ""; 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 = visitorsCore.visit(node.value, self); return { ...structValue, value: `${node.name}: ${structValue.value}` }; }, visitStructType(structType, { self }) { const currentParentName = parentName; parentName = null; const fields = structType.fields.map((field) => visitorsCore.visit(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: '${nodes.pascalCase(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 = nodes.camelCase(f.name); const defaultValue = f.defaultValue; const { value: renderedValue, valueImports } = visitorsCore.visit(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) => visitorsCore.visit(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) => visitorsCore.visit(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) => visitorsCore.visit(value, self)); return { ...typeManifest(), value: `[${list.map((c) => c.value).join(", ")}]`, valueImports: new ImportMap().mergeWith(...list.map((c) => c.valueImports)) }; }, visitZeroableOptionType(node) { throw new errors.CodamaError(errors.CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node }); } }), (v) => visitorsCore.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 (nodes.isNode(count, "fixedCountNode")) return `size: ${count.value}`; if (nodes.isNode(count, "remainderCountNode")) return `size: 'remainder'`; const prefixManifest = visitorsCore.visit(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}`; } 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 = nodes.camelCase(input.name); if (input.kind === "instructionAccountNode" && nodes.isNode(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 = nodes.camelCase(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 (nodes.isNode(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 (nodes.isNode(seed, "constantPdaSeedNode") && nodes.isNode(seed.value, "programIdValueNode")) { imports.add("umiSerializers", "publicKey").addAlias("umiSerializers", "publicKey", "publicKeySerializer"); return [`publicKeySerializer().serialize(${pdaProgram})`]; } if (nodes.isNode(seed, "constantPdaSeedNode") && !nodes.isNode(seed.value, "programIdValueNode")) { const typeManifest2 = visitorsCore.visit(seed.type, typeManifestVisitor); const valueManifest2 = visitorsCore.visit(seed.value, typeManifestVisitor); imports.mergeWith(typeManifest2.serializerImports); imports.mergeWith(valueManifest2.valueImports); return [`${typeManifest2.serializer}.serialize(${valueManifest2.value})`]; } if (nodes.isNode(seed, "variablePdaSeedNode")) { const typeManifest2 = visitorsCore.visit(seed.type, typeManifestVisitor); const valueSeed = defaultValue.seeds.find((s) => s.name === seed.name)?.value; if (!valueSeed) return []; if (nodes.isNode(valueSeed, "accountValueNode")) { imports.mergeWith(typeManifest2.serializerImports); imports.add("shared", "expectPublicKey"); return [ `${typeManifest2.serializer}.serialize(expectPublicKey(resolvedAccounts.${nodes.camelCase(valueSeed.name)}.value))` ]; } if (nodes.isNode(valueSeed, "argumentValueNode")) { imports.mergeWith(typeManifest2.serializerImports); imports.add("shared", "expectSome"); return [ `${typeManifest2.serializer}.serialize(expectSome(${argObject}.${nodes.camelCase(valueSeed.name)}))` ]; } const valueManifest2 = visitorsCore.visit(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${nodes.pascalCase(defaultValue.pda.name)}Pda`; imports.add(getImportFrom(defaultValue.pda), pdaFunction); interfaces.add("eddsa"); const pdaArgs = ["context"]; const pdaSeeds = defaultValue.seeds.map((seed) => { if (nodes.isNode(seed.value, "accountValueNode")) { imports.add("shared", "expectPublicKey"); return `${seed.name}: expectPublicKey(resolvedAccounts.${nodes.camelCase(seed.value.name)}.value)`; } if (nodes.isNode(seed.value, "argumentValueNode")) { imports.add("shared", "expectSome"); return `${seed.name}: expectSome(${argObject}.${nodes.camelCase(seed.value.name)})`; } const valueManifest2 = visitorsCore.visit(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${nodes.pascalCase(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.${nodes.camelCase(defaultValue.name)}.value)[1]`); case "argumentValueNode": imports.add("shared", "expectSome"); return render2(`expectSome(${argObject}.${nodes.camelCase(defaultValue.name)})`); case "resolverValueNode": const resolverName = nodes.camelCase(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 (nodes.isNode(defaultValue.condition, "resolverValueNode")) { const conditionalResolverName = nodes.camelCase(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, ${conditionalIsWritable})`; condition = negatedCondition ? `!${condition}` : condition; } else { const comparedInputName = nodes.isNode(defaultValue.condition, "accountValueNode") ? `resolvedAccounts.${nodes.camelCase(defaultValue.condition.name)}.value` : `${argObject}.${nodes.camelCase(defaultValue.condition.name)}`; if (defaultValue.value) { const comparedValue = visitorsCore.visit(defaultValue.value, typeManifestVisitor); imports.mergeWith(comparedValue.valueImports); const operator = negatedCondition ? "!==" : "==="; condition = `${comparedInputName} ${operator} ${comparedValue.value}`; } else { condition = negatedCondition ? `!${comparedInputName}` : comparedInputName; } } if (ifTrueRenderer && ifFalseRenderer) { return { imports, interfaces, render: `if (${condition}) { ${ifTrueRenderer.render} } else { ${ifFalseRenderer.render} }` }; } return { imports, interfaces, render: `if (${condition}) { ${ifTrueRenderer ? ifTrueRenderer.render : ifFalseRenderer?.render} }` }; default: const valueManifest = visitorsCore.visit(defaultValue, typeManifestVisitor); imports.mergeWith(valueManifest.valueImports); return render2(valueManifest.value); } } function renderNestedInstructionDefault(input, typeManifestVisitor, optionalAccountStrategy, defaultValue, argObject, getImportFrom) { if (!defaultValue) return void 0; return renderInstructionDefaults( { ...input, defaultValue }, typeManifestVisitor, optionalAccountStrategy, argObject, getImportFrom ); } // src/getRenderMapVisitor.ts function getRenderMapVisitor(options = {}) { const linkables = new visitorsCore.LinkableDictionary(); const stack = new visitorsCore.NodeStack(); let program = null; const renderParentInstructions = options.renderParentInstructions ?? false; const dependencyMap = { generated: "..", hooked: "../../hooked", mplEssentials: "@metaplex-foundation/mpl-toolbox", mplToolbox: "@metaplex-foundation/mpl-toolbox", umi: "@metaplex-foundation/umi", umiSerializers: "@metaplex-foundation/umi/serializers", ...options.dependencyMap, // Custom relative dependencies to link generated files together. generatedAccounts: "../accounts", generatedErrors: "../errors", generatedInstructions: "../instructions", generatedPrograms: "../programs", generatedTypes: "../types" }; const nonScalarEnums = (options.nonScalarEnums ?? []).map(nodes.camelCase); const internalNodes = (options.internalNodes ?? []).map(nodes.camelCase); const customAccountData = parseCustomDataOptions(options.customAccountData ?? [], "AccountData"); const customInstructionData = parseCustomDataOptions(options.customInstructionData ?? [], "InstructionData"); const getImportFrom = getImportFromFactory(options.linkOverrides ?? {}, customAccountData, customInstructionData); const typeManifestVisitor = getTypeManifestVisitor({ customAccountData, customInstructionData, getImportFrom, linkables, nonScalarEnums, stack }); const resolvedInstructionInputVisitor = visitorsCore.getResolvedInstructionInputsVisitor(); const byteSizeVisitor = visitorsCore.getByteSizeVisitor(linkables, { stack }); function getInstructionAccountType(account) { if (account.isPda && account.isSigner === false) return "Pda"; if (account.isSigner === "either") return "PublicKey | Pda | Signer"; return account.isSigner ? "Signer" : "PublicKey | Pda"; } function getInstructionAccountImports(accounts) { const imports = new ImportMap(); accounts.forEach((account) => { if (account.isSigner !== true && !account.isPda) imports.add("umi", "PublicKey"); if (account.isSigner !== true) imports.add("umi", "Pda"); if (account.isSigner !== false) imports.add("umi", "Signer"); }); return imports; } function getMergeConflictsForInstructionAccountsAndArgs(instruction) { const allNames = [ ...instruction.accounts.map((account) => account.name), ...instruction.arguments.map((field) => field.name), ...(instruction.extraArguments ?? []).map((field) => field.name) ]; const duplicates = allNames.filter((e, i, a) => a.indexOf(e) !== i); return [...new Set(duplicates)]; } return visitorsCore.pipe( visitorsCore.staticVisitor(() => new renderersCore.RenderMap()), (v) => visitorsCore.extendVisitor(v, { visitAccount(node) { const customData = customAccountData.get(node.name); const isLinked = !!customData; const typeManifest2 = visitorsCore.visit(node, typeManifestVisitor); const imports = new ImportMap().mergeWith( typeManifest2.strictImports, typeManifest2.serializerImports ); if (!isLinked) { imports.mergeWith(typeManifest2.looseImports); } imports.add("umi", [ "Account", "assertAccountExists", "Context", "deserializeAccount", "Pda", "PublicKey", "publicKey", "RpcAccount", "RpcGetAccountOptions", "RpcGetAccountsOptions" ]).add("umiSerializers", !isLinked ? ["Serializer"] : []).addAlias("umi", "publicKey", "toPublicKey"); const discriminator = (node.discriminators ?? []).find((d) => !nodes.isNode(d, "constantDiscriminatorNode")) ?? null; let resolvedDiscriminator = null; if (nodes.isNode(discriminator, "fieldDiscriminatorNode")) { const discriminatorField = nodes.resolveNestedTypeNode(node.data).fields.find( (f) => f.name === discriminator.name ); const discriminatorValue = discriminatorField?.defaultValue ? visitorsCore.visit(discriminatorField.defaultValue, typeManifestVisitor) : void 0; if (discriminatorValue) { imports.mergeWith(discriminatorValue.valueImports); resolvedDiscriminator = { ...discriminator, value: discriminatorValue.value }; } } else if (nodes.isNode(discriminator, "sizeDiscriminatorNode")) { resolvedDiscriminator = discriminator; } const gpaFields = getGpaFieldsFromAccount(node, byteSizeVisitor).map((gpaField) => { const gpaFieldManifest = visitorsCore.visit(gpaField.type, typeManifestVisitor); imports.mergeWith(gpaFieldManifest.looseImports, gpaFieldManifest.serializerImports); return { ...gpaField, manifest: gpaFieldManifest }; }); let resolvedGpaFields = null; if (gpaFields.length > 0) { imports.add("umi", ["gpaBuilder"]); resolvedGpaFields = { argument: `{ ${gpaFields.map((f) => { const offset = f.offset === null ? "null" : `${f.offset}`; return `'${f.name}': [${offset}, ${f.manifest.serializer}]`; }).join(", ")} }`, type: `{ ${gpaFields.map((f) => `'${f.name}': ${f.manifest.looseType}`).join(", ")} }` }; } const pda = node.pda ? linkables.get([...stack.getPath(), node.pda]) : void 0; const pdaSeeds = pda?.seeds ?? []; const seeds = pdaSeeds.map((seed) => { if (nodes.isNode(seed, "variablePdaSeedNode")) { const seedManifest2 = visitorsCore.visit(seed.type, typeManifestVisitor); imports.mergeWith(seedManifest2.looseImports, seedManifest2.serializerImports); return { ...seed, typeManifest: seedManifest2 }; } if (nodes.isNode(seed.value, "programIdValueNode")) { imports.add("umiSerializers", "publicKey").addAlias("umiSerializers", "publicKey", "publicKeySerializer"); return seed; } const seedManifest = visitorsCore.visit(seed.type, typeManifestVisitor); imports.mergeWith(seedManifest.serializerImports); const valueManifest = visitorsCore.visit(seed.value, typeManifestVisitor); imports.mergeWith(valueManifest.valueImports); return { ...seed, typeManifest: seedManifest, valueManifest }; }); if (seeds.length > 0) { imports.add("umi", ["Pda"]); } const hasVariableSeeds = pdaSeeds.filter(nodes.isNodeFilter("variablePdaSeedNode")).length > 0; return new renderersCore.RenderMap().add( `accounts/${nodes.camelCase(node.name)}.ts`, render("accountsPage.njk", { account: node, customData, discriminator: resolvedDiscriminator, gpaFields: resolvedGpaFields, hasVariableSeeds, imports: imports.toString(dependencyMap), program, seeds, typeManifest: typeManifest2 }) ); }, visitDefinedType(node) { const pascalCaseName = nodes.pascalCase(node.name); const typeManifest2 = visitorsCore.visit(node, typeManifestVisitor); const imports = new ImportMap().mergeWithManifest(typeManifest2).add("umiSerializers", ["Serializer"]).remove("generatedTypes", [ pascalCaseName, `${pascalCaseName}Args`, `get${pascalCaseName}Serializer` ]); return new renderersCore.RenderMap().add( `types/${nodes.camelCase(node.name)}.ts`, render("definedTypesPage.njk", { definedType: node, imports: imports.toString({ ...dependencyMap, generatedTypes: "." }), isDataEnum: nodes.isNode(node.type, "enumTypeNode") && nodes.isDataEnum(node.type), typeManifest: typeManifest2 }) ); }, visitInstruction(node) { const interfaces = new ContextMap().add("programs"); const imports = new ImportMap().add("umi", ["Context", "TransactionBuilder", "transactionBuilder"]).add("shared", ["ResolvedAccount", "ResolvedAccountsWithIndices", "getAccountMetasAndSigners"]); const customData = customInstructionData.get(node.name); const linkedDataArgs = !!customData; const hasAccounts = node.accounts.length > 0; const hasData = linkedDataArgs || node.arguments.length > 0; const hasDataArgs = linkedDataArgs || node.arguments.filter((field) => field.defaultValueStrategy !== "omitted").length > 0; const hasExtraArgs = (node.extraArguments ?? []).filter((field) => field.defaultValueStrategy !== "omitted").length > 0; const hasAnyArgs = hasDataArgs || hasExtraArgs; const allArgumentsWithDefaultValue = [ ...node.arguments.filter((a) => a.defaultValue && !nodes.isNode(a.defaultValue, nodes.VALUE_NODES)), ...(node.extraArguments ?? []).filter((a) => a.defaultValue) ]; const hasArgDefaults = allArgumentsWithDefaultValue.length > 0; const hasArgResolvers = allArgumentsWithDefaultValue.some( (a) => nodes.isNode(a.defaultValue, "resolverValueNode") ); const hasAccountResolvers = node.accounts.some((a) => nodes.isNode(a.defaultValue, "resolverValueNode")); const byteDelta = node.byteDeltas?.[0] ?? void 0; const hasByteResolver = byteDelta && nodes.isNode(byteDelta.value, "resolverValueNode"); let remainingAccounts = node.remainingAccounts?.[0] ?? void 0; if (remainingAccounts && nodes.isNode(remainingAccounts.value, "argumentValueNode") && nodes.getAllInstructionArguments(node).every((arg) => arg.name !== remainingAccounts?.value.name)) { remainingAccounts = void 0; } const hasRemainingAccountsResolver = remainingAccounts && nodes.isNode(remainingAccounts.value, "resolverValueNode"); const hasResolvers = hasArgResolvers || hasAccountResolvers || hasByteResolver || hasRemainingAccountsResolver; const hasResolvedArgs = hasDataArgs || hasArgDefaults || hasResolvers; if (hasResolvers) { interfaces.add(["eddsa", "identity", "payer"]); } let canMergeAccountsAndArgs = false; if (!linkedDataArgs) { const accountsAndArgsConflicts = getMergeConflictsForInstructionAccountsAndArgs(node); if (accountsAndArgsConflicts.length > 0) { errors.logWarn( `[JavaScript Umi] Accounts and args of instruction [${node.name}] have the following conflicting attributes [${accountsAndArgsConflicts.join(", ")}]. Thus, they could not be merged into a single input object. You may want to rename the conflicting attributes.` ); } canMergeAccountsAndArgs = accountsAndArgsConflicts.length === 0; } let argObject = canMergeAccountsAndArgs ? "input" : "args"; argObject = hasResolvedArgs ? "resolvedArgs" : argObject; const resolvedInputs = visitorsCore.visit(node, resolvedInstructionInputVisitor).map( (input) => { const renderedInput = renderInstructionDefaults( input, typeManifestVisitor, node.optionalAccountStrategy, argObject, getImportFrom ); imports.mergeWith(renderedInput.imports); interfaces.mergeWith(renderedInput.interfaces); return { ...input, render: renderedInput.render }; } ); const resolvedInputsWithDefaults = resolvedInputs.filter( (input) => input.defaultValue !== void 0 && input.render !== "" ); const argsWithDefaults = resolvedInputsWithDefaults.filter(nodes.isNodeFilter("instructionArgumentNode")).map((input) => input.name); const accounts = node.accounts.map((account) => { const hasDefaultValue = !!account.defaultValue; const resolvedAccount = resolvedInputs.find( (input) => input.kind === "instructionAccountNode" && input.name === account.name ); return { ...resolvedAccount, hasDefaultValue, optionalSign: hasDefaultValue || account.isOptional ? "?" : "", type: getInstructionAccountType(resolvedAccount) }; }); imports.mergeWith(getInstructionAccountImports(accounts)); const dataArgManifest = visitorsCore.visit(node, typeManifestVisitor); if (linkedDataArgs || hasData) { imports.mergeWith(dataArgManifest.looseImports, dataArgManifest.serializerImports); } if (!linkedDataArgs) { imports.mergeWith(dataArgManifest.strictImports); } if (!linkedDataArgs && hasData) { imports.add("umiSerializers", ["Serializer"]); } const extraArgStruct = nodes.definedTypeNode({ name: `${node.name}InstructionExtra`, type: nodes.structTypeNodeFromInstructionArgumentNodes(node.extraArguments ?? []) }); const extraArgManifest = visitorsCore.visit(extraArgStruct, typeManifestVisitor); imports.mergeWith(extraArgManifest.looseImports); allArgumentsWithDefaultValue.forEach((argument) => { if (nodes.isNode(argument.defaultValue, "resolverValueNode")) { imports.add(getImportFrom(argument.defaultValue), nodes.camelCase(argument.defaultValue.name)); } }); if (argsWithDefaults.length > 0) { imports.add("shared", ["PickPartial"]); } if (byteDelta && byteDelta.withHeader) { imports.add("umi", "ACCOUNT_HEADER_SIZE"); } if (byteDelta && nodes.isNode(byteDelta.value, "accountLinkNode")) { const accountName = nodes.pascalCase(byteDelta.value.name); imports.add(getImportFrom(byteDelta.value), `get${accountName}Size`); } else if (byteDelta && nodes.isNode(byteDelta.value, "resolverValueNode")) { imports.add(getImportFrom(byteDelta.value), nodes.camelCase(byteDelta.value.name)); } if (remainingAccounts && nodes.isNode(remainingAccounts.value, "resolverValueNode")) { imports.add(getImportFrom(remainingAccounts.value), nodes.camelCase(remainingAccounts.value.name)); } return new renderersCore.RenderMap().add( `instructions/${nodes.camelCase(node.name)}.ts`, render("instructionsPage.njk", { accounts, argsWithDefaults, byteDelta, canMergeAccountsAndArgs, customData, dataArgManifest, extraArgManifest, hasAccountResolvers, hasAccounts, hasAnyArgs, hasArgDefaults, hasArgResolvers, hasByteResolver, hasData, hasDataArgs, hasExtraArgs, hasRemainingAccountsResolver, hasResolvedArgs, hasResolvers, imports: imports.toString(dependencyMap), instruction: node, interfaces: interfaces.toString(), program, remainingAccounts, resolvedInputs, resolvedInputsWithDefaults }) ); }, visitProgram(node, { self }) { program = node; const pascalCaseName = nodes.pascalCase(node.name); const customDataDefinedType = [ ...getDefinedTypeNodesToExtract(node.accounts, customAccountData), ...getDefinedTypeNodesToExtract(node.instructions, customInstructionData) ]; const renderMap = new renderersCore.RenderMap().mergeWith(...node.accounts.map((a) => visitorsCore.visit(a, self))).mergeWith(...node.definedTypes.map((t) => visitorsCore.visit(t, self))).mergeWith(...customDataDefinedType.map((t) => visitorsCore.visit(t, self))).mergeWith( ...nodes.getAllInstructionsWithSubs(node, { leavesOnly: !renderParentInstructions }).map((ix) => visitorsCore.visit(ix, self)) ).add( `errors/${nodes.camelCase(node.name)}.ts`, render("errorsPage.njk", { errors: node.errors, imports: new ImportMap().add("umi", ["ProgramError", "Program"]).toString(dependencyMap), program: node }) ).add( `programs/${nodes.camelCase(node.name)}.ts`, render("programsPage.njk", { imports: new ImportMap().add("umi", ["ClusterFilter", "Context", "Program", "PublicKey"]).add("errors", [ `get${pascalCaseName}ErrorFromCode`, `get${pascalCaseName}ErrorFromName` ]).toString(dependencyMap), program: node }) ); program = null; return renderMap; }, visitRoot(node, { self }) { const isNotInternal = (n) => !internalNodes.includes(n.name); const programsToExport = nodes.getAllPrograms(node).filter(isNotInternal); const accountsToExport = nodes.getAllAccounts(node).filter(isNotInternal); const instructionsToExport = nodes.getAllInstructionsWithSubs(node, { leavesOnly: !renderParentInstructions }).filter(isNotInternal); const definedTypesToExport = nodes.getAllDefinedTypes(node).filter(isNotInternal); const hasAnythingToExport = programsToExport.length > 0 || accountsToExport.length > 0 || instructionsToExport.length > 0 || definedTypesToExport.length > 0; const ctx = { accountsToExport, definedTypesToExport, hasAnythingToExport, instructionsToExport, programsToExport, root: node }; const map = new renderersCore.RenderMap(); if (hasAnythingToExport) { map.add("shared/index.ts", render("sharedPage.njk", ctx)); } if (programsToExport.length > 0) { map.add("programs/index.ts", render("programsIndex.njk", ctx)).add( "errors/index.ts", render("errorsIndex.njk", ctx) ); } if (accountsToExport.length > 0) { map.add("accounts/index.ts", render("accountsIndex.njk", ctx)); } if (instructionsToExport.length > 0) { map.add("instructions/index.ts", render("instructionsIndex.njk", ctx)); } if (definedTypesToExport.length > 0) { map.add("types/index.ts", render("definedTypesIndex.njk", ctx)); } return map.add("index.ts", render("rootIndex.njk", ctx)).mergeWith(...nodes.getAllPrograms(node).map((p) => visitorsCore.visit(p, self))); } }), (v) => visitorsCore.recordNodeStackVisitor(v, stack), (v) => visitorsCore.recordLinkablesOnFirstVisitVisitor(v, linkables) ); } function getValidationItemsVisitor() { const exportMap = /* @__PURE__ */ new Map(); const stack = new visitorsCore.NodeStack(); const isEponymousExport = (node, exportName) => exportName === ("name" in node ? node.name : ""); const getNodeTitle = (node) => { const name = "name" in node ? node.name : ""; const type = nodes.titleCase(node.kind.slice(0, -4)).toLowerCase(); return `"${name}" ${type}`; }; const checkExportConflicts = (node, exports) => { const items = []; const conflictingNodes = []; Object.entries(exports).forEach(([exportName, exportType]) => { const exportConflict = exportMap.get(exportName); if (!exportConflict) { exportMap.set(exportName, { exportType, node, stack: stack.clone() }); return; } const conflictingNode = exportConflict.node; if (conflictingNodes.includes(conflictingNode)) return; conflictingNodes.push(conflictingNode); let exportDetails = ""; let conflictExportDetails = ""; if (!isEponymousExport(node, exportName)) { exportDetails = `exports a "${exportName}" ${exportType} that `; } if (!isEponymousExport(conflictingNode, exportName)) { conflictExportDetails = `"${exportName}" ${exportConflict.exportType} exported by the `; } const message = `The ${getNodeTitle(node)} ${exportDetails}conflicts with the ${conflictExportDetails}${getNodeTitle(conflictingNode)}. |> Conflicting stack: ${exportConflict.stack}.`; items.push(validators.validationItem("error", message, node, stack)); }); return items; }; return visitorsCore.pipe( visitorsCore.mergeVisitor( () => [], (_, items) => items.flat() ), (v) => visitorsCore.recordNodeStackVisitor(v, stack), (v) => visitorsCore.extendVisitor(v, { visitAccount(node, { next }) { const items = []; const pascalCaseName = nodes.pascalCase(node.name); const exports = { [pascalCaseName]: "type", [`${pascalCaseName}AccountData`]: "type", [`${pascalCaseName}AccountDataArgs`]: "type", [`fetch${pascalCaseName}`]: "function", [`safeFetch${pascalCaseName}`]: "function", [`deserialize${pascalCaseName}`]: "function", [`get${pascalCaseName}AccountDataSerializer`]: "function", [`get${pascalCaseName}GpaBuilder`]: "function", [`get${pascalCaseName}Size`]: "function" }; items.push(...checkExportConflicts(node, exports)); const reservedAccountFields = /* @__PURE__ */ new Set(["publicKey", "header"]); const invalidFields = nodes.resolveNestedTypeNode(node.data).fields.map((field) => field.name).filter((name) => reservedAccountFields.has(name)); if (invalidFields.length > 0) { const x = invalidFields.join(", "); const message = invalidFields.length === 1 ? `Account field [${x}] is reserved. Please rename it.` : `Account fields [${x}] are reserved. Please rename them.`; items.push(validators.validationItem("error", message, node, stack)); } return [...items, ...next(node)]; }, visitDefinedType(node, { next }) { const items = []; const camelCaseName = nodes.camelCase(node.name); const pascalCaseName = nodes.pascalCase(node.name); items.push( ...checkExportConflicts(node, { [pascalCaseName]: "type", [`${pascalCaseName}Args`]: "type", [`fetch${pascalCaseName}`]: "function", ...nodes.isNode(node.type, "enumTypeNode") && nodes.isDataEnum(node.type) ? { [camelCaseName]: "function", [`is${pascalCaseName}`]: "function" } : {} }) ); return [...items, ...next(node)]; }, visitError(node, { next }) { const items = []; items.push( ...checkExportConflicts(node, { [`${nodes.pascalCase(node.name)}Error`]: "class" }) ); return [...items, ...next(node)]; }, visitInstruction(node, { next }) { const items = []; const camelCaseName = nodes.camelCase(node.name); const pascalCaseName = nodes.pascalCase(node.name); const pascalCaseData = `${pascalCaseName}InstructionData`; const pascalCaseExtra = `${pascalCaseName}InstructionExtra`; items.push( ...checkExportConflicts(node, { [camelCaseName]: "function", [`${pascalCaseName}InstructionAccounts`]: "type", [`${pascalCaseName}InstructionArgs`]: "type", [`${pascalCaseData}`]: "type", [`${pascalCaseData}Args`]: "type", [`get${pascalCaseData}Serializer`]: "function", [`${pascalCaseExtra}Args`]: "type" }) ); return [...items, ...next(node)]; }, visitProgram(node, { next }) { const items = []; const pascalCaseName = nodes.pascalCase(node.name); items.push( ...checkExportConflicts(node, { [`get${pascalCaseName}Program`]: "function", [`get${pascalCaseName}ErrorFromCode`]: "function", [`get${pascalCaseName}ErrorFromName`]: "function" }) ); return [...items, ...next(node)]; } }) ); } // src/renderVisitor.ts var DEFAULT_PRETTIER_OPTIONS = { arrowParens: "always", parser: "typescript", plugins: [estreePlugin__namespace, typeScriptPlugin__namespace], printWidth: 80, semi: true, singleQuote: true, tabWidth: 2, trailingComma: "es5", useTabs: false }; function renderVisitor(path, options = {}) { return visitorsCore.rootNodeVisitor(async (root) => { visitorsCore.visit(root, validators.throwValidatorItemsVisitor(getValidationItemsVisitor(), options.throwLevel)); if (options.deleteFolderBeforeRendering ?? true) { renderersCore.deleteDirectory(path); } const renderMap = visitorsCore.visit(root, getRenderMapVisitor(options)); if (options.formatCode ?? true) { const prettierOptions = { ...DEFAULT_PRETTIER_OPTIONS, ...options.prettierOptions }; await renderMap.mapContentAsync((code) => standalone.format(code, prettierOptions)); } renderMap.write(path); }); } exports.ImportMap = ImportMap; exports.getRenderMapVisitor = getRenderMapVisitor; exports.getTypeManifestVisitor = getTypeManifestVisitor; exports.renderVisitor = renderVisitor; //# sourceMappingURL=index.node.cjs.map //# sourceMappingURL=index.node.cjs.map