Skip to content

Commit

Permalink
Implement ConstantValueNode type manifests (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva authored Apr 10, 2024
1 parent 3726e1f commit 75f5f91
Show file tree
Hide file tree
Showing 17 changed files with 563 additions and 433 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-queens-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@metaplex-foundation/kinobi": minor
---

Implement ConstantValueNode type manifests
48 changes: 29 additions & 19 deletions src/renderers/js-experimental/TypeManifest.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,45 @@
import { Fragment, mergeFragments } from './fragments';
import { Fragment, fragment, mergeFragments } from './fragments';

export type TypeManifest = {
isEnum: boolean;
strictType: Fragment;
looseType: Fragment;
encoder: Fragment;
decoder: Fragment;
value: Fragment;
};

export function typeManifest(): TypeManifest {
return {
isEnum: false,
strictType: fragment(''),
looseType: fragment(''),
encoder: fragment(''),
decoder: fragment(''),
value: fragment(''),
};
}

export function mergeManifests(
manifests: TypeManifest[],
mergeTypes: (renders: string[]) => string,
mergeCodecs: (renders: string[]) => string
options: {
mergeTypes?: (renders: string[]) => string;
mergeCodecs?: (renders: string[]) => string;
mergeValues?: (renders: string[]) => string;
} = {}
): TypeManifest {
const { mergeTypes, mergeCodecs, mergeValues } = options;
const merge = (
fragmentFn: (m: TypeManifest) => Fragment,
mergeFn?: (r: string[]) => string
) =>
mergeFn ? mergeFragments(manifests.map(fragmentFn), mergeFn) : fragment('');
return {
isEnum: false,
strictType: mergeFragments(
manifests.map((m) => m.strictType),
mergeTypes
),
looseType: mergeFragments(
manifests.map((m) => m.looseType),
mergeTypes
),
encoder: mergeFragments(
manifests.map((m) => m.encoder),
mergeCodecs
),
decoder: mergeFragments(
manifests.map((m) => m.decoder),
mergeCodecs
),
strictType: merge((m) => m.strictType, mergeTypes),
looseType: merge((m) => m.looseType, mergeTypes),
encoder: merge((m) => m.encoder, mergeCodecs),
decoder: merge((m) => m.decoder, mergeCodecs),
value: merge((m) => m.value, mergeValues),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ import { Fragment, fragment, mergeFragments } from './common';
* ```
*/
export function getDiscriminatorConditionFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor'
> & {
scope: Pick<GlobalFragmentScope, 'nameApi' | 'typeManifestVisitor'> & {
programNode: ProgramNode;
discriminators: DiscriminatorNode[];
struct: StructTypeNode;
Expand Down Expand Up @@ -77,10 +74,10 @@ function getByteConditionFragment(

function getFieldConditionFragment(
discriminator: FieldDiscriminatorNode,
scope: Pick<
GlobalFragmentScope,
'typeManifestVisitor' | 'valueNodeVisitor'
> & { dataName: string; struct: StructTypeNode }
scope: Pick<GlobalFragmentScope, 'typeManifestVisitor'> & {
dataName: string;
struct: StructTypeNode;
}
): Fragment {
const field = scope.struct.fields.find((f) => f.name === discriminator.name);
if (!field || !field.defaultValue) {
Expand Down Expand Up @@ -111,7 +108,7 @@ function getFieldConditionFragment(
return mergeFragments(
[
visit(field.type, scope.typeManifestVisitor).encoder,
visit(field.defaultValue, scope.valueNodeVisitor),
visit(field.defaultValue, scope.typeManifestVisitor).value,
],
([encoderFunction, value]) => `${encoderFunction}.encode(${value})`
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import { getInstructionRemainingAccountsFragment } from './instructionRemainingA
export function getInstructionFunctionFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'asyncResolvers' | 'valueNodeVisitor' | 'customInstructionData'
| 'nameApi'
| 'asyncResolvers'
| 'typeManifestVisitor'
| 'customInstructionData'
> & {
instructionNode: InstructionNode;
programNode: ProgramNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Fragment, fragment, mergeFragments } from './common';
export function getInstructionInputDefaultFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'asyncResolvers' | 'valueNodeVisitor'
'nameApi' | 'asyncResolvers' | 'typeManifestVisitor'
> & {
input: ResolvedInstructionInput;
optionalAccountStrategy: 'programId' | 'omitted';
Expand All @@ -21,7 +21,7 @@ export function getInstructionInputDefaultFragment(
asyncResolvers,
useAsync,
nameApi,
valueNodeVisitor,
typeManifestVisitor,
} = scope;
if (!input.defaultValue) {
return fragment('');
Expand Down Expand Up @@ -96,7 +96,7 @@ export function getInstructionInputDefaultFragment(
`${seed.name}: expectSome(args.${camelCase(seed.value.name)})`
).addImports('shared', 'expectSome');
}
return visit(seed.value, valueNodeVisitor).mapRender(
return visit(seed.value, typeManifestVisitor).value.mapRender(
(r) => `${seed.name}: ${r}`
);
});
Expand Down Expand Up @@ -209,7 +209,10 @@ export function getInstructionInputDefaultFragment(
? `accounts.${camelCase(defaultValue.condition.name)}.value`
: `args.${camelCase(defaultValue.condition.name)}`;
if (defaultValue.value) {
const comparedValue = visit(defaultValue.value, valueNodeVisitor);
const comparedValue = visit(
defaultValue.value,
typeManifestVisitor
).value;
conditionalFragment
.mergeImportsWith(comparedValue)
.mergeFeaturesWith(comparedValue);
Expand All @@ -235,7 +238,7 @@ export function getInstructionInputDefaultFragment(
);

default:
const valueManifest = visit(defaultValue, valueNodeVisitor);
const valueManifest = visit(defaultValue, typeManifestVisitor).value;
return defaultFragment(valueManifest.render).mergeImportsWith(
valueManifest
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getInstructionInputDefaultFragment } from './instructionInputDefault';
export function getInstructionInputResolvedFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'asyncResolvers' | 'valueNodeVisitor'
'nameApi' | 'asyncResolvers' | 'typeManifestVisitor'
> & {
instructionNode: InstructionNode;
resolvedInputs: ResolvedInstructionInput[];
Expand Down
15 changes: 3 additions & 12 deletions src/renderers/js-experimental/fragments/pdaFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,12 @@ import type { GlobalFragmentScope } from '../getRenderMapVisitor';
import { Fragment, fragmentFromTemplate } from './common';

export function getPdaFunctionFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor'
> & {
scope: Pick<GlobalFragmentScope, 'nameApi' | 'typeManifestVisitor'> & {
pdaNode: PdaNode;
programNode: ProgramNode;
}
): Fragment {
const {
pdaNode,
programNode,
typeManifestVisitor,
valueNodeVisitor,
nameApi,
} = scope;
const { pdaNode, programNode, typeManifestVisitor, nameApi } = scope;

// Seeds.
const imports = new ImportMap();
Expand All @@ -28,7 +19,7 @@ export function getPdaFunctionFragment(
const seedManifest = visit(seed.type, typeManifestVisitor);
imports.mergeWith(seedManifest.encoder);
const seedValue = seed.value;
const valueManifest = visit(seedValue, valueNodeVisitor);
const valueManifest = visit(seedValue, typeManifestVisitor).value;
(seedValue as any).render = valueManifest.render;
imports.mergeWith(valueManifest.imports);
return { ...seed, typeManifest: seedManifest };
Expand Down
10 changes: 2 additions & 8 deletions src/renderers/js-experimental/fragments/programAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import { Fragment, fragment, mergeFragments } from './common';
import { getDiscriminatorConditionFragment } from './discriminatorCondition';

export function getProgramAccountsFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor'
> & {
scope: Pick<GlobalFragmentScope, 'nameApi' | 'typeManifestVisitor'> & {
programNode: ProgramNode;
}
): Fragment {
Expand Down Expand Up @@ -39,10 +36,7 @@ function getProgramAccountsEnumFragment(
}

function getProgramAccountsIdentifierFunctionFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor'
> & {
scope: Pick<GlobalFragmentScope, 'nameApi' | 'typeManifestVisitor'> & {
programNode: ProgramNode;
}
): Fragment {
Expand Down
10 changes: 2 additions & 8 deletions src/renderers/js-experimental/fragments/programInstructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import { getDiscriminatorConditionFragment } from './discriminatorCondition';
export function getProgramInstructionsFragment(
scope: Pick<
GlobalFragmentScope,
| 'nameApi'
| 'typeManifestVisitor'
| 'valueNodeVisitor'
| 'renderParentInstructions'
'nameApi' | 'typeManifestVisitor' | 'renderParentInstructions'
> & {
programNode: ProgramNode;
}
Expand Down Expand Up @@ -56,10 +53,7 @@ function getProgramInstructionsEnumFragment(
}

function getProgramInstructionsIdentifierFunctionFragment(
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'typeManifestVisitor' | 'valueNodeVisitor'
> & {
scope: Pick<GlobalFragmentScope, 'nameApi' | 'typeManifestVisitor'> & {
programNode: ProgramNode;
allInstructions: InstructionNode[];
}
Expand Down
14 changes: 2 additions & 12 deletions src/renderers/js-experimental/getRenderMapVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ import {
NameApi,
NameTransformers,
} from './nameTransformers';
import {
renderValueNodeVisitor,
ValueNodeVisitor,
} from './renderValueNodeVisitor';

const DEFAULT_PRETTIER_OPTIONS: PrettierOptions = {
semi: true,
Expand Down Expand Up @@ -96,7 +92,6 @@ export type GlobalFragmentScope = {
nameApi: NameApi;
linkables: LinkableDictionary;
typeManifestVisitor: TypeManifestVisitor;
valueNodeVisitor: ValueNodeVisitor;
asyncResolvers: MainCaseString[];
nonScalarEnums: MainCaseString[];
renderParentInstructions: boolean;
Expand Down Expand Up @@ -132,18 +127,14 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
'InstructionData'
);

const valueNodeVisitor = renderValueNodeVisitor({
nameApi,
linkables,
nonScalarEnums,
});
const getTypeManifestVisitor = (parentName?: {
strict: string;
loose: string;
}) =>
baseGetTypeManifestVisitor({
nameApi,
valueNodeVisitor,
linkables,
nonScalarEnums,
customAccountData,
customInstructionData,
parentName,
Expand All @@ -155,7 +146,6 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
nameApi,
linkables,
typeManifestVisitor,
valueNodeVisitor,
asyncResolvers,
nonScalarEnums,
renderParentInstructions,
Expand Down
Loading

0 comments on commit 75f5f91

Please sign in to comment.