'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 child_process = require('child_process'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var nunjucks__default = /*#__PURE__*/_interopDefault(nunjucks); // src/ImportMap.ts var DEFAULT_MODULE_MAP = { generated: "crate::generated", generatedAccounts: "crate::generated::accounts", generatedErrors: "crate::generated::errors", generatedInstructions: "crate::generated::instructions", generatedTypes: "crate::generated::types", hooked: "crate::hooked", mplEssentials: "mpl_toolbox", mplToolbox: "mpl_toolbox" }; var ImportMap = class _ImportMap { _imports = /* @__PURE__ */ new Set(); _aliases = /* @__PURE__ */ new Map(); get imports() { return this._imports; } get aliases() { return this._aliases; } add(imports) { const newImports = typeof imports === "string" ? [imports] : imports; newImports.forEach((i) => this._imports.add(i)); return this; } remove(imports) { const importsToRemove = typeof imports === "string" ? [imports] : imports; importsToRemove.forEach((i) => this._imports.delete(i)); return this; } mergeWith(...others) { others.forEach((other) => { this.add(other._imports); other._aliases.forEach((alias, importName) => this.addAlias(importName, alias)); }); return this; } mergeWithManifest(manifest) { return this.mergeWith(manifest.imports); } addAlias(importName, alias) { this._aliases.set(importName, alias); return this; } isEmpty() { return this._imports.size === 0; } resolveDependencyMap(dependencies) { const dependencyMap = { ...DEFAULT_MODULE_MAP, ...dependencies }; const newImportMap = new _ImportMap(); const resolveDependency = (i) => { const dependencyKey = Object.keys(dependencyMap).find((key) => i.startsWith(`${key}::`)); if (!dependencyKey) return i; const dependencyValue = dependencyMap[dependencyKey]; return dependencyValue + i.slice(dependencyKey.length); }; this._imports.forEach((i) => newImportMap.add(resolveDependency(i))); this._aliases.forEach((alias, i) => newImportMap.addAlias(resolveDependency(i), alias)); return newImportMap; } toString(dependencies) { const resolvedMap = this.resolveDependencyMap(dependencies); const importStatements = [...resolvedMap.imports].map((i) => { const alias = resolvedMap.aliases.get(i); if (alias) return `use ${i} as ${alias};`; return `use ${i};`; }); return importStatements.join("\n"); } }; 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); } } function getImportFromFactory(overrides) { const linkOverrides = { accounts: overrides.accounts ?? {}, definedTypes: 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 rustDocblock(docs) { if (docs.length <= 0) return ""; 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("rustDocblock", rustDocblock); return env.render(template, context); }; var DEFAULT_TRAIT_OPTIONS = { baseDefaults: [ "borsh::BorshSerialize", "borsh::BorshDeserialize", "serde::Serialize", "serde::Deserialize", "Clone", "Debug", "Eq", "PartialEq" ], dataEnumDefaults: [], featureFlags: { serde: ["serde::Serialize", "serde::Deserialize"] }, overrides: {}, scalarEnumDefaults: ["Copy", "PartialOrd", "Hash", "num_derive::FromPrimitive"], structDefaults: [], useFullyQualifiedName: false }; function getTraitsFromNodeFactory(options = {}) { return (node) => getTraitsFromNode(node, options); } function getTraitsFromNode(node, userOptions = {}) { nodes.assertIsNode(node, ["accountNode", "definedTypeNode"]); const options = { ...DEFAULT_TRAIT_OPTIONS, ...userOptions }; const nodeType = getNodeType(node); if (nodeType === "alias") { return { imports: new ImportMap(), render: "" }; } const sanitizedOverrides = Object.fromEntries( Object.entries(options.overrides).map(([key, value]) => [nodes.camelCase(key), value]) ); const nodeOverrides = sanitizedOverrides[node.name]; const allTraits = nodeOverrides === void 0 ? getDefaultTraits(nodeType, options) : nodeOverrides; const partitionedTraits = partitionTraitsInFeatures(allTraits, options.featureFlags); let unfeaturedTraits = partitionedTraits[0]; const featuredTraits = partitionedTraits[1]; const imports = new ImportMap(); if (!options.useFullyQualifiedName) { unfeaturedTraits = extractFullyQualifiedNames(unfeaturedTraits, imports); } const traitLines = [ ...unfeaturedTraits.length > 0 ? [`#[derive(${unfeaturedTraits.join(", ")})] `] : [], ...Object.entries(featuredTraits).map(([feature, traits]) => { return `#[cfg_attr(feature = "${feature}", derive(${traits.join(", ")}))] `; }) ]; return { imports, render: traitLines.join("") }; } function getNodeType(node) { if (nodes.isNode(node, "accountNode")) return "struct"; if (nodes.isNode(node.type, "structTypeNode")) return "struct"; if (nodes.isNode(node.type, "enumTypeNode")) { return nodes.isScalarEnum(node.type) ? "scalarEnum" : "dataEnum"; } return "alias"; } function getDefaultTraits(nodeType, options) { switch (nodeType) { case "dataEnum": return [...options.baseDefaults, ...options.dataEnumDefaults]; case "scalarEnum": return [...options.baseDefaults, ...options.scalarEnumDefaults]; case "struct": return [...options.baseDefaults, ...options.structDefaults]; } } function partitionTraitsInFeatures(traits, featureFlags) { const reverseFeatureFlags = Object.entries(featureFlags).reduce( (acc, [feature, traits2]) => { for (const trait of traits2) { if (!acc[trait]) acc[trait] = feature; } return acc; }, {} ); const unfeaturedTraits = []; const featuredTraits = {}; for (const trait of traits) { const feature = reverseFeatureFlags[trait]; if (feature === void 0) { unfeaturedTraits.push(trait); } else { if (!featuredTraits[feature]) featuredTraits[feature] = []; featuredTraits[feature].push(trait); } } return [unfeaturedTraits, featuredTraits]; } function extractFullyQualifiedNames(traits, imports) { return traits.map((trait) => { const index = trait.lastIndexOf("::"); if (index === -1) return trait; imports.add(trait); return trait.slice(index + 2); }); } // src/getTypeManifestVisitor.ts function getTypeManifestVisitor(options) { const { getImportFrom, getTraitsFromNode: getTraitsFromNode2 } = options; let parentName = options.parentName ?? null; let nestedStruct = options.nestedStruct ?? false; let inlineStruct = false; let parentSize = null; return visitorsCore.pipe( visitorsCore.mergeVisitor( () => ({ imports: new ImportMap(), nestedStructs: [], type: "" }), (_, values) => ({ ...mergeManifests(values), type: values.map((v) => v.type).join("\n") }), { keys: [...nodes.REGISTERED_TYPE_NODE_KINDS, "definedTypeLinkNode", "definedTypeNode", "accountNode"] } ), (v) => visitorsCore.extendVisitor(v, { visitAccount(account, { self }) { parentName = nodes.pascalCase(account.name); const manifest = visitorsCore.visit(account.data, self); const traits = getTraitsFromNode2(account); manifest.imports.mergeWith(traits.imports); parentName = null; return { ...manifest, type: traits.render + manifest.type }; }, visitArrayType(arrayType, { self }) { const childManifest = visitorsCore.visit(arrayType.item, self); if (nodes.isNode(arrayType.count, "fixedCountNode")) { return { ...childManifest, type: `[${childManifest.type}; ${arrayType.count.value}]` }; } if (nodes.isNode(arrayType.count, "remainderCountNode")) { childManifest.imports.add("kaigan::types::RemainderVec"); return { ...childManifest, type: `RemainderVec<${childManifest.type}>` }; } const prefix = nodes.resolveNestedTypeNode(arrayType.count.prefix); if (prefix.endian === "le") { switch (prefix.format) { case "u32": return { ...childManifest, type: `Vec<${childManifest.type}>` }; case "u8": case "u16": case "u64": { const prefixFormat = prefix.format.toUpperCase(); childManifest.imports.add(`kaigan::types::${prefixFormat}PrefixVec`); return { ...childManifest, type: `${prefixFormat}PrefixVec<${childManifest.type}>` }; } case "shortU16": { childManifest.imports.add("solana_program::short_vec::ShortVec"); return { ...childManifest, type: `ShortVec<${childManifest.type}>` }; } default: throw new Error(`Array prefix not supported: ${prefix.format}`); } } throw new Error("Array size not supported by Borsh"); }, visitBooleanType(booleanType) { const resolvedSize = nodes.resolveNestedTypeNode(booleanType.size); if (resolvedSize.format === "u8" && resolvedSize.endian === "le") { return { imports: new ImportMap(), nestedStructs: [], type: "bool" }; } throw new Error("Bool size not supported by Borsh"); }, visitBytesType(_bytesType, { self }) { let arraySize = nodes.remainderCountNode(); if (typeof parentSize === "number") { arraySize = nodes.fixedCountNode(parentSize); } else if (parentSize && typeof parentSize === "object") { arraySize = nodes.prefixedCountNode(parentSize); } const arrayType = nodes.arrayTypeNode(nodes.numberTypeNode("u8"), arraySize); return visitorsCore.visit(arrayType, self); }, visitDefinedType(definedType, { self }) { parentName = nodes.pascalCase(definedType.name); const manifest = visitorsCore.visit(definedType.type, self); const traits = getTraitsFromNode2(definedType); manifest.imports.mergeWith(traits.imports); parentName = null; const renderedType = nodes.isNode(definedType.type, ["enumTypeNode", "structTypeNode"]) ? manifest.type : `pub type ${nodes.pascalCase(definedType.name)} = ${manifest.type};`; return { ...manifest, type: `${traits.render}${renderedType}` }; }, visitDefinedTypeLink(node) { const pascalCaseDefinedType = nodes.pascalCase(node.name); const importFrom = getImportFrom(node); return { imports: new ImportMap().add(`${importFrom}::${pascalCaseDefinedType}`), nestedStructs: [], type: pascalCaseDefinedType }; }, visitEnumEmptyVariantType(enumEmptyVariantType) { const name = nodes.pascalCase(enumEmptyVariantType.name); return { imports: new ImportMap(), nestedStructs: [], type: `${name},` }; }, visitEnumStructVariantType(enumStructVariantType, { self }) { const name = nodes.pascalCase(enumStructVariantType.name); const originalParentName = parentName; if (!originalParentName) { throw new Error("Enum struct variant type must have a parent name."); } inlineStruct = true; parentName = nodes.pascalCase(originalParentName) + name; const typeManifest = visitorsCore.visit(enumStructVariantType.struct, self); inlineStruct = false; parentName = originalParentName; return { ...typeManifest, type: `${name} ${typeManifest.type},` }; }, visitEnumTupleVariantType(enumTupleVariantType, { self }) { const name = nodes.pascalCase(enumTupleVariantType.name); const originalParentName = parentName; if (!originalParentName) { throw new Error("Enum struct variant type must have a parent name."); } parentName = nodes.pascalCase(originalParentName) + name; const childManifest = visitorsCore.visit(enumTupleVariantType.tuple, self); parentName = originalParentName; let derive = ""; if (childManifest.type === "(Pubkey)") { derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))]\n'; } else if (childManifest.type === "(Vec)") { derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::>"))]\n'; } return { ...childManifest, type: `${derive}${name}${childManifest.type},` }; }, visitEnumType(enumType, { self }) { const originalParentName = parentName; if (!originalParentName) { throw new Error("Enum type must have a parent name."); } const variants = enumType.variants.map((variant) => visitorsCore.visit(variant, self)); const variantNames = variants.map((variant) => variant.type).join("\n"); const mergedManifest = mergeManifests(variants); return { ...mergedManifest, type: `pub enum ${nodes.pascalCase(originalParentName)} { ${variantNames} }` }; }, visitFixedSizeType(fixedSizeType, { self }) { parentSize = fixedSizeType.size; const manifest = visitorsCore.visit(fixedSizeType.type, self); parentSize = null; return manifest; }, visitMapType(mapType, { self }) { const key = visitorsCore.visit(mapType.key, self); const value = visitorsCore.visit(mapType.value, self); const mergedManifest = mergeManifests([key, value]); mergedManifest.imports.add("std::collections::HashMap"); return { ...mergedManifest, type: `HashMap<${key.type}, ${value.type}>` }; }, visitNumberType(numberType) { if (numberType.endian !== "le") { throw new Error("Number endianness not supported by Borsh"); } if (numberType.format === "shortU16") { return { imports: new ImportMap().add("solana_program::short_vec::ShortU16"), nestedStructs: [], type: "ShortU16" }; } return { imports: new ImportMap(), nestedStructs: [], type: numberType.format }; }, visitOptionType(optionType, { self }) { const childManifest = visitorsCore.visit(optionType.item, self); const optionPrefix = nodes.resolveNestedTypeNode(optionType.prefix); if (optionPrefix.format === "u8" && optionPrefix.endian === "le") { return { ...childManifest, type: `Option<${childManifest.type}>` }; } throw new Error("Option size not supported by Borsh"); }, visitPublicKeyType() { return { imports: new ImportMap().add("solana_program::pubkey::Pubkey"), nestedStructs: [], type: "Pubkey" }; }, 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.imports.add("std::collections::HashSet"); return { ...childManifest, type: `HashSet<${childManifest.type}>` }; }, visitSizePrefixType(sizePrefixType, { self }) { parentSize = nodes.resolveNestedTypeNode(sizePrefixType.prefix); const manifest = visitorsCore.visit(sizePrefixType.type, self); parentSize = null; return manifest; }, visitStringType() { if (!parentSize) { return { imports: new ImportMap().add(`kaigan::types::RemainderStr`), nestedStructs: [], type: `RemainderStr` }; } if (typeof parentSize === "number") { return { imports: new ImportMap(), nestedStructs: [], type: `[u8; ${parentSize}]` }; } if (nodes.isNode(parentSize, "numberTypeNode") && parentSize.endian === "le") { switch (parentSize.format) { case "u32": return { imports: new ImportMap(), nestedStructs: [], type: "String" }; case "u8": case "u16": case "u64": { const prefix = parentSize.format.toUpperCase(); return { imports: new ImportMap().add(`kaigan::types::${prefix}PrefixString`), nestedStructs: [], type: `${prefix}PrefixString` }; } default: throw new Error(`'String size not supported: ${parentSize.format}`); } } throw new Error("String size not supported by Borsh"); }, visitStructFieldType(structFieldType, { self }) { const originalParentName = parentName; const originalInlineStruct = inlineStruct; const originalNestedStruct = nestedStruct; if (!originalParentName) { throw new Error("Struct field type must have a parent name."); } parentName = nodes.pascalCase(originalParentName) + nodes.pascalCase(structFieldType.name); nestedStruct = true; inlineStruct = false; const fieldManifest = visitorsCore.visit(structFieldType.type, self); parentName = originalParentName; inlineStruct = originalInlineStruct; nestedStruct = originalNestedStruct; const fieldName = nodes.snakeCase(structFieldType.name); const docblock = rustDocblock(structFieldType.docs); const resolvedNestedType = nodes.resolveNestedTypeNode(structFieldType.type); let derive = ""; if (fieldManifest.type === "Pubkey") { derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))]\n'; } else if (fieldManifest.type === "Vec") { derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::>"))]\n'; } else if (nodes.isNode(resolvedNestedType, "arrayTypeNode") && nodes.isNode(resolvedNestedType.count, "fixedCountNode") && resolvedNestedType.count.value > 32 || nodes.isNode(resolvedNestedType, ["bytesTypeNode", "stringTypeNode"]) && nodes.isNode(structFieldType.type, "fixedSizeTypeNode") && structFieldType.type.size > 32) { derive = '#[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))]\n'; } return { ...fieldManifest, type: inlineStruct ? `${docblock}${derive}${fieldName}: ${fieldManifest.type},` : `${docblock}${derive}pub ${fieldName}: ${fieldManifest.type},` }; }, visitStructType(structType, { self }) { const originalParentName = parentName; if (!originalParentName) { throw new Error("Struct type must have a parent name."); } const fields = structType.fields.map((field) => visitorsCore.visit(field, self)); const fieldTypes = fields.map((field) => field.type).join("\n"); const mergedManifest = mergeManifests(fields); if (nestedStruct) { const nestedTraits = getTraitsFromNode2( nodes.definedTypeNode({ name: originalParentName, type: structType }) ); mergedManifest.imports.mergeWith(nestedTraits.imports); return { ...mergedManifest, nestedStructs: [ ...mergedManifest.nestedStructs, `${nestedTraits.render}pub struct ${nodes.pascalCase(originalParentName)} { ${fieldTypes} }` ], type: nodes.pascalCase(originalParentName) }; } if (inlineStruct) { return { ...mergedManifest, type: `{ ${fieldTypes} }` }; } return { ...mergedManifest, type: `pub struct ${nodes.pascalCase(originalParentName)} { ${fieldTypes} }` }; }, visitTupleType(tupleType, { self }) { const items = tupleType.items.map((item) => visitorsCore.visit(item, self)); const mergedManifest = mergeManifests(items); return { ...mergedManifest, type: `(${items.map((item) => item.type).join(", ")})` }; }, visitZeroableOptionType(node) { throw new errors.CodamaError(errors.CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE, { kind: node.kind, node }); } }) ); } function mergeManifests(manifests) { return { imports: new ImportMap().mergeWith(...manifests.map((td) => td.imports)), nestedStructs: manifests.flatMap((m) => m.nestedStructs) }; } function renderValueNode(value, getImportFrom, useStr = false) { return visitorsCore.visit(value, renderValueNodeVisitor(getImportFrom, useStr)); } function renderValueNodeVisitor(getImportFrom, useStr = false) { return { visitArrayValue(node) { const list = node.items.map((v) => visitorsCore.visit(v, this)); return { imports: new ImportMap().mergeWith(...list.map((c) => c.imports)), render: `[${list.map((c) => c.render).join(", ")}]` }; }, visitBooleanValue(node) { return { imports: new ImportMap(), render: JSON.stringify(node.boolean) }; }, visitBytesValue(node) { const bytes = getBytesFromBytesValueNode(node); const numbers = Array.from(bytes).map(nodes.numberValueNode); return visitorsCore.visit(nodes.arrayValueNode(numbers), this); }, visitConstantValue(node) { if (nodes.isNode(node.value, "bytesValueNode")) { return visitorsCore.visit(node.value, this); } if (nodes.isNode(node.type, "stringTypeNode") && nodes.isNode(node.value, "stringValueNode")) { return visitorsCore.visit(nodes.bytesValueNode(node.type.encoding, node.value.string), this); } if (nodes.isNode(node.type, "numberTypeNode") && nodes.isNode(node.value, "numberValueNode")) { const numberManifest = visitorsCore.visit(node.value, this); const { format, endian } = node.type; const byteFunction = endian === "le" ? "to_le_bytes" : "to_be_bytes"; numberManifest.render = `${numberManifest.render}${format}.${byteFunction}()`; return numberManifest; } throw new Error("Unsupported constant value type."); }, visitEnumValue(node) { const imports = new ImportMap(); const enumName = nodes.pascalCase(node.enum.name); const variantName = nodes.pascalCase(node.variant); const importFrom = getImportFrom(node.enum); imports.add(`${importFrom}::${enumName}`); if (!node.value) { return { imports, render: `${enumName}::${variantName}` }; } const enumValue = visitorsCore.visit(node.value, this); const fields = enumValue.render; return { imports: imports.mergeWith(enumValue.imports), render: `${enumName}::${variantName} ${fields}` }; }, visitMapEntryValue(node) { const mapKey = visitorsCore.visit(node.key, this); const mapValue = visitorsCore.visit(node.value, this); return { imports: mapKey.imports.mergeWith(mapValue.imports), render: `[${mapKey.render}, ${mapValue.render}]` }; }, visitMapValue(node) { const map = node.entries.map((entry) => visitorsCore.visit(entry, this)); const imports = new ImportMap().add("std::collection::HashMap"); return { imports: imports.mergeWith(...map.map((c) => c.imports)), render: `HashMap::from([${map.map((c) => c.render).join(", ")}])` }; }, visitNoneValue() { return { imports: new ImportMap(), render: "None" }; }, visitNumberValue(node) { return { imports: new ImportMap(), render: node.number.toString() }; }, visitPublicKeyValue(node) { return { imports: new ImportMap().add("solana_program::pubkey"), render: `pubkey!("${node.publicKey}")` }; }, visitSetValue(node) { const set = node.items.map((v) => visitorsCore.visit(v, this)); const imports = new ImportMap().add("std::collection::HashSet"); return { imports: imports.mergeWith(...set.map((c) => c.imports)), render: `HashSet::from([${set.map((c) => c.render).join(", ")}])` }; }, visitSomeValue(node) { const child = visitorsCore.visit(node.value, this); return { ...child, render: `Some(${child.render})` }; }, visitStringValue(node) { return { imports: new ImportMap(), render: useStr ? `${JSON.stringify(node.string)}` : `String::from(${JSON.stringify(node.string)})` }; }, visitStructFieldValue(node) { const structValue = visitorsCore.visit(node.value, this); return { imports: structValue.imports, render: `${node.name}: ${structValue.render}` }; }, visitStructValue(node) { const struct = node.fields.map((field) => visitorsCore.visit(field, this)); return { imports: new ImportMap().mergeWith(...struct.map((c) => c.imports)), render: `{ ${struct.map((c) => c.render).join(", ")} }` }; }, visitTupleValue(node) { const tuple = node.items.map((v) => visitorsCore.visit(v, this)); return { imports: new ImportMap().mergeWith(...tuple.map((c) => c.imports)), render: `(${tuple.map((c) => c.render).join(", ")})` }; } }; } // 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 = options.dependencyMap ?? {}; const getImportFrom = getImportFromFactory(options.linkOverrides ?? {}); const getTraitsFromNode2 = getTraitsFromNodeFactory(options.traitOptions); const typeManifestVisitor = getTypeManifestVisitor({ getImportFrom, getTraitsFromNode: getTraitsFromNode2 }); const anchorTraits = options.anchorTraits ?? true; return visitorsCore.pipe( visitorsCore.staticVisitor(() => new renderersCore.RenderMap(), { keys: ["rootNode", "programNode", "instructionNode", "accountNode", "definedTypeNode"] }), (v) => visitorsCore.extendVisitor(v, { visitAccount(node) { const typeManifest = visitorsCore.visit(node, typeManifestVisitor); const seedsImports = new ImportMap(); 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); seedsImports.mergeWith(seedManifest2.imports); const resolvedType2 = nodes.resolveNestedTypeNode(seed.type); return { ...seed, resolvedType: resolvedType2, typeManifest: seedManifest2 }; } if (nodes.isNode(seed.value, "programIdValueNode")) { return seed; } const seedManifest = visitorsCore.visit(seed.type, typeManifestVisitor); const valueManifest = renderValueNode(seed.value, getImportFrom, true); seedsImports.mergeWith(valueManifest.imports); const resolvedType = nodes.resolveNestedTypeNode(seed.type); return { ...seed, resolvedType, typeManifest: seedManifest, valueManifest }; }); const hasVariableSeeds = pdaSeeds.filter(nodes.isNodeFilter("variablePdaSeedNode")).length > 0; const constantSeeds = seeds.filter(nodes.isNodeFilter("constantPdaSeedNode")).filter((seed) => !nodes.isNode(seed.value, "programIdValueNode")); const { imports } = typeManifest; if (hasVariableSeeds) { imports.mergeWith(seedsImports); } return new renderersCore.RenderMap().add( `accounts/${nodes.snakeCase(node.name)}.rs`, render("accountsPage.njk", { account: node, anchorTraits, constantSeeds, hasVariableSeeds, imports: imports.remove(`generatedAccounts::${nodes.pascalCase(node.name)}`).toString(dependencyMap), pda, program, seeds, typeManifest }) ); }, visitDefinedType(node) { const typeManifest = visitorsCore.visit(node, typeManifestVisitor); const imports = new ImportMap().mergeWithManifest(typeManifest); return new renderersCore.RenderMap().add( `types/${nodes.snakeCase(node.name)}.rs`, render("definedTypesPage.njk", { definedType: node, imports: imports.remove(`generatedTypes::${nodes.pascalCase(node.name)}`).toString(dependencyMap), typeManifest }) ); }, visitInstruction(node) { const imports = new ImportMap(); imports.add(["borsh::BorshDeserialize", "borsh::BorshSerialize"]); const accountsAndArgsConflicts = getConflictsForInstructionAccountsAndArgs(node); if (accountsAndArgsConflicts.length > 0) { errors.logWarn( `[Rust] Accounts and args of instruction [${node.name}] have the following conflicting attributes [${accountsAndArgsConflicts.join(", ")}]. Thus, the conflicting arguments will be suffixed with "_arg". You may want to rename the conflicting attributes.` ); } const instructionArgs = []; let hasArgs = false; let hasOptional = false; node.arguments.forEach((argument) => { const argumentVisitor = getTypeManifestVisitor({ getImportFrom, getTraitsFromNode: getTraitsFromNode2, nestedStruct: true, parentName: `${nodes.pascalCase(node.name)}InstructionData` }); const manifest = visitorsCore.visit(argument.type, argumentVisitor); imports.mergeWith(manifest.imports); const innerOptionType = nodes.isNode(argument.type, "optionTypeNode") ? manifest.type.slice("Option<".length, -1) : null; const hasDefaultValue = !!argument.defaultValue && nodes.isNode(argument.defaultValue, nodes.VALUE_NODES); let renderValue = null; if (hasDefaultValue) { const { imports: argImports, render: value } = renderValueNode( argument.defaultValue, getImportFrom ); imports.mergeWith(argImports); renderValue = value; } hasArgs = hasArgs || argument.defaultValueStrategy !== "omitted"; hasOptional = hasOptional || hasDefaultValue && argument.defaultValueStrategy !== "omitted"; const name = accountsAndArgsConflicts.includes(argument.name) ? `${argument.name}_arg` : argument.name; instructionArgs.push({ default: hasDefaultValue && argument.defaultValueStrategy === "omitted", innerOptionType, name, optional: hasDefaultValue && argument.defaultValueStrategy !== "omitted", type: manifest.type, value: renderValue }); }); const struct = nodes.structTypeNodeFromInstructionArgumentNodes(node.arguments); const structVisitor = getTypeManifestVisitor({ getImportFrom, getTraitsFromNode: getTraitsFromNode2, parentName: `${nodes.pascalCase(node.name)}InstructionData` }); const typeManifest = visitorsCore.visit(struct, structVisitor); return new renderersCore.RenderMap().add( `instructions/${nodes.snakeCase(node.name)}.rs`, render("instructionsPage.njk", { hasArgs, hasOptional, imports: imports.remove(`generatedInstructions::${nodes.pascalCase(node.name)}`).toString(dependencyMap), instruction: node, instructionArgs, program, typeManifest }) ); }, visitProgram(node, { self }) { program = node; const renderMap = new renderersCore.RenderMap().mergeWith(...node.accounts.map((account) => visitorsCore.visit(account, self))).mergeWith(...node.definedTypes.map((type) => visitorsCore.visit(type, self))).mergeWith( ...nodes.getAllInstructionsWithSubs(node, { leavesOnly: !renderParentInstructions }).map((ix) => visitorsCore.visit(ix, self)) ); if (node.errors.length > 0) { renderMap.add( `errors/${nodes.snakeCase(node.name)}.rs`, render("errorsPage.njk", { errors: node.errors, imports: new ImportMap().toString(dependencyMap), program: node }) ); } program = null; return renderMap; }, visitRoot(node, { self }) { const programsToExport = nodes.getAllPrograms(node); const accountsToExport = nodes.getAllAccounts(node); const instructionsToExport = nodes.getAllInstructionsWithSubs(node, { leavesOnly: !renderParentInstructions }); const definedTypesToExport = nodes.getAllDefinedTypes(node); 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 (programsToExport.length > 0) { map.add("programs.rs", render("programsMod.njk", ctx)).add( "errors/mod.rs", render("errorsMod.njk", ctx) ); } if (accountsToExport.length > 0) { map.add("accounts/mod.rs", render("accountsMod.njk", ctx)); } if (instructionsToExport.length > 0) { map.add("instructions/mod.rs", render("instructionsMod.njk", ctx)); } if (definedTypesToExport.length > 0) { map.add("types/mod.rs", render("definedTypesMod.njk", ctx)); } return map.add("mod.rs", render("rootMod.njk", ctx)).mergeWith(...nodes.getAllPrograms(node).map((p) => visitorsCore.visit(p, self))); } }), (v) => visitorsCore.recordNodeStackVisitor(v, stack), (v) => visitorsCore.recordLinkablesOnFirstVisitVisitor(v, linkables) ); } function getConflictsForInstructionAccountsAndArgs(instruction) { const allNames = [ ...instruction.accounts.map((account) => account.name), ...instruction.arguments.map((argument) => argument.name) ]; const duplicates = allNames.filter((e, i, a) => a.indexOf(e) !== i); return [...new Set(duplicates)]; } function renderVisitor(path, options = {}) { return visitorsCore.rootNodeVisitor((root) => { if (options.deleteFolderBeforeRendering ?? true) { renderersCore.deleteDirectory(path); } visitorsCore.visit(root, renderersCore.writeRenderMapVisitor(getRenderMapVisitor(options), path)); if (options.formatCode) { if (options.crateFolder) { const toolchain = options.toolchain ?? "+stable"; runFormatter("cargo", [toolchain, "fmt", "--manifest-path", `${options.crateFolder}/Cargo.toml`]); } else { errors.logWarn("No crate folder specified, skipping formatting."); } } }); } function runFormatter(cmd, args) { const { stdout, stderr, error } = child_process.spawnSync(cmd, args); if (error?.message?.includes("ENOENT")) { errors.logWarn(`Could not find ${cmd}, skipping formatting.`); return; } if (stdout.length > 0) { errors.logWarn(`(cargo-fmt) ${stdout || error}`); } if (stderr.length > 0) { errors.logError(`(cargo-fmt) ${stderr || error}`); } } exports.ImportMap = ImportMap; exports.getRenderMapVisitor = getRenderMapVisitor; exports.getTypeManifestVisitor = getTypeManifestVisitor; exports.renderVisitor = renderVisitor; //# sourceMappingURL=index.node.cjs.map //# sourceMappingURL=index.node.cjs.map