codama-renderers-dart 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{readme.md → README.md} +10 -43
- package/dist/index.browser.cjs +173 -21
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +173 -21
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +173 -21
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +173 -21
- package/dist/index.node.js.map +1 -1
- package/dist/index.react-native.js +173 -21
- package/dist/index.react-native.js.map +1 -1
- package/dist/types/fragments/instructionPage.d.ts.map +1 -1
- package/dist/types/utils/nameTransformers.d.ts +5 -0
- package/dist/types/utils/nameTransformers.d.ts.map +1 -1
- package/dist/types/visitors/getTypeManifestVisitor.d.ts +1 -1
- package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/fragments/instructionPage.ts +9 -2
- package/src/fragments/typePage.ts +102 -19
- package/src/utils/nameTransformers.ts +28 -1
- package/src/visitors/getTypeManifestVisitor.ts +24 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructionPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/instructionPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAOrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAUvD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,WAAW,GACjB,QAAQ,
|
|
1
|
+
{"version":3,"file":"instructionPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/instructionPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAOrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAUvD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,WAAW,GACjB,QAAQ,CA8UV"}
|
|
@@ -4,6 +4,11 @@ import type { CamelCaseString } from "@codama/nodes";
|
|
|
4
4
|
*/
|
|
5
5
|
/** Convert a string to PascalCase */
|
|
6
6
|
export declare function pascalCase(str: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Escape a lowercase identifier if it collides with a Dart reserved word
|
|
9
|
+
* by appending `_` (e.g. an account named `null` renders as `null_`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function escapeDartIdentifier(name: string): string;
|
|
7
12
|
/** Convert a string to camelCase */
|
|
8
13
|
export declare function camelCase(str: string): string;
|
|
9
14
|
/** Convert a string to snake_case */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nameTransformers.d.ts","sourceRoot":"","sources":["../../../src/utils/nameTransformers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AAEH,qCAAqC;AACrC,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,oCAAoC;AACpC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"nameTransformers.d.ts","sourceRoot":"","sources":["../../../src/utils/nameTransformers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AAEH,qCAAqC;AACrC,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAoBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,oCAAoC;AACpC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,sDAAsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,sDAAsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,kDAAkD;IAClD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,kDAAkD;IAClD,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,6DAA6D;IAC7D,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAChD,oDAAoD;IACpD,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,+CAA+C;IAC/C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,iDAAiD;IACjD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,iEAAiE;IACjE,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,iEAAiE;IACjE,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,4DAA4D;IAC5D,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,4DAA4D;IAC5D,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9D,4DAA4D;IAC5D,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,gCAAgC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,mEAAmE;IACnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IACpE,mCAAmC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,yDAAyD;IACzD,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,oDAAoD;IACpD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACtC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAwB/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAEhE"}
|
|
@@ -15,5 +15,5 @@ export declare function getTypeManifestVisitor(input: {
|
|
|
15
15
|
linkables: LinkableDictionary;
|
|
16
16
|
stack: NodeStack;
|
|
17
17
|
nonScalarEnums?: CamelCaseString[];
|
|
18
|
-
}): Visitor<TypeManifest, "accountBumpValueNode" | "accountValueNode" | "argumentValueNode" | "conditionalValueNode" | "identityValueNode" | "payerValueNode" | "pdaValueNode" | "programIdValueNode" | "resolverValueNode" | "programLinkNode" | "arrayValueNode" | "booleanValueNode" | "bytesValueNode" | "constantValueNode" | "enumValueNode" | "mapValueNode" | "noneValueNode" | "numberValueNode" | "publicKeyValueNode" | "setValueNode" | "someValueNode" | "stringValueNode" | "structValueNode" | "tupleValueNode" | "amountTypeNode" | "arrayTypeNode" | "booleanTypeNode" | "bytesTypeNode" | "dateTimeTypeNode" | "enumTypeNode" | "fixedSizeTypeNode" | "hiddenPrefixTypeNode" | "hiddenSuffixTypeNode" | "mapTypeNode" | "numberTypeNode" | "optionTypeNode" | "postOffsetTypeNode" | "preOffsetTypeNode" | "publicKeyTypeNode" | "remainderOptionTypeNode" | "sentinelTypeNode" | "setTypeNode" | "sizePrefixTypeNode" | "solAmountTypeNode" | "stringTypeNode" | "structTypeNode" | "tupleTypeNode" | "zeroableOptionTypeNode" | "constantDiscriminatorNode" | "fieldDiscriminatorNode" | "sizeDiscriminatorNode" | "accountNode" | "constantNode" | "definedTypeNode" | "errorNode" | "eventNode" | "instructionAccountNode" | "instructionArgumentNode" | "instructionByteDeltaNode" | "instructionNode" | "instructionRemainingAccountsNode" | "
|
|
18
|
+
}): Visitor<TypeManifest, "accountBumpValueNode" | "accountFieldValueNode" | "accountValueNode" | "argumentValueNode" | "conditionalValueNode" | "identityValueNode" | "payerValueNode" | "pdaValueNode" | "programIdValueNode" | "resolverValueNode" | "programLinkNode" | "arrayValueNode" | "booleanValueNode" | "bytesValueNode" | "constantValueNode" | "enumValueNode" | "injectedValueNode" | "mapValueNode" | "noneValueNode" | "numberValueNode" | "publicKeyValueNode" | "setValueNode" | "someValueNode" | "stringValueNode" | "structValueNode" | "tupleValueNode" | "amountTypeNode" | "arrayTypeNode" | "booleanTypeNode" | "bytesTypeNode" | "dateTimeTypeNode" | "enumTypeNode" | "fixedSizeTypeNode" | "hiddenPrefixTypeNode" | "hiddenSuffixTypeNode" | "mapTypeNode" | "numberTypeNode" | "optionTypeNode" | "postOffsetTypeNode" | "preOffsetTypeNode" | "publicKeyTypeNode" | "remainderOptionTypeNode" | "sentinelTypeNode" | "setTypeNode" | "sizePrefixTypeNode" | "solAmountTypeNode" | "stringTypeNode" | "structTypeNode" | "tupleTypeNode" | "zeroableOptionTypeNode" | "structFieldDisplayNode" | "constantDiscriminatorNode" | "fieldDiscriminatorNode" | "sizeDiscriminatorNode" | "instructionAccountDisplayNode" | "instructionStatusNode" | "accountNode" | "constantNode" | "definedTypeNode" | "errorNode" | "eventNode" | "instructionAccountNode" | "instructionArgumentNode" | "instructionByteDeltaNode" | "instructionNode" | "instructionRemainingAccountsNode" | "pdaNode" | "pluginNode" | "programNode" | "providedNode" | "pdaSeedValueNode" | "fixedCountNode" | "prefixedCountNode" | "remainderCountNode" | "amountNumberDisplayNode" | "dateTimeNumberDisplayNode" | "durationNumberDisplayNode" | "enumVariantDisplayNode" | "instructionDisplayNode" | "stringDisplayNode" | "accountLinkNode" | "definedTypeLinkNode" | "instructionAccountLinkNode" | "instructionArgumentLinkNode" | "instructionLinkNode" | "pdaLinkNode" | "constantPdaSeedNode" | "variablePdaSeedNode" | "enumEmptyVariantTypeNode" | "enumStructVariantTypeNode" | "enumTupleVariantTypeNode" | "structFieldTypeNode" | "mapEntryValueNode" | "structFieldValueNode" | "rootNode">;
|
|
19
19
|
//# sourceMappingURL=getTypeManifestVisitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTypeManifestVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getTypeManifestVisitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EA8BrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAEL,KAAK,YAAY,EAKlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,8BAA8B,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC,
|
|
1
|
+
{"version":3,"file":"getTypeManifestVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getTypeManifestVisitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EA8BrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAEL,KAAK,YAAY,EAKlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,8BAA8B,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC,8kEAooBA"}
|
package/package.json
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"repository": "https://github.com/openbudgetfun/solana_kit/tree/main/packages/codama-renderers-dart",
|
|
56
56
|
"type": "module",
|
|
57
57
|
"types": "./dist/types/index.d.ts",
|
|
58
|
-
"version": "0.5.
|
|
58
|
+
"version": "0.5.3",
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "pnpm exec rimraf dist && pnpm exec tsup && pnpm exec tsc -p tsconfig.declarations.json",
|
|
61
61
|
"lint": "eslint --ext .ts src",
|
|
@@ -178,6 +178,13 @@ export function getInstructionPageFragment(
|
|
|
178
178
|
const hasProgramAddressCollision = argNames.has('programAddress') || accountNames.has('programAddress');
|
|
179
179
|
const instrProgramParam = hasProgramAddressCollision ? 'instructionProgramAddress' : 'programAddress';
|
|
180
180
|
|
|
181
|
+
// The generated local holding the constructed instruction data must not
|
|
182
|
+
// collide with an account or argument parameter name.
|
|
183
|
+
let instructionDataLocal = 'instructionData';
|
|
184
|
+
while (argNames.has(instructionDataLocal) || accountNames.has(instructionDataLocal)) {
|
|
185
|
+
instructionDataLocal = `${instructionDataLocal}_`;
|
|
186
|
+
}
|
|
187
|
+
|
|
181
188
|
// Build the instruction builder function
|
|
182
189
|
const accountParams = accounts
|
|
183
190
|
.map((acc) => {
|
|
@@ -322,7 +329,7 @@ Instruction ${fragmentFromString(instrFnName)}({
|
|
|
322
329
|
${fragmentFromString(accountParams)}
|
|
323
330
|
${fragmentFromString(argParams)}
|
|
324
331
|
}) {
|
|
325
|
-
final
|
|
332
|
+
final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
|
|
326
333
|
${fragmentFromString(dataConstruction)}
|
|
327
334
|
);
|
|
328
335
|
|
|
@@ -331,7 +338,7 @@ ${fragmentFromString(dataConstruction)}
|
|
|
331
338
|
accounts: [
|
|
332
339
|
${fragmentFromString(accountMetas)}
|
|
333
340
|
],
|
|
334
|
-
data: ${fragmentFromString(dataEncoderName)}().encode(
|
|
341
|
+
data: ${fragmentFromString(dataEncoderName)}().encode(${instructionDataLocal}),
|
|
335
342
|
);
|
|
336
343
|
}`);
|
|
337
344
|
|
|
@@ -147,6 +147,7 @@ function getDataEnumPageFragment(
|
|
|
147
147
|
const decoderVariants: string[] = [];
|
|
148
148
|
const encodeCases: string[] = [];
|
|
149
149
|
const decodeCases: string[] = [];
|
|
150
|
+
let pageHasListEnums = false;
|
|
150
151
|
// Collect all field type manifests from variants so we can merge their imports
|
|
151
152
|
const allVariantManifests: { encoder: Fragment; decoder: Fragment; type: Fragment }[] = [];
|
|
152
153
|
|
|
@@ -212,12 +213,18 @@ function getDataEnumPageFragment(
|
|
|
212
213
|
fields.length === 0
|
|
213
214
|
? "true"
|
|
214
215
|
: fields
|
|
215
|
-
.map((f: StructFieldTypeNode) =>
|
|
216
|
+
.map((f: StructFieldTypeNode, index) => {
|
|
217
|
+
const fieldName = camelCase(f.name as string);
|
|
218
|
+
return isListManifest(fieldManifests[index].manifest)
|
|
219
|
+
? `_listEquals(${fieldName}, other.${fieldName})`
|
|
220
|
+
: `${fieldName} == other.${fieldName}`;
|
|
221
|
+
})
|
|
216
222
|
.join(" &&\n ");
|
|
217
223
|
|
|
218
|
-
const hashExpression =
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
const hashExpression = getValueHashExpression(fields, fieldManifests);
|
|
225
|
+
if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
|
|
226
|
+
pageHasListEnums = true;
|
|
227
|
+
}
|
|
221
228
|
|
|
222
229
|
const toStringFields = fields
|
|
223
230
|
.map((f: StructFieldTypeNode) => `${camelCase(f.name as string)}: $${camelCase(f.name as string)}`)
|
|
@@ -351,7 +358,7 @@ sealed class ${fragmentFromString(typeName)} {
|
|
|
351
358
|
const ${fragmentFromString(typeName)}();
|
|
352
359
|
}
|
|
353
360
|
|
|
354
|
-
${fragmentFromString(variantClasses.join("\n\n"))}
|
|
361
|
+
${fragmentFromString(variantClasses.join("\n\n"))}${pageHasListEnums ? LIST_VALUE_HELPERS : ""}
|
|
355
362
|
|
|
356
363
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
357
364
|
return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
|
|
@@ -405,6 +412,7 @@ function getStructPageFragment(
|
|
|
405
412
|
if (structNode.kind !== "structTypeNode") return emptyFragment();
|
|
406
413
|
|
|
407
414
|
const fields = structNode.fields ?? [];
|
|
415
|
+
let pageHasListStructs = false;
|
|
408
416
|
|
|
409
417
|
// Visit each field type once and collect manifests for reuse
|
|
410
418
|
const fieldManifests = fields.map((f: StructFieldTypeNode) => ({
|
|
@@ -429,19 +437,17 @@ function getStructPageFragment(
|
|
|
429
437
|
const eqChecks =
|
|
430
438
|
fields.length === 0
|
|
431
439
|
? "true"
|
|
432
|
-
:
|
|
433
|
-
.map(
|
|
434
|
-
(f
|
|
435
|
-
|
|
436
|
-
|
|
440
|
+
: fieldManifests
|
|
441
|
+
.map(({ field: f, manifest }, index) => {
|
|
442
|
+
const fieldName = camelCase(f.name as string);
|
|
443
|
+
return isListManifest(manifest)
|
|
444
|
+
? `_listEquals(${fieldName}, other.${fieldName})`
|
|
445
|
+
: `${fieldName} == other.${fieldName}`;
|
|
446
|
+
})
|
|
437
447
|
.join(" &&\n ");
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
? "runtimeType"
|
|
442
|
-
: fields
|
|
443
|
-
.map((f: StructFieldTypeNode) => camelCase(f.name as string))
|
|
444
|
-
.join(", ");
|
|
448
|
+
if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
|
|
449
|
+
pageHasListStructs = true;
|
|
450
|
+
}
|
|
445
451
|
|
|
446
452
|
const toStringFields = fields
|
|
447
453
|
.map(
|
|
@@ -511,12 +517,14 @@ ${fragmentFromString(fieldDecls)}
|
|
|
511
517
|
${fragmentFromString(eqChecks)};
|
|
512
518
|
|
|
513
519
|
@override
|
|
514
|
-
int get hashCode => ${fragmentFromString(
|
|
520
|
+
int get hashCode => ${fragmentFromString(
|
|
521
|
+
getValueHashExpression(fields, fieldManifests),
|
|
522
|
+
)};
|
|
515
523
|
|
|
516
524
|
@override
|
|
517
525
|
String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
|
|
518
526
|
}
|
|
519
|
-
|
|
527
|
+
${pageHasListStructs ? LIST_VALUE_HELPERS : ""}
|
|
520
528
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
521
529
|
final structEncoder = getStructEncoder(<(String, Encoder<Object?>)>[
|
|
522
530
|
${fragmentFromString(encFields)}
|
|
@@ -595,6 +603,81 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
|
|
|
595
603
|
}`;
|
|
596
604
|
}
|
|
597
605
|
|
|
606
|
+
/**
|
|
607
|
+
* Returns true when the rendered field type is a Dart list or byte sequence,
|
|
608
|
+
* whose reference equality must be replaced with a deep comparison in
|
|
609
|
+
* generated value classes.
|
|
610
|
+
*/
|
|
611
|
+
function isListManifest(manifest: { type: Fragment }): boolean {
|
|
612
|
+
return manifest.type.content.startsWith("List<") ||
|
|
613
|
+
manifest.type.content === "Uint8List";
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Private Dart helpers emitted into pages whose value classes contain
|
|
618
|
+
* list-typed fields, so `==` and `hashCode` match recursive structural value
|
|
619
|
+
* equality instead of list reference identity. The recursive branch matters
|
|
620
|
+
* for generated types such as `List<Uint8List>`.
|
|
621
|
+
*/
|
|
622
|
+
const LIST_VALUE_HELPERS = `
|
|
623
|
+
bool _listEquals<T>(List<T>? a, List<T>? b) {
|
|
624
|
+
if (identical(a, b)) return true;
|
|
625
|
+
if (a == null || b == null) return a == b;
|
|
626
|
+
if (a.length != b.length) return false;
|
|
627
|
+
for (var i = 0; i < a.length; i++) {
|
|
628
|
+
final left = a[i];
|
|
629
|
+
final right = b[i];
|
|
630
|
+
if (left is List<Object?> && right is List<Object?>) {
|
|
631
|
+
if (!_listEquals(left, right)) return false;
|
|
632
|
+
} else if (left != right) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
Object? _deepHash(Object? value) {
|
|
640
|
+
if (value is List<Object?>) {
|
|
641
|
+
return Object.hashAll(value.map(_deepHash));
|
|
642
|
+
}
|
|
643
|
+
return value;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
int _listHashCode<T>(List<T>? a) {
|
|
647
|
+
if (a == null) return 0;
|
|
648
|
+
return Object.hashAll(a.map(_deepHash));
|
|
649
|
+
}
|
|
650
|
+
`;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Builds the `hashCode` expression for a class whose fields render as the
|
|
654
|
+
* given lowercase names. List-typed fields compare structurally through the
|
|
655
|
+
* private `_listHashCode` helper emitted alongside the class.
|
|
656
|
+
*/
|
|
657
|
+
function getValueHashExpression(
|
|
658
|
+
fields: StructFieldTypeNode[],
|
|
659
|
+
fieldManifests: { field: StructFieldTypeNode; manifest: { type: Fragment } }[],
|
|
660
|
+
): string {
|
|
661
|
+
const fieldNames = fields.map((f: StructFieldTypeNode) =>
|
|
662
|
+
camelCase(f.name as string),
|
|
663
|
+
);
|
|
664
|
+
const hasListFields = fieldManifests.some(({ manifest }) =>
|
|
665
|
+
isListManifest(manifest),
|
|
666
|
+
);
|
|
667
|
+
if (!hasListFields) {
|
|
668
|
+
return getHashExpression(fieldNames);
|
|
669
|
+
}
|
|
670
|
+
if (fieldNames.length === 1) {
|
|
671
|
+
return `_listHashCode(${fieldNames[0]})`;
|
|
672
|
+
}
|
|
673
|
+
const hashArgs = fields.map((f: StructFieldTypeNode, index) =>
|
|
674
|
+
isListManifest(fieldManifests[index].manifest)
|
|
675
|
+
? `_listHashCode(${camelCase(f.name as string)})`
|
|
676
|
+
: camelCase(f.name as string),
|
|
677
|
+
);
|
|
678
|
+
return `Object.hash(${hashArgs.join(", ")})`;
|
|
679
|
+
}
|
|
680
|
+
|
|
598
681
|
function getHashExpression(fields: string[]): string {
|
|
599
682
|
if (fields.length === 0) return "runtimeType.hashCode";
|
|
600
683
|
if (fields.length === 1) return `${fields[0]}.hashCode`;
|
|
@@ -11,10 +11,37 @@ export function pascalCase(str: string): string {
|
|
|
11
11
|
.replace(/^(.)/, (c) => c.toUpperCase());
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Dart reserved words (plus `async`/`await`/`yield` where they cannot be
|
|
16
|
+
* plain identifiers). A lowercase declaration named with one of these must
|
|
17
|
+
* be escaped, e.g. an account named `null` becomes a `null_` parameter.
|
|
18
|
+
*/
|
|
19
|
+
const DART_RESERVED_WORDS = new Set([
|
|
20
|
+
"abstract", "as", "assert", "async", "augment", "await", "base",
|
|
21
|
+
"break", "case", "catch", "class", "const", "continue", "covariant",
|
|
22
|
+
"default", "deferred", "do", "dynamic", "else", "enum", "export",
|
|
23
|
+
"extends", "extension", "external", "factory", "false", "final",
|
|
24
|
+
"finally", "for", "get", "hide", "if", "implements", "import", "in",
|
|
25
|
+
"interface", "is", "late", "library", "mixin", "native", "new", "null",
|
|
26
|
+
"of", "on", "operator", "part", "required", "rethrow", "return",
|
|
27
|
+
"sealed", "set", "show", "static", "super", "switch", "sync", "this",
|
|
28
|
+
"throw", "true", "try", "typedef", "var", "void", "when", "while",
|
|
29
|
+
"with", "yield",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Escape a lowercase identifier if it collides with a Dart reserved word
|
|
34
|
+
* by appending `_` (e.g. an account named `null` renders as `null_`).
|
|
35
|
+
*/
|
|
36
|
+
export function escapeDartIdentifier(name: string): string {
|
|
37
|
+
return DART_RESERVED_WORDS.has(name) ? `${name}_` : name;
|
|
38
|
+
}
|
|
39
|
+
|
|
14
40
|
/** Convert a string to camelCase */
|
|
15
41
|
export function camelCase(str: string): string {
|
|
16
42
|
const pascal = pascalCase(str);
|
|
17
|
-
|
|
43
|
+
const cased = pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
44
|
+
return escapeDartIdentifier(cased);
|
|
18
45
|
}
|
|
19
46
|
|
|
20
47
|
/** Convert a string to snake_case */
|
|
@@ -239,7 +239,7 @@ export function getTypeManifestVisitor(input: {
|
|
|
239
239
|
encoder: fragment`${use(
|
|
240
240
|
"getArrayEncoder",
|
|
241
241
|
"solanaCodecsDataStructures",
|
|
242
|
-
)}
|
|
242
|
+
)}(${itemEncoder}${encoderSizeExpr})`,
|
|
243
243
|
decoder: fragment`${use(
|
|
244
244
|
"getArrayDecoder",
|
|
245
245
|
"solanaCodecsDataStructures",
|
|
@@ -305,19 +305,33 @@ export function getTypeManifestVisitor(input: {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
const encoderListStr = encoderList.map((e) => e.content).join(", ");
|
|
308
|
+
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
309
|
+
// Two-element tuples render through the typed getTuple2 helpers so the
|
|
310
|
+
// generated value type is the Dart record `(T1, T2)` rather than a
|
|
311
|
+
// heterogeneous List<Object?>. Arity-2 is the only shape that currently
|
|
312
|
+
// appears in supported IDLs; wider tuples stay on the untyped helpers.
|
|
313
|
+
let tupleEncoderFn = "getTupleEncoder";
|
|
314
|
+
let tupleDecoderFn = "getTupleDecoder";
|
|
315
|
+
let tupleEncoderArgs = `[${encoderListStr}]`;
|
|
316
|
+
let tupleDecoderArgs = `[${decoderListStr}]`;
|
|
317
|
+
if (itemManifests.length === 2) {
|
|
318
|
+
tupleEncoderFn = "getTuple2Encoder";
|
|
319
|
+
tupleDecoderFn = "getTuple2Decoder";
|
|
320
|
+
tupleEncoderArgs = encoderListStr;
|
|
321
|
+
tupleDecoderArgs = decoderListStr;
|
|
322
|
+
}
|
|
308
323
|
const encoderFrag = fragment`${use(
|
|
309
|
-
|
|
324
|
+
tupleEncoderFn,
|
|
310
325
|
"solanaCodecsDataStructures",
|
|
311
|
-
)}(
|
|
326
|
+
)}(${tupleEncoderArgs})`;
|
|
312
327
|
for (const e of encoderList) {
|
|
313
328
|
encoderFrag.imports.mergeWith(e.imports);
|
|
314
329
|
}
|
|
315
330
|
|
|
316
|
-
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
317
331
|
const decoderFrag = fragment`${use(
|
|
318
|
-
|
|
332
|
+
tupleDecoderFn,
|
|
319
333
|
"solanaCodecsDataStructures",
|
|
320
|
-
)}(
|
|
334
|
+
)}(${tupleDecoderArgs})`;
|
|
321
335
|
for (const d of decoderList) {
|
|
322
336
|
decoderFrag.imports.mergeWith(d.imports);
|
|
323
337
|
}
|
|
@@ -442,7 +456,10 @@ export function getTypeManifestVisitor(input: {
|
|
|
442
456
|
encoder: fragment`${use(
|
|
443
457
|
"getNullableEncoder",
|
|
444
458
|
"solanaCodecsDataStructures",
|
|
445
|
-
)}<${fragmentFromString(itemTypeStr)}>(${
|
|
459
|
+
)}<${fragmentFromString(itemTypeStr)}>(${use(
|
|
460
|
+
"transformEncoder",
|
|
461
|
+
"solanaCodecsCore",
|
|
462
|
+
)}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${fragmentFromString(prefixExpr)}${fragmentFromString(noneValueExpr)})`,
|
|
446
463
|
decoder: fragment`${use(
|
|
447
464
|
"getNullableDecoder",
|
|
448
465
|
"solanaCodecsDataStructures",
|