@codama/renderers-js
Version:
JavaScript renderer compatible with the Solana Kit library
1,093 lines (1,078 loc) • 152 kB
JavaScript
import { camelCase, pascalCase, snakeCase, titleCase, kebabCase, capitalize, REGISTERED_TYPE_NODE_KINDS, REGISTERED_VALUE_NODE_KINDS, isNodeFilter, resolveNestedTypeNode, structTypeNodeFromInstructionArgumentNodes, isNode, isScalarEnum, structTypeNode, structFieldTypeNode, getAllPrograms, getAllPdas, getAllAccounts, getAllInstructionsWithSubs, getAllDefinedTypes, definedTypeLinkNode, definedTypeNode, isDataEnum, accountValueNode, argumentValueNode, parseOptionalAccountStrategy, VALUE_NODES, getAllInstructionArguments, constantDiscriminatorNode, constantValueNodeFromBytes, constantValueNode, assertIsNode } from '@codama/nodes';
import { setFragmentContent, mapFragmentContent, createRenderMap, mergeRenderMaps, deleteDirectory, mapRenderMapContentAsync, writeRenderMap, createFragmentTemplate, joinPath, fileExists, readJson, writeFile } from '@codama/renderers-core';
import { NodeStack, pipe, staticVisitor, extendVisitor, visit, findLastNodeFromPath, recordNodeStackVisitor, LinkableDictionary, getResolvedInstructionInputsVisitor, getByteSizeVisitor, recordLinkablesOnFirstVisitVisitor, rootNodeVisitor, findProgramNodeFromPath, getLastNodeFromPath, findInstructionNodeFromPath, deduplicateInstructionDependencies } from '@codama/visitors-core';
import { getBase64Encoder, getBase58Encoder, getBase16Encoder, getUtf8Encoder, getBase64Decoder } from '@solana/codecs-strings';
import { resolveConfig } from 'prettier';
import * as babelPlugin from 'prettier/plugins/babel';
import * as estreePlugin from 'prettier/plugins/estree';
import * as typeScriptPlugin from 'prettier/plugins/typescript';
import { format } from 'prettier/standalone';
import { CodamaError, CODAMA_ERROR__UNEXPECTED_NODE_KIND, logWarn, CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS } from '@codama/errors';
import { subset, minVersion, lt } from 'semver';
// src/utils/importMap.ts
var DEFAULT_EXTERNAL_MODULE_MAP = {
solanaAccounts: "@solana/kit",
solanaAddresses: "@solana/kit",
solanaCodecsCore: "@solana/kit",
solanaCodecsDataStructures: "@solana/kit",
solanaCodecsNumbers: "@solana/kit",
solanaCodecsStrings: "@solana/kit",
solanaErrors: "@solana/kit",
solanaInstructions: "@solana/kit",
solanaOptions: "@solana/kit",
solanaPrograms: "@solana/kit",
solanaRpcTypes: "@solana/kit",
solanaSigners: "@solana/kit"
};
var DEFAULT_GRANULAR_EXTERNAL_MODULE_MAP = {
solanaAccounts: "@solana/accounts",
solanaAddresses: "@solana/addresses",
solanaCodecsCore: "@solana/codecs",
solanaCodecsDataStructures: "@solana/codecs",
solanaCodecsNumbers: "@solana/codecs",
solanaCodecsStrings: "@solana/codecs",
solanaErrors: "@solana/errors",
solanaInstructions: "@solana/instructions",
solanaOptions: "@solana/codecs",
solanaPrograms: "@solana/programs",
solanaRpcTypes: "@solana/rpc-types",
solanaSigners: "@solana/signers"
};
var DEFAULT_INTERNAL_MODULE_MAP = {
errors: "../errors",
generated: "..",
generatedAccounts: "../accounts",
generatedErrors: "../errors",
generatedInstructions: "../instructions",
generatedPdas: "../pdas",
generatedPrograms: "../programs",
generatedTypes: "../types",
hooked: "../../hooked",
shared: "../shared",
types: "../types"
};
function createImportMap() {
return Object.freeze(/* @__PURE__ */ new Map());
}
function parseImportInput(input) {
const matches = input.match(/^(type )?([^ ]+)(?: as (.+))?$/);
if (!matches) return Object.freeze({ importedIdentifier: input, isType: false, usedIdentifier: input });
const [_, isType, name, alias] = matches;
return Object.freeze({
importedIdentifier: name,
isType: !!isType,
usedIdentifier: alias ?? name
});
}
function addToImportMap(importMap, module, imports) {
const parsedImports = imports.map(parseImportInput).map((i) => [i.usedIdentifier, i]);
return mergeImportMaps([importMap, /* @__PURE__ */ new Map([[module, new Map(parsedImports)]])]);
}
function removeFromImportMap(importMap, module, usedIdentifiers) {
const newMap = new Map(importMap);
const newModuleMap = new Map(newMap.get(module));
usedIdentifiers.forEach((usedIdentifier) => {
newModuleMap.delete(usedIdentifier);
});
if (newModuleMap.size === 0) {
newMap.delete(module);
} else {
newMap.set(module, newModuleMap);
}
return Object.freeze(newMap);
}
function mergeImportMaps(importMaps) {
if (importMaps.length === 0) return createImportMap();
if (importMaps.length === 1) return importMaps[0];
const mergedMap = new Map(importMaps[0]);
for (const map of importMaps.slice(1)) {
for (const [module, imports] of map) {
const mergedModuleMap = mergedMap.get(module) ?? /* @__PURE__ */ new Map();
for (const [usedIdentifier, importInfo] of imports) {
const existingImportInfo = mergedModuleMap.get(usedIdentifier);
const shouldOverwriteTypeOnly = existingImportInfo && existingImportInfo.importedIdentifier === importInfo.importedIdentifier && existingImportInfo.isType && !importInfo.isType;
if (!existingImportInfo || shouldOverwriteTypeOnly) {
mergedModuleMap.set(usedIdentifier, importInfo);
}
}
mergedMap.set(module, mergedModuleMap);
}
}
return Object.freeze(mergedMap);
}
function importMapToString(importMap, dependencyMap = {}, useGranularImports = false) {
const resolvedMap = resolveImportMapModules(importMap, dependencyMap, useGranularImports);
return [...resolvedMap.entries()].sort(([a], [b]) => {
const relative = Number(a.startsWith(".")) - Number(b.startsWith("."));
if (relative !== 0) return relative;
return a.localeCompare(b);
}).map(([module, imports]) => {
const innerImports = [...imports.values()].map(importInfoToString).sort((a, b) => a.localeCompare(b)).join(", ");
return `import { ${innerImports} } from '${module}';`;
}).join("\n");
}
function getExternalDependencies(importMap, dependencyMap, useGranularImports) {
const resolvedImports = resolveImportMapModules(importMap, dependencyMap, useGranularImports);
return new Set([...resolvedImports.keys()].filter((module) => !module.startsWith(".")));
}
function resolveImportMapModules(importMap, dependencyMap, useGranularImports) {
const dependencyMapWithDefaults = {
...useGranularImports ? DEFAULT_GRANULAR_EXTERNAL_MODULE_MAP : DEFAULT_EXTERNAL_MODULE_MAP,
...DEFAULT_INTERNAL_MODULE_MAP,
...dependencyMap
};
return mergeImportMaps(
[...importMap.entries()].map(([module, imports]) => {
const resolvedModule = dependencyMapWithDefaults[module] ?? module;
return /* @__PURE__ */ new Map([[resolvedModule, imports]]);
})
);
}
function importInfoToString({ importedIdentifier, isType, usedIdentifier }) {
const alias = importedIdentifier !== usedIdentifier ? ` as ${usedIdentifier}` : "";
return `${isType ? "type " : ""}${importedIdentifier}${alias}`;
}
function getNameApi(transformers) {
const helpers = {
camelCase,
capitalize,
kebabCase,
pascalCase,
snakeCase,
titleCase
};
return Object.fromEntries(
Object.entries(transformers).map(([key, transformer]) => [key, (name) => transformer(name, helpers)])
);
}
var DEFAULT_NAME_TRANSFORMERS = {
accountDecodeFunction: (name) => `decode${pascalCase(name)}`,
accountFetchAllFunction: (name) => `fetchAll${pascalCase(name)}`,
accountFetchAllMaybeFunction: (name) => `fetchAllMaybe${pascalCase(name)}`,
accountFetchFromSeedsFunction: (name) => `fetch${pascalCase(name)}FromSeeds`,
accountFetchFunction: (name) => `fetch${pascalCase(name)}`,
accountFetchMaybeFromSeedsFunction: (name) => `fetchMaybe${pascalCase(name)}FromSeeds`,
accountFetchMaybeFunction: (name) => `fetchMaybe${pascalCase(name)}`,
accountGetSizeFunction: (name) => `get${pascalCase(name)}Size`,
codecFunction: (name) => `get${pascalCase(name)}Codec`,
constant: (name) => snakeCase(name).toUpperCase(),
constantFunction: (name) => `get${pascalCase(name)}Bytes`,
dataArgsType: (name) => `${pascalCase(name)}Args`,
dataType: (name) => `${pascalCase(name)}`,
decoderFunction: (name) => `get${pascalCase(name)}Decoder`,
discriminatedUnionDiscriminator: () => "__kind",
discriminatedUnionFunction: (name) => `${camelCase(name)}`,
discriminatedUnionVariant: (name) => `${pascalCase(name)}`,
encoderFunction: (name) => `get${pascalCase(name)}Encoder`,
enumVariant: (name) => `${pascalCase(name)}`,
instructionAsyncFunction: (name) => `get${pascalCase(name)}InstructionAsync`,
instructionAsyncInputType: (name) => `${pascalCase(name)}AsyncInput`,
instructionDataType: (name) => `${pascalCase(name)}InstructionData`,
instructionExtraType: (name) => `${pascalCase(name)}InstructionExtra`,
instructionParseFunction: (name) => `parse${pascalCase(name)}Instruction`,
instructionParsedType: (name) => `Parsed${pascalCase(name)}Instruction`,
instructionSyncFunction: (name) => `get${pascalCase(name)}Instruction`,
instructionSyncInputType: (name) => `${pascalCase(name)}Input`,
instructionType: (name) => `${pascalCase(name)}Instruction`,
isDiscriminatedUnionFunction: (name) => `is${pascalCase(name)}`,
pdaFindFunction: (name) => `find${pascalCase(name)}Pda`,
pdaSeedsType: (name) => `${pascalCase(name)}Seeds`,
programAccountsEnum: (name) => `${pascalCase(name)}Account`,
programAccountsEnumVariant: (name) => `${pascalCase(name)}`,
programAccountsIdentifierFunction: (name) => `identify${pascalCase(name)}Account`,
programAddressConstant: (name) => `${snakeCase(name).toUpperCase()}_PROGRAM_ADDRESS`,
programErrorConstant: (name) => snakeCase(name).toUpperCase(),
programErrorConstantPrefix: (name) => `${snakeCase(name).toUpperCase()}_ERROR__`,
programErrorMessagesMap: (name) => `${camelCase(name)}ErrorMessages`,
programErrorUnion: (name) => `${pascalCase(name)}Error`,
programGetErrorMessageFunction: (name) => `get${pascalCase(name)}ErrorMessage`,
programInstructionsEnum: (name) => `${pascalCase(name)}Instruction`,
programInstructionsEnumVariant: (name) => `${pascalCase(name)}`,
programInstructionsIdentifierFunction: (name) => `identify${pascalCase(name)}Instruction`,
programInstructionsParsedUnionType: (name) => `Parsed${pascalCase(name)}Instruction`,
programIsErrorFunction: (name) => `is${pascalCase(name)}Error`,
resolverFunction: (name) => `${camelCase(name)}`
};
function createFragment(content) {
return Object.freeze({ content, features: /* @__PURE__ */ new Set(), imports: createImportMap() });
}
function isFragment(value) {
return typeof value === "object" && value !== null && "content" in value;
}
function fragment(template, ...items) {
return createFragmentTemplate(template, items, isFragment, mergeFragments);
}
function mergeFragments(fragments, mergeContent) {
const filteredFragments = fragments.filter((f) => f !== void 0);
return Object.freeze({
content: mergeContent(filteredFragments.map((fragment2) => fragment2.content)),
features: new Set(filteredFragments.flatMap((f) => [...f.features])),
imports: mergeImportMaps(filteredFragments.map((f) => f.imports))
});
}
function use(importInput, module) {
const importInfo = parseImportInput(importInput);
return addFragmentImports(createFragment(importInfo.usedIdentifier), module, [importInput]);
}
function mergeFragmentImports(fragment2, importMaps) {
return Object.freeze({ ...fragment2, imports: mergeImportMaps([fragment2.imports, ...importMaps]) });
}
function addFragmentImports(fragment2, module, importInputs) {
return Object.freeze({ ...fragment2, imports: addToImportMap(fragment2.imports, module, importInputs) });
}
function removeFragmentImports(fragment2, module, usedIdentifiers) {
return Object.freeze({ ...fragment2, imports: removeFromImportMap(fragment2.imports, module, usedIdentifiers) });
}
function addFragmentFeatures(fragment2, features) {
return Object.freeze({ ...fragment2, features: /* @__PURE__ */ new Set([...fragment2.features, ...features]) });
}
function getExportAllFragment(module) {
return fragment`export * from '${module}';`;
}
function getDocblockFragment(lines, withLineJump = false) {
const lineJump = withLineJump ? "\n" : "";
if (lines.length === 0) return;
if (lines.length === 1) return fragment`/** ${lines[0]} */${lineJump}`;
const prefixedLines = lines.map((line) => line ? ` * ${line}` : " *");
return fragment`/**\n${prefixedLines.join("\n")}\n */${lineJump}`;
}
function getPageFragment(page, scope) {
const header = getDocblockFragment([
"This code was AUTOGENERATED using the Codama library.",
"Please DO NOT EDIT THIS FILE, instead use visitors",
"to add features, then rerun Codama to update it.",
"",
"@see https://github.com/codama-idl/codama"
]);
const imports = page.imports.size === 0 ? void 0 : fragment`${importMapToString(page.imports, scope.dependencyMap, scope.useGranularImports)}`;
return mergeFragments([header, imports, page], (cs) => cs.join("\n\n"));
}
// src/utils/typeManifest.ts
function typeManifest(input = {}) {
return Object.freeze({
decoder: fragment``,
encoder: fragment``,
isEnum: false,
looseType: fragment``,
strictType: fragment``,
value: fragment``,
...input
});
}
function mergeTypeManifests(manifests, options = {}) {
const { mergeTypes, mergeCodecs, mergeValues } = options;
const merge = (fragmentFn, mergeFn) => mergeFn ? mergeFragments(manifests.map(fragmentFn), mergeFn) : fragment``;
return Object.freeze({
decoder: merge((m) => m.decoder, mergeCodecs),
encoder: merge((m) => m.encoder, mergeCodecs),
isEnum: false,
looseType: merge((m) => m.looseType, mergeTypes),
strictType: merge((m) => m.strictType, mergeTypes),
value: merge((m) => m.value, mergeValues)
});
}
function hasAsyncFunction(instructionNode, resolvedInputs, asyncResolvers) {
const hasByteDeltasAsync = (instructionNode.byteDeltas ?? []).some(
({ value }) => isNode(value, "resolverValueNode") && asyncResolvers.includes(value.name)
);
const hasRemainingAccountsAsync = (instructionNode.remainingAccounts ?? []).some(
({ value }) => isNode(value, "resolverValueNode") && asyncResolvers.includes(value.name)
);
return hasAsyncDefaultValues(resolvedInputs, asyncResolvers) || hasByteDeltasAsync || hasRemainingAccountsAsync;
}
function hasAsyncDefaultValues(resolvedInputs, asyncResolvers) {
return resolvedInputs.some(
(input) => !!input.defaultValue && isAsyncDefaultValue(input.defaultValue, asyncResolvers)
);
}
function isAsyncDefaultValue(defaultValue, asyncResolvers) {
switch (defaultValue.kind) {
case "pdaValueNode":
return true;
case "resolverValueNode":
return asyncResolvers.includes(defaultValue.name);
case "conditionalValueNode":
return isAsyncDefaultValue(defaultValue.condition, asyncResolvers) || (defaultValue.ifFalse == null ? false : isAsyncDefaultValue(defaultValue.ifFalse, asyncResolvers)) || (defaultValue.ifTrue == null ? false : isAsyncDefaultValue(defaultValue.ifTrue, asyncResolvers));
default:
return false;
}
}
function getInstructionDependencies(input, asyncResolvers, useAsync) {
if (isNode(input, "instructionNode")) {
return deduplicateInstructionDependencies([
...input.accounts.flatMap((x) => getInstructionDependencies(x, asyncResolvers, useAsync)),
...input.arguments.flatMap((x) => getInstructionDependencies(x, asyncResolvers, useAsync)),
...(input.extraArguments ?? []).flatMap((x) => getInstructionDependencies(x, asyncResolvers, useAsync))
]);
}
if (!input.defaultValue) return [];
const getNestedDependencies = (defaultValue) => {
if (!defaultValue) return [];
return getInstructionDependencies({ ...input, defaultValue }, asyncResolvers, useAsync);
};
if (isNode(input.defaultValue, ["accountValueNode", "accountBumpValueNode"])) {
return [accountValueNode(input.defaultValue.name)];
}
if (isNode(input.defaultValue, ["argumentValueNode"])) {
return [argumentValueNode(input.defaultValue.name)];
}
if (isNode(input.defaultValue, "pdaValueNode")) {
const dependencies = /* @__PURE__ */ new Map();
input.defaultValue.seeds.forEach((seed) => {
if (isNode(seed.value, ["accountValueNode", "argumentValueNode"])) {
dependencies.set(seed.value.name, { ...seed.value });
}
});
return [...dependencies.values()];
}
if (isNode(input.defaultValue, "resolverValueNode")) {
const isSynchronousResolver = !asyncResolvers.includes(input.defaultValue.name);
if (useAsync || isSynchronousResolver) {
return input.defaultValue.dependsOn ?? [];
}
}
if (isNode(input.defaultValue, "conditionalValueNode")) {
return deduplicateInstructionDependencies([
...getNestedDependencies(input.defaultValue.condition),
...getNestedDependencies(input.defaultValue.ifTrue),
...getNestedDependencies(input.defaultValue.ifFalse)
]);
}
return [];
}
function getBytesFromBytesValueNode(node) {
switch (node.encoding) {
case "utf8":
return getUtf8Encoder().encode(node.data);
case "base16":
return getBase16Encoder().encode(node.data);
case "base58":
return getBase58Encoder().encode(node.data);
case "base64":
default:
return getBase64Encoder().encode(node.data);
}
}
var parseCustomDataOptions = (customDataOptions, defaultSuffix) => new Map(
customDataOptions.map((o) => {
const options = typeof o === "string" ? { name: o } : o;
const importAs = camelCase(options.importAs ?? `${options.name}${defaultSuffix}`);
const importFrom = options.importFrom ?? "hooked";
return [
camelCase(options.name),
{
extract: options.extract ?? false,
extractAs: options.extractAs ? camelCase(options.extractAs) : importAs,
importAs,
importFrom,
linkNode: definedTypeLinkNode(importAs)
}
];
})
);
var getDefinedTypeNodesToExtract = (nodes, parsedCustomDataOptions) => nodes.flatMap((node) => {
const options = parsedCustomDataOptions.get(node.name);
if (!options || !options.extract) return [];
if (isNode(node, "accountNode")) {
return [definedTypeNode({ name: options.extractAs, type: { ...node.data } })];
}
return [
definedTypeNode({
name: options.extractAs,
type: structTypeNodeFromInstructionArgumentNodes(node.arguments)
})
];
});
var DEFAULT_PRETTIER_OPTIONS = {
plugins: [estreePlugin, typeScriptPlugin, babelPlugin]
};
async function getCodeFormatter(options) {
const shouldFormatCode = options.formatCode ?? true;
if (!shouldFormatCode) return (code) => Promise.resolve(code);
const prettierOptions = {
...DEFAULT_PRETTIER_OPTIONS,
...await resolvePrettierOptions(options.packageFolder),
...options.prettierOptions
};
return (code, filepath) => format(code, { ...prettierOptions, filepath });
}
async function resolvePrettierOptions(packageFolder) {
if (!packageFolder) return null;
const filePathToResolve = joinPath(packageFolder, "package.json");
return await resolveConfig(filePathToResolve);
}
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] ?? "generatedPdas";
case "programLinkNode":
return linkOverrides.programs[node.name] ?? "generatedPrograms";
case "resolverValueNode":
return linkOverrides.resolvers[node.name] ?? "hooked";
default:
throw new CodamaError(CODAMA_ERROR__UNEXPECTED_NODE_KIND, {
expectedKinds: [
"AccountLinkNode",
"DefinedTypeLinkNode",
"InstructionLinkNode",
"PdaLinkNode",
"ProgramLinkNode",
"resolverValueNode"
],
kind,
node
});
}
};
}
var DEFAULT_DEPENDENCY_VERSIONS = {
"@solana/accounts": "^5.0.0",
"@solana/addresses": "^5.0.0",
"@solana/codecs": "^5.0.0",
"@solana/errors": "^5.0.0",
"@solana/instructions": "^5.0.0",
"@solana/kit": "^5.0.0",
"@solana/programs": "^5.0.0",
"@solana/rpc-types": "^5.0.0",
"@solana/signers": "^5.0.0"
};
async function syncPackageJson(renderMap, formatCode, options) {
const shouldSyncPackageJson = options.syncPackageJson ?? false;
const packageFolder = options.packageFolder;
if (!packageFolder) {
if (shouldSyncPackageJson) {
logWarn("Cannot sync package.json. Please provide the 'packageFolder' option.");
}
return;
}
const packageJsonPath = joinPath(packageFolder, "package.json");
const usedDependencies = getUsedDependencyVersions(
renderMap,
options.dependencyMap ?? {},
options.dependencyVersions ?? {},
options.useGranularImports ?? false
);
if (!shouldSyncPackageJson) {
if (fileExists(packageJsonPath)) {
checkExistingPackageJson(readJson(packageJsonPath), usedDependencies);
}
return;
}
if (fileExists(packageJsonPath)) {
const packageJson = updateExistingPackageJson(readJson(packageJsonPath), usedDependencies);
await writePackageJson(packageJson, packageJsonPath, formatCode);
} else {
const packageJson = createNewPackageJson(usedDependencies);
await writePackageJson(packageJson, packageJsonPath, formatCode);
}
}
function createNewPackageJson(dependencyVersions) {
return updateExistingPackageJson(
{
name: "js-client",
version: "1.0.0",
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
description: "",
main: "src/index.ts",
scripts: { test: 'echo "Error: no test specified" && exit 1' },
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
keywords: [],
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
author: ""
},
dependencyVersions
);
}
function updateExistingPackageJson(packageJson, dependencyVersions) {
const updatedDependencies = { ...packageJson.dependencies };
const updatedPeerDependencies = { ...packageJson.peerDependencies };
const updatedDevDependencies = { ...packageJson.devDependencies };
for (const [dependency, requiredRange] of Object.entries(dependencyVersions)) {
let found = false;
if (updatedDependencies[dependency]) {
updateDependency(updatedDependencies, dependency, requiredRange);
found = true;
}
if (updatedPeerDependencies[dependency]) {
updateDependency(updatedPeerDependencies, dependency, requiredRange);
found = true;
}
if (updatedDevDependencies[dependency]) {
updateDependency(updatedDevDependencies, dependency, requiredRange);
found = true;
}
if (!found) {
const dependencyGroupToAdd = dependency === "@solana/kit" ? updatedPeerDependencies : updatedDependencies;
dependencyGroupToAdd[dependency] = requiredRange;
}
}
return {
...packageJson,
...Object.entries(updatedPeerDependencies).length > 0 ? { peerDependencies: updatedPeerDependencies } : {},
...Object.entries(updatedDependencies).length > 0 ? { dependencies: updatedDependencies } : {},
...Object.entries(updatedDevDependencies).length > 0 ? { devDependencies: updatedDevDependencies } : {}
};
}
function checkExistingPackageJson(packageJson, dependencyVersions) {
const missingDependencies = [];
const dependenciesToUpdate = [];
const existingDependencies = {
...packageJson.devDependencies,
...packageJson.peerDependencies,
...packageJson.dependencies
};
for (const [dependency, requiredRange] of Object.entries(dependencyVersions)) {
if (!existingDependencies[dependency]) {
missingDependencies.push(dependency);
} else if (shouldUpdateRange(dependency, existingDependencies[dependency], requiredRange)) {
dependenciesToUpdate.push(dependency);
}
}
if (missingDependencies.length === 0 && dependenciesToUpdate.length === 0) return;
const missingList = missingDependencies.map((d) => `- ${d} missing: ${dependencyVersions[d]}
`).join("");
const outdatedList = dependenciesToUpdate.map((d) => `- ${d} outdated: ${existingDependencies[d]} -> ${dependencyVersions[d]}
`).join("");
logWarn(
`The following dependencies in your \`package.json\` are out-of-date or missing:
${missingList}${outdatedList}`
);
}
function getUsedDependencyVersions(renderMap, dependencyMap, dependencyVersions, useGranularImports) {
const dependencyVersionsWithDefaults = {
...DEFAULT_DEPENDENCY_VERSIONS,
...dependencyVersions
};
const fragment2 = mergeFragments([...renderMap.values()], () => "");
const usedDependencies = getExternalDependencies(fragment2.imports, dependencyMap, useGranularImports);
const [usedDependencyVersion, missingDependencies] = [...usedDependencies].reduce(
([acc, missingDependencies2], dependency) => {
const version = dependencyVersionsWithDefaults[dependency];
if (version) {
acc[dependency] = version;
} else {
missingDependencies2.add(dependency);
}
return [acc, missingDependencies2];
},
[{}, /* @__PURE__ */ new Set()]
);
if (missingDependencies.size > 0) {
throw new CodamaError(CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, {
dependencies: [...missingDependencies],
message: "Please add these dependencies to the `dependencyVersions` option."
});
}
return usedDependencyVersion;
}
function shouldUpdateRange(dependency, currentRange, requiredRange) {
try {
if (subset(currentRange, requiredRange)) {
return false;
}
const minRequiredVersion = minVersion(requiredRange);
const minCurrentVersion = minVersion(currentRange);
if (!minCurrentVersion || !minRequiredVersion) {
throw new Error("Could not determine minimum versions.");
}
if (lt(minCurrentVersion, minRequiredVersion)) {
return true;
}
return false;
} catch (error) {
console.warn(
`Could not parse the following ranges for dependency "${dependency}": [${currentRange}] and/or [${requiredRange}]. Caused by: ${error.message}`
);
return false;
}
}
function updateDependency(dependencyGroup, dependency, requiredRange) {
const currentRange = dependencyGroup[dependency];
if (!shouldUpdateRange(dependency, currentRange, requiredRange)) return;
dependencyGroup[dependency] = requiredRange;
}
async function writePackageJson(packageJson, packageJsonPath, formatCode) {
const packageJsonContent = JSON.stringify(packageJson, null, 2) + "\n";
const formattedContent = await formatCode(packageJsonContent, packageJsonPath);
writeFile(packageJsonPath, formattedContent);
}
// src/fragments/accountFetchHelpers.ts
function getAccountFetchHelpersFragment(scope) {
const { accountPath, typeManifest: typeManifest2, nameApi, customAccountData } = scope;
const accountNode = getLastNodeFromPath(accountPath);
const decodeFunction = nameApi.accountDecodeFunction(accountNode.name);
const fetchAllFunction = nameApi.accountFetchAllFunction(accountNode.name);
const fetchAllMaybeFunction = nameApi.accountFetchAllMaybeFunction(accountNode.name);
const fetchFunction = nameApi.accountFetchFunction(accountNode.name);
const fetchMaybeFunction = nameApi.accountFetchMaybeFunction(accountNode.name);
const hasCustomData = customAccountData.has(accountNode.name);
const accountType = hasCustomData ? typeManifest2.strictType : nameApi.dataType(accountNode.name);
const decoderFunction = hasCustomData ? typeManifest2.decoder : `${nameApi.decoderFunction(accountNode.name)}()`;
return pipe(
fragment`export function ${decodeFunction}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress>): Account<${accountType}, TAddress>;
export function ${decodeFunction}<TAddress extends string = string>(encodedAccount: MaybeEncodedAccount<TAddress>): MaybeAccount<${accountType}, TAddress>;
export function ${decodeFunction}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>): Account<${accountType}, TAddress> | MaybeAccount<${accountType}, TAddress> {
return decodeAccount(encodedAccount as MaybeEncodedAccount<TAddress>, ${decoderFunction});
}
export async function ${fetchFunction}<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig,
): Promise<Account<${accountType}, TAddress>> {
const maybeAccount = await ${fetchMaybeFunction}(rpc, address, config);
assertAccountExists(maybeAccount);
return maybeAccount;
}
export async function ${fetchMaybeFunction}<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig,
): Promise<MaybeAccount<${accountType}, TAddress>> {
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
return ${decodeFunction}(maybeAccount);
}
export async function ${fetchAllFunction}(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig,
): Promise<Account<${accountType}>[]> {
const maybeAccounts = await ${fetchAllMaybeFunction}(rpc, addresses, config);
assertAccountsExist(maybeAccounts);
return maybeAccounts;
}
export async function ${fetchAllMaybeFunction}(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig,
): Promise<MaybeAccount<${accountType}>[]> {
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
return maybeAccounts.map((maybeAccount) => ${decodeFunction}(maybeAccount));
}`,
(f) => addFragmentImports(f, "solanaAddresses", ["type Address"]),
(f) => addFragmentImports(f, "solanaAccounts", [
"type Account",
"assertAccountExists",
"assertAccountsExist",
"decodeAccount",
"type EncodedAccount",
"fetchEncodedAccount",
"fetchEncodedAccounts",
"type FetchAccountConfig",
"type FetchAccountsConfig",
"type MaybeAccount",
"type MaybeEncodedAccount"
])
);
}
function getAccountPdaHelpersFragment(scope) {
const { accountPath, nameApi, linkables, customAccountData, typeManifest: typeManifest2 } = scope;
const accountNode = getLastNodeFromPath(accountPath);
const pdaNode = accountNode.pda ? linkables.get([...accountPath, accountNode.pda]) : void 0;
if (!pdaNode) return;
const accountType = customAccountData.has(accountNode.name) ? typeManifest2.strictType : nameApi.dataType(accountNode.name);
const importFrom = "generatedPdas";
const pdaSeedsType = nameApi.pdaSeedsType(pdaNode.name);
const findPdaFunction = nameApi.pdaFindFunction(pdaNode.name);
const hasVariableSeeds = pdaNode.seeds.filter(isNodeFilter("variablePdaSeedNode")).length > 0;
const fetchFromSeedsFunction = nameApi.accountFetchFromSeedsFunction(accountNode.name);
const fetchMaybeFromSeedsFunction = nameApi.accountFetchMaybeFromSeedsFunction(accountNode.name);
const fetchMaybeFunction = nameApi.accountFetchMaybeFunction(accountNode.name);
return pipe(
fragment`export async function ${fetchFromSeedsFunction}(
rpc: Parameters<typeof fetchEncodedAccount>[0],
${hasVariableSeeds ? `seeds: ${pdaSeedsType},` : ""}
config: FetchAccountConfig & { programAddress?: Address } = {},
): Promise<Account<${accountType}>> {
const maybeAccount = await ${fetchMaybeFromSeedsFunction}(rpc, ${hasVariableSeeds ? "seeds, " : ""}config);
assertAccountExists(maybeAccount);
return maybeAccount;
}
export async function ${fetchMaybeFromSeedsFunction}(
rpc: Parameters<typeof fetchEncodedAccount>[0],
${hasVariableSeeds ? `seeds: ${pdaSeedsType},` : ""}
config: FetchAccountConfig & { programAddress?: Address } = {},
): Promise<MaybeAccount<${accountType}>> {
const { programAddress, ...fetchConfig } = config;
const [address] = await ${findPdaFunction}(${hasVariableSeeds ? "seeds, " : ""}{ programAddress });
return await ${fetchMaybeFunction}(rpc, address, fetchConfig);
}`,
(f) => addFragmentImports(f, importFrom, hasVariableSeeds ? [pdaSeedsType, findPdaFunction] : [findPdaFunction]),
(f) => addFragmentImports(f, "solanaAddresses", ["type Address"]),
(f) => addFragmentImports(f, "solanaAccounts", [
"type Account",
"assertAccountExists",
"type FetchAccountConfig",
"type MaybeAccount"
])
);
}
function getAccountSizeHelpersFragment(scope) {
const { accountPath, nameApi } = scope;
const accountNode = getLastNodeFromPath(accountPath);
if (accountNode.size == null) return;
const getSizeFunction = nameApi.accountGetSizeFunction(accountNode.name);
return fragment`export function ${getSizeFunction}(): number {
return ${accountNode.size};
}`;
}
// src/fragments/type.ts
function getTypeFragment(scope) {
const { name, manifest, nameApi, docs = [] } = scope;
const docblock = getDocblockFragment(docs, true);
const strictName = nameApi.dataType(name);
const looseName = nameApi.dataArgsType(name);
const aliasedLooseName = `export type ${looseName} = ${strictName};`;
if (manifest.isEnum) {
return fragment`${docblock}export enum ${strictName} ${manifest.strictType};\n\n${aliasedLooseName}`;
}
const looseExport = manifest.strictType.content === manifest.looseType.content ? aliasedLooseName : fragment`export type ${looseName} = ${manifest.looseType};`;
return fragment`${docblock}export type ${strictName} = ${manifest.strictType};\n\n${looseExport}`;
}
function getTypeDecoderFragment(scope) {
const { name, node, manifest, nameApi, docs = [] } = scope;
const decoderFunction = nameApi.decoderFunction(name);
const strictName = nameApi.dataType(name);
const docblock = getDocblockFragment(docs, true);
const decoderType = use(
typeof scope.size === "number" ? "type FixedSizeDecoder" : "type Decoder",
"solanaCodecsCore"
);
const useTypeCast = isNode(node, "enumTypeNode") && isDataEnum(node) && typeof scope.size === "number";
const typeCast = useTypeCast ? fragment` as ${decoderType}<${strictName}>` : "";
return fragment`${docblock}export function ${decoderFunction}(): ${decoderType}<${strictName}> {
return ${manifest.decoder}${typeCast};
}`;
}
function getTypeEncoderFragment(scope) {
const { name, node, manifest, nameApi, docs = [] } = scope;
const encoderFunction = nameApi.encoderFunction(name);
const looseName = nameApi.dataArgsType(name);
const docblock = getDocblockFragment(docs, true);
const encoderType = use(
typeof scope.size === "number" ? "type FixedSizeEncoder" : "type Encoder",
"solanaCodecsCore"
);
const useTypeCast = isNode(node, "enumTypeNode") && isDataEnum(node) && typeof scope.size === "number";
const typeCast = useTypeCast ? fragment` as ${encoderType}<${looseName}>` : "";
return fragment`${docblock}export function ${encoderFunction}(): ${encoderType}<${looseName}> {
return ${manifest.encoder}${typeCast};
}`;
}
// src/fragments/typeCodec.ts
function getTypeCodecFragment(scope) {
const { codecDocs = [], name, nameApi } = scope;
const codecFunction = nameApi.codecFunction(name);
const decoderFunction = nameApi.decoderFunction(name);
const encoderFunction = nameApi.encoderFunction(name);
const looseName = nameApi.dataArgsType(name);
const strictName = nameApi.dataType(name);
const docblock = getDocblockFragment(codecDocs, true);
const codecType = use(typeof scope.size === "number" ? "type FixedSizeCodec" : "type Codec", "solanaCodecsCore");
return mergeFragments(
[
getTypeEncoderFragment({ ...scope, docs: scope.encoderDocs }),
getTypeDecoderFragment({ ...scope, docs: scope.decoderDocs }),
fragment`${docblock}export function ${codecFunction}(): ${codecType}<${looseName}, ${strictName}> {
return ${use("combineCodec", "solanaCodecsCore")}(${encoderFunction}(), ${decoderFunction}());
}`
],
(renders) => renders.join("\n\n")
);
}
// src/fragments/typeWithCodec.ts
function getTypeWithCodecFragment(scope) {
return mergeFragments(
[getTypeFragment({ ...scope, docs: scope.typeDocs }), getTypeCodecFragment(scope)],
(renders) => renders.join("\n\n")
);
}
// src/fragments/accountType.ts
function getAccountTypeFragment(scope) {
const { accountPath, typeManifest: typeManifest2, nameApi, customAccountData } = scope;
const accountNode = getLastNodeFromPath(accountPath);
if (customAccountData.has(accountNode.name)) return;
return getTypeWithCodecFragment({
codecDocs: [`Gets the codec for {@link ${nameApi.dataType(accountNode.name)}} account data.`],
decoderDocs: [`Gets the decoder for {@link ${nameApi.dataType(accountNode.name)}} account data.`],
encoderDocs: [`Gets the encoder for {@link ${nameApi.dataArgsType(accountNode.name)}} account data.`],
manifest: typeManifest2,
name: accountNode.name,
nameApi,
node: resolveNestedTypeNode(accountNode.data),
size: scope.size,
typeDocs: accountNode.docs
});
}
function getDiscriminatorConstantsFragment(scope) {
const fragments = scope.discriminatorNodes.map((node) => getDiscriminatorConstantFragment(node, scope)).filter(Boolean);
return mergeFragments(fragments, (c) => c.join("\n\n"));
}
function getDiscriminatorConstantFragment(discriminatorNode, scope) {
switch (discriminatorNode.kind) {
case "constantDiscriminatorNode":
return getConstantDiscriminatorConstantFragment(discriminatorNode, scope);
case "fieldDiscriminatorNode":
return getFieldDiscriminatorConstantFragment(discriminatorNode, scope);
default:
return null;
}
}
function getConstantDiscriminatorConstantFragment(discriminatorNode, scope) {
const { discriminatorNodes, typeManifestVisitor, prefix } = scope;
const index = discriminatorNodes.filter(isNodeFilter("constantDiscriminatorNode")).indexOf(discriminatorNode);
const suffix = index <= 0 ? "" : `_${index + 1}`;
const name = camelCase(`${prefix}_discriminator${suffix}`);
const encoder = visit(discriminatorNode.constant.type, typeManifestVisitor).encoder;
const value = visit(discriminatorNode.constant.value, typeManifestVisitor).value;
return getConstantFragment({ ...scope, encoder, name, value });
}
function getFieldDiscriminatorConstantFragment(discriminatorNode, scope) {
const { fields, prefix, typeManifestVisitor } = scope;
const field = fields.find((f) => f.name === discriminatorNode.name);
if (!field || !field.defaultValue || !isNode(field.defaultValue, VALUE_NODES)) {
return null;
}
const name = camelCase(`${prefix}_${discriminatorNode.name}`);
const encoder = visit(field.type, typeManifestVisitor).encoder;
const value = visit(field.defaultValue, typeManifestVisitor).value;
return getConstantFragment({ ...scope, encoder, name, value });
}
function getConstantFragment(scope) {
const { encoder, name, nameApi, value } = scope;
const constantName = nameApi.constant(name);
const constantFunction = nameApi.constantFunction(name);
return fragment`export const ${constantName} = ${value};\n\nexport function ${constantFunction}() { return ${encoder}.encode(${constantName}); }`;
}
// src/fragments/accountPage.ts
function getAccountPageFragment(scope) {
const node = getLastNodeFromPath(scope.accountPath);
if (!findProgramNodeFromPath(scope.accountPath)) {
throw new Error("Account must be visited inside a program.");
}
const typeManifest2 = visit(node, scope.typeManifestVisitor);
const fields = resolveNestedTypeNode(node.data).fields;
return mergeFragments(
[
getDiscriminatorConstantsFragment({
...scope,
discriminatorNodes: node.discriminators ?? [],
fields,
prefix: node.name
}),
getAccountTypeFragment({ ...scope, typeManifest: typeManifest2 }),
getAccountFetchHelpersFragment({ ...scope, typeManifest: typeManifest2 }),
getAccountSizeHelpersFragment(scope),
getAccountPdaHelpersFragment({ ...scope, typeManifest: typeManifest2 })
],
(cs) => cs.join("\n\n")
);
}
function getDiscriminatorConditionFragment(scope) {
return pipe(
mergeFragments(
scope.discriminators.flatMap((discriminator) => {
if (isNode(discriminator, "sizeDiscriminatorNode")) {
return [getSizeConditionFragment(discriminator, scope)];
}
if (isNode(discriminator, "constantDiscriminatorNode")) {
return [getByteConditionFragment(discriminator, scope)];
}
if (isNode(discriminator, "fieldDiscriminatorNode")) {
return [getFieldConditionFragment(discriminator, scope)];
}
return [];
}),
(c) => c.join(" && ")
),
(f) => mapFragmentContent(f, (c) => `if (${c}) { ${scope.ifTrue} }`)
);
}
function getSizeConditionFragment(discriminator, scope) {
const { dataName } = scope;
return fragment`${dataName}.length === ${discriminator.size}`;
}
function getByteConditionFragment(discriminator, scope) {
const { dataName, typeManifestVisitor } = scope;
const constant = visit(discriminator.constant, typeManifestVisitor).value;
return fragment`${use("containsBytes", "solanaCodecsCore")}(${dataName}, ${constant}, ${discriminator.offset})`;
}
function getFieldConditionFragment(discriminator, scope) {
const field = scope.struct.fields.find((f) => f.name === discriminator.name);
if (!field || !field.defaultValue) {
throw new Error(
`Field discriminator "${discriminator.name}" does not have a matching argument with default value.`
);
}
if (isNode(field.type, "arrayTypeNode") && isNode(field.type.item, "numberTypeNode") && field.type.item.format === "u8" && isNode(field.type.count, "fixedCountNode") && isNode(field.defaultValue, "arrayValueNode") && field.defaultValue.items.every(isNodeFilter("numberValueNode"))) {
const base64Bytes = getBase64Decoder().decode(
new Uint8Array(field.defaultValue.items.map((node) => node.number))
);
return getByteConditionFragment(
constantDiscriminatorNode(constantValueNodeFromBytes("base64", base64Bytes), discriminator.offset),
scope
);
}
return getByteConditionFragment(
constantDiscriminatorNode(constantValueNode(field.type, field.defaultValue), discriminator.offset),
scope
);
}
// src/fragments/errorPage.ts
function getErrorPageFragment(scope) {
return mergeFragments(
[
getConstantsFragment(scope),
getConstantUnionTypeFragment(scope),
getErrorMessagesFragment(scope),
getErrorMessageFunctionFragment(scope),
getIsErrorFunctionFragment(scope)
],
(cs) => cs.join("\n\n")
);
}
function getConstantsFragment(scope) {
const constantPrefix = scope.nameApi.programErrorConstantPrefix(scope.programNode.name);
return mergeFragments(
[...scope.programNode.errors].sort((a, b) => a.code - b.code).map((error) => {
const docs = getDocblockFragment(error.docs ?? [], true);
const name = constantPrefix + scope.nameApi.programErrorConstant(error.name);
return fragment`${docs}export const ${name} = 0x${error.code.toString(16)}; // ${error.code}`;
}),
(cs) => cs.join("\n")
);
}
function getConstantUnionTypeFragment(scope) {
const constantPrefix = scope.nameApi.programErrorConstantPrefix(scope.programNode.name);
const typeName = scope.nameApi.programErrorUnion(scope.programNode.name);
const errorTypes = mergeFragments(
[...scope.programNode.errors].sort((a, b) => a.name.localeCompare(b.name)).map((error) => fragment`typeof ${constantPrefix + scope.nameApi.programErrorConstant(error.name)}`),
(cs) => cs.join(" | ")
);
return fragment`export type ${typeName} = ${errorTypes};`;
}
function getErrorMessagesFragment(scope) {
const mapName = scope.nameApi.programErrorMessagesMap(scope.programNode.name);
const errorUnionType = scope.nameApi.programErrorUnion(scope.programNode.name);
const constantPrefix = scope.nameApi.programErrorConstantPrefix(scope.programNode.name);
const messageEntries = mergeFragments(
[...scope.programNode.errors].sort((a, b) => a.name.localeCompare(b.name)).map((error) => {
const constantName = constantPrefix + scope.nameApi.programErrorConstant(error.name);
const escapedMessage = error.message.replace(/`/g, "\\`");
return fragment`[${constantName}]: \`${escapedMessage}\``;
}),
(cs) => cs.join(", ")
);
return fragment`let ${mapName}: Record<${errorUnionType}, string> | undefined;
if (process.env.NODE_ENV !== 'production') {
${mapName} = { ${messageEntries} };
}`;
}
function getErrorMessageFunctionFragment(scope) {
const functionName = scope.nameApi.programGetErrorMessageFunction(scope.programNode.name);
const errorUnionType = scope.nameApi.programErrorUnion(scope.programNode.name);
const messageMapName = scope.nameApi.programErrorMessagesMap(scope.programNode.name);
return fragment`export function ${functionName}(code: ${errorUnionType}): string {
if (process.env.NODE_ENV !== 'production') {
return (${messageMapName} as Record<${errorUnionType}, string>)[code];
}
return 'Error message not available in production bundles.';
}`;
}
function getIsErrorFunctionFragment(scope) {
const { programNode, nameApi } = scope;
const programAddressConstant = use(nameApi.programAddressConstant(programNode.name), "generatedPrograms");
const functionName = nameApi.programIsErrorFunction(programNode.name);
const programErrorUnion = nameApi.programErrorUnion(programNode.name);
return fragment`export function ${functionName}<TProgramErrorCode extends ${programErrorUnion}>(
error: unknown,
transactionMessage: { instructions: Record<number, { programAddress: ${use("type Address", "solanaAddresses")} }> },
code?: TProgramErrorCode,
): error is ${use("type SolanaError", "solanaErrors")}<typeof ${use("type SOLANA_ERROR__INSTRUCTION_ERROR__CUSTOM", "solanaErrors")}> & Readonly<{ context: Readonly<{ code: TProgramErrorCode }> }> {
return ${use("isProgramError", "solanaPrograms")}<TProgramErrorCode>(error, transactionMessage, ${programAddressConstant}, code);
}`;
}
function getIndexPageFragment(items) {
if (items.length === 0) return;
const names = items.map((item) => camelCase(item.name)).sort((a, b) => a.localeCompare(b)).map((name) => getExportAllFragment(`./${name}`));
return mergeFragments(names, (cs) => cs.join("\n"));
}
function getInstructionAccountMetaFragment(instructionAccountNode) {
const typeParam = `TAccount${pascalCase(instructionAccountNode.name)}`;
if (instructionAccountNode.isSigner === true && instructionAccountNode.isWritable) {
return fragment`${use("type WritableSignerAccount", "solanaInstructions")}<${typeParam}> & ${use("type AccountSignerMeta", "solanaSigners")}<${typeParam}>`;
}
if (instructionAccountNode.isSigner === true) {
return fragment`${use("type ReadonlySignerAccount", "solanaInstructions")}<${typeParam}> & ${use("type AccountSignerMeta", "solanaSigners")}<${typeParam}>`;
}
if (instructionAccountNode.isWritable) {
return fragment`${use("type WritableAccount", "solanaInstructions")}<${typeParam}>`;
}
return fragment`${use("type ReadonlyAccount", "solanaInstructions")}<${typeParam}>`;
}
function getInstructionAccountTypeParamFragment(scope) {
const { instructionAccountPath, linkables } = scope;
const instructionAccountNode = getLastNodeFromPath(instructionAccountPath);
const instructionNode = findInstructionNodeFromPath(instructionAccountPath);
const programNode = findProgramNodeFromPath(instructionAccountPath);
const typeParam = `TAccount${pascalCase(instructionAccountNode.name)}`;
const accountMeta = fragment` | ${use("type AccountMeta", "solanaInstructions")}<string>` ;
if (instructionNode.optionalAccountStrategy === "omitted" && instructionAccountNode.isOptional) {
return fragment`${typeParam} extends string${accountMeta} | undefined = undefined`;
}
const defaultAddress = getDefaultAddress(instructionAccountNode.defaultValue, programNode.publicKey, linkables);
return fragment`${typeParam} extends string${accountMeta} = ${defaultAddress}`;
}
function getDefaultAddress(defaultValue, programId, linkables) {
switch (defaultValue?.kind) {
case "publicKeyValueNode":
return `"${defaultValue.publicKey}"`;
case "programLinkNode":
const programNode = linkables.get([defaultValue]);
return programNode ? `"${programNode.publicKey}"` : "string";
case "programIdValueNode":
return `"${programId}"`;
default:
return "string";
}
}
function getInstructionByteDeltaFragment(scope) {
const { byteDeltas } = getLastNodeFromPath(scope.instructionPath);
const fragments = (byteDeltas ?? []).flatMap((c) => getByteDeltaFragment(c, scope));
if (fragments.length === 0) return;
return mergeFragments(
fragments,
(c) => `// Bytes created or reallocated by the instruction.
const byteDelta: number = [${c.join(",")}].reduce((a, b) => a + b, 0);`
);
}
function getByteDeltaFragment(byteDelta, scope) {
let bytesFragment = (() => {
if (isNode(byteDelta.value, "numberValueNode")) {
return getNumberValueNodeFragment(byteDelta);
}
if (isNode(byteDelta.value, "argumentValueNode")) {
return getArgumentValueNodeFragment(byteDelta);
}
if (isNode