fix: handling nested optional action param types

This commit is contained in:
dafuga
2025-04-11 23:32:07 +09:00
parent 83d5c57461
commit c88c2ceeea
7 changed files with 74 additions and 50 deletions
+9 -9
View File
@@ -24,15 +24,15 @@ test_generate: node_modules clean lib
.PHONY: update_mock_contracts
update_mock_contracts: node_modules clean lib
# node lib/cli.js generate atomicassets -u https://jungle4.greymass.com -j test/data/abis/atomicassets.json -f test/data/contracts/mock-atomicassets.ts -e .eslintrc
# node lib/cli.js generate boid -u https://jungle4.greymass.com -j test/data/abis/boid.json -f test/data/contracts/mock-boid.ts -e .eslintrc
# node lib/cli.js generate payroll.boid -u https://telos.api.animus.is -j test/data/abis/payroll.boid.json -f test/data/contracts/mock-payroll.boid.ts -e .eslintrc
# node lib/cli.js generate eosio -u https://jungle4.greymass.com -j test/data/abis/eosio.json -f test/data/contracts/mock-eosio.ts -e .eslintrc
# node lib/cli.js generate eosio.msig -u https://jungle4.greymass.com -j test/data/abis/eosio.msig.json -f test/data/contracts/mock-eosio.msig.ts -e .eslintrc
# node lib/cli.js generate eosio.token -u https://jungle4.greymass.com -j test/data/abis/eosio.token.json -f test/data/contracts/mock-eosio.token.ts -e .eslintrc
# node lib/cli.js generate hegemon.hgm -u https://jungle4.greymass.com -j test/data/abis/hegemon.hgm.json -f test/data/contracts/mock-hegemon.hgm.ts -e .eslintrc
# node lib/cli.js generate rewards.gm -u https://jungle4.greymass.com -j test/data/abis/rewards.gm.json -f test/data/contracts/mock-rewards.gm.ts -e .eslintrc
# node lib/cli.js generate testing.gm -u https://jungle4.greymass.com -j test/data/abis/testing.gm.json -f test/data/contracts/mock-testing.gm.ts -e .eslintrc
node lib/cli.js generate atomicassets -u https://jungle4.greymass.com -j test/data/abis/atomicassets.json -f test/data/contracts/mock-atomicassets.ts -e .eslintrc
node lib/cli.js generate boid -u https://jungle4.greymass.com -j test/data/abis/boid.json -f test/data/contracts/mock-boid.ts -e .eslintrc
node lib/cli.js generate payroll.boid -u https://telos.api.animus.is -j test/data/abis/payroll.boid.json -f test/data/contracts/mock-payroll.boid.ts -e .eslintrc
node lib/cli.js generate eosio -u https://jungle4.greymass.com -j test/data/abis/eosio.json -f test/data/contracts/mock-eosio.ts -e .eslintrc
node lib/cli.js generate eosio.msig -u https://jungle4.greymass.com -j test/data/abis/eosio.msig.json -f test/data/contracts/mock-eosio.msig.ts -e .eslintrc
node lib/cli.js generate eosio.token -u https://jungle4.greymass.com -j test/data/abis/eosio.token.json -f test/data/contracts/mock-eosio.token.ts -e .eslintrc
node lib/cli.js generate hegemon.hgm -u https://jungle4.greymass.com -j test/data/abis/hegemon.hgm.json -f test/data/contracts/mock-hegemon.hgm.ts -e .eslintrc
node lib/cli.js generate rewards.gm -u https://jungle4.greymass.com -j test/data/abis/rewards.gm.json -f test/data/contracts/mock-rewards.gm.ts -e .eslintrc
node lib/cli.js generate testing.gm -u https://jungle4.greymass.com -j test/data/abis/testing.gm.json -f test/data/contracts/mock-testing.gm.ts -e .eslintrc
node lib/cli.js generate unicove2.gm -u https://jungle4.greymass.com -j test/data/abis/unicove2.gm.json -f test/data/contracts/mock-unicove2.gm.ts -e .eslintrc
.PHONY: update_mock_abis
+10 -8
View File
@@ -265,16 +265,18 @@ export function capitalize(string) {
export function removeDuplicateInterfaces(
interfaces: TypeInterfaceDeclaration[]
): TypeInterfaceDeclaration[] {
// Reverted to original logic: Keep the first encountered interface/alias by name
const seen: string[] = []
return interfaces.filter((interfaceDeclaration) => {
const name = String(interfaceDeclaration.name.escapedText)
if (seen.includes(name)) {
return false
return interfaces.filter((declaration) => {
if (!declaration.name || !ts.isIdentifier(declaration.name)) {
return true // Keep declarations without a simple name (or handle differently if needed)
}
seen.push(name)
return true
const name = String(declaration.name.escapedText)
if (seen.includes(name)) {
return false // Discard if name already seen
}
seen.push(name) // Mark name as seen
return true // Keep if name is new
})
}
+36 -19
View File
@@ -1,7 +1,7 @@
import type {ABI} from '@wharfkit/antelope'
import ts from 'typescript'
import {parseType, removeCommas, removeDuplicateInterfaces} from './helpers'
import {findFieldTypeString, getActionFieldFromAbi} from './structs'
import {findFieldTypeString, getActionFieldFromAbi, type StructData} from './structs'
import {findAbiStruct, findExternalType, findTypeFromAlias, findVariant} from './finders'
export function generateActionNamesInterface(abi: ABI.Def): ts.InterfaceDeclaration {
@@ -30,11 +30,14 @@ export function generateActionNamesInterface(abi: ABI.Def): ts.InterfaceDeclarat
export type TypeInterfaceDeclaration = ts.InterfaceDeclaration | ts.TypeAliasDeclaration
export function generateActionInterface(
struct,
abi
struct: any,
abi: ABI.Def,
allStructData: StructData[]
): {actionInterface: ts.InterfaceDeclaration; typeInterfaces: TypeInterfaceDeclaration[]} {
const typeInterfaces: TypeInterfaceDeclaration[] = []
const structName = typeof struct.name === 'string' ? struct.name : ''
const members = struct.fields.map((field) => {
const abiVariant = findVariant(field.type, abi)
@@ -60,7 +63,6 @@ export function generateActionInterface(
undefined,
ts.factory.createUnionTypeNode(allTypeNodes)
)
typeInterfaces.push(variantTypeAlias)
} else {
types = [field.type]
@@ -72,8 +74,7 @@ export function generateActionInterface(
const typeStruct = findAbiStruct(type, abi)
if (typeStruct) {
const interfaces = generateActionInterface(typeStruct, abi)
const interfaces = generateActionInterface(typeStruct, abi, allStructData)
typeInterfaces.push(interfaces.actionInterface, ...interfaces.typeInterfaces)
}
})
@@ -82,17 +83,27 @@ export function generateActionInterface(
variantName || findParamTypeString(aliasType || field.type, 'Type.', abi)
)
let isOptional = false
const fieldName = field.name
const processedStruct = allStructData.find((s) => s.name === structName)
if (processedStruct) {
const processedField = processedStruct.fields.find((f) => f.name === fieldName)
if (processedField) {
isOptional = processedField.optional
}
}
return ts.factory.createPropertySignature(
undefined,
field.name,
field.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
fieldName,
isOptional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
typeReferenceNode
)
})
const actionInterface = ts.factory.createInterfaceDeclaration(
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
removeCommas(struct.name),
structName ? removeCommas(structName) : ts.createIdentifier('UnknownStruct'),
undefined,
undefined,
members
@@ -106,19 +117,25 @@ export function generateActionsNamespace(abi: ABI.Def): ts.Statement {
const typeInterfaces: TypeInterfaceDeclaration[] = []
const actionParamInterfaces = abi.actions.map((action) => {
const actionStruct = actionStructsWithFields.find(
(actionStructWithField) => actionStructWithField.name === action.type
)
const actionParamInterfaces = abi.actions
.map((action) => {
const actionStruct = actionStructsWithFields.find(
(actionStructWithField) => actionStructWithField.name === action.type
)
const interfaces = generateActionInterface(actionStruct, abi)
if (!actionStruct) {
return undefined
}
if (interfaces.actionInterface) {
typeInterfaces.push(...interfaces.typeInterfaces)
}
const interfaces = generateActionInterface(actionStruct, abi, actionStructsWithFields)
return interfaces.actionInterface
})
if (interfaces.actionInterface) {
typeInterfaces.push(...interfaces.typeInterfaces)
}
return interfaces.actionInterface
})
.filter((iface): iface is ts.InterfaceDeclaration => !!iface)
const actionParamsTypes = ts.factory.createModuleDeclaration(
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
+1 -1
View File
@@ -11,7 +11,7 @@ interface FieldType {
extension: boolean
}
interface StructData {
export interface StructData {
name: string
base?: string
fields: FieldType[]
+12 -12
View File
@@ -1323,18 +1323,18 @@ export namespace ActionParams {
export interface powerup_config {
net: Type.powerup_config_resource
cpu: Type.powerup_config_resource
powerup_days: UInt32Type
min_powerup_fee: AssetType
powerup_days?: UInt32Type
min_powerup_fee?: AssetType
}
export interface powerup_config_resource {
current_weight_ratio: Int64Type
target_weight_ratio: Int64Type
assumed_stake_weight: Int64Type
target_timestamp: TimePointSec
exponent: Float64Type
decay_secs: UInt32Type
min_price: AssetType
max_price: AssetType
current_weight_ratio?: Int64Type
target_weight_ratio?: Int64Type
assumed_stake_weight?: Int64Type
target_timestamp?: TimePointSec
exponent?: Float64Type
decay_secs?: UInt32Type
min_price?: AssetType
max_price?: AssetType
}
export interface authority {
threshold: UInt32Type
@@ -1362,7 +1362,7 @@ export namespace ActionParams {
transaction_mroot: Checksum256Type
action_mroot: Checksum256Type
schedule_version: UInt32Type
new_producers: Type.producer_schedule
new_producers?: Type.producer_schedule
}
export interface producer_schedule {
version: UInt32Type
@@ -1380,7 +1380,7 @@ export namespace ActionParams {
keys: Type.key_weight[]
}
export interface blockchain_parameters_v1 {
max_action_return_value_size: UInt32Type
max_action_return_value_size?: UInt32Type
}
}
export interface activate {
+1 -1
View File
@@ -491,7 +491,7 @@ export type TableNames = keyof TableTypes
export namespace ActionParams {
export namespace Type {
export interface token_definition {
chain: Checksum256Type
chain?: Checksum256Type
contract: NameType
symbol: Asset.SymbolType
}
+5
View File
@@ -77,6 +77,11 @@ export function getMockParams(contract: Contract): ActionDataType {
commit: 'bar',
}
}
case 'unicove2.gm': {
return {
account: 'foo',
}
}
default: {
throw new Error(`getMockParams not implemented for ${contract.account}`)
}