codama-renderers-dart 0.5.1 → 0.5.4

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.
Files changed (37) hide show
  1. package/{readme.md → README.md} +16 -43
  2. package/dist/index.browser.cjs +345 -124
  3. package/dist/index.browser.cjs.map +1 -1
  4. package/dist/index.browser.js +346 -125
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.node.cjs +345 -124
  7. package/dist/index.node.cjs.map +1 -1
  8. package/dist/index.node.js +346 -125
  9. package/dist/index.node.js.map +1 -1
  10. package/dist/index.react-native.js +346 -125
  11. package/dist/index.react-native.js.map +1 -1
  12. package/dist/types/fragments/accountPage.d.ts.map +1 -1
  13. package/dist/types/fragments/errorPage.d.ts.map +1 -1
  14. package/dist/types/fragments/instructionPage.d.ts.map +1 -1
  15. package/dist/types/fragments/pdaPage.d.ts.map +1 -1
  16. package/dist/types/fragments/programPage.d.ts.map +1 -1
  17. package/dist/types/utils/nameTransformers.d.ts +5 -0
  18. package/dist/types/utils/nameTransformers.d.ts.map +1 -1
  19. package/dist/types/utils/valueNodes.d.ts +4 -0
  20. package/dist/types/utils/valueNodes.d.ts.map +1 -1
  21. package/dist/types/visitors/getRenderMapVisitor.d.ts.map +1 -1
  22. package/dist/types/visitors/getTypeManifestVisitor.d.ts +1 -1
  23. package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
  24. package/dist/types/visitors/renderVisitor.d.ts.map +1 -1
  25. package/package.json +7 -7
  26. package/src/fragments/accountPage.ts +2 -1
  27. package/src/fragments/errorPage.ts +8 -5
  28. package/src/fragments/instructionPage.ts +54 -17
  29. package/src/fragments/pdaPage.ts +7 -14
  30. package/src/fragments/programPage.ts +3 -2
  31. package/src/fragments/typePage.ts +102 -19
  32. package/src/utils/formatCode.ts +2 -2
  33. package/src/utils/nameTransformers.ts +39 -1
  34. package/src/utils/valueNodes.ts +24 -1
  35. package/src/visitors/getRenderMapVisitor.ts +30 -5
  36. package/src/visitors/getTypeManifestVisitor.ts +123 -81
  37. package/src/visitors/renderVisitor.ts +11 -13
@@ -1 +1 @@
1
- {"version":3,"file":"accountPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/accountPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,eAAe,CAAC;AAItE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAUvD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,GACjB,QAAQ,CA8OV"}
1
+ {"version":3,"file":"accountPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/accountPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,eAAe,CAAC;AAItE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAWvD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,GACjB,QAAQ,CA8OV"}
@@ -1 +1 @@
1
- {"version":3,"file":"errorPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/errorPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,WAAW,GACjB,QAAQ,CA0DV"}
1
+ {"version":3,"file":"errorPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/errorPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AASrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,WAAW,GACjB,QAAQ,CA2DV"}
@@ -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,CAuUV"}
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,CA+VV"}
@@ -1 +1 @@
1
- {"version":3,"file":"pdaPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/pdaPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAOrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,WAAW,GACjB,QAAQ,CAmJV"}
1
+ {"version":3,"file":"pdaPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/pdaPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAOrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMvD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,WAAW,GACjB,QAAQ,CAoJV"}
@@ -1 +1 @@
1
- {"version":3,"file":"programPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/programPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,GACjB,QAAQ,CAiJV"}
1
+ {"version":3,"file":"programPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/programPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,GACjB,QAAQ,CAiJV"}
@@ -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,CAG7C;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"}
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,CAM9C;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,CAS7C;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"}
@@ -12,5 +12,9 @@ type RenderableValueNode = InstructionInputValueNode | ValueNode;
12
12
  export declare function getDartValueFragment(value: RenderableValueNode, dartType?: string): Fragment;
13
13
  /** Return whether a rendered standalone value is valid in a const initializer. */
14
14
  export declare function isConstDartValueNode(value: RenderableValueNode): boolean;
15
+ /** Escape IDL text so it remains data inside a Dart string literal. */
16
+ export declare function toDartStringLiteral(value: string): string;
17
+ /** Reject executable or lossy sizes from serialized IDLs before generating Dart. */
18
+ export declare function getNonNegativeInteger(value: number): string;
15
19
  export {};
16
20
  //# sourceMappingURL=valueNodes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"valueNodes.d.ts","sourceRoot":"","sources":["../../../src/utils/valueNodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,SAAS,EACV,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,KAAK,mBAAmB,GAAG,yBAAyB,GAAG,SAAS,CAAC;AAEjE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,CAAC,EAAE,MAAM,GAChB,QAAQ,CAsCV;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAWxE"}
1
+ {"version":3,"file":"valueNodes.d.ts","sourceRoot":"","sources":["../../../src/utils/valueNodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,SAAS,EACV,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,KAAK,mBAAmB,GAAG,yBAAyB,GAAG,SAAS,CAAC;AAEjE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,CAAC,EAAE,MAAM,GAChB,QAAQ,CAmDV;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAWxE;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUzD;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"getRenderMapVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getRenderMapVisitor.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,OAAO,EASb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,qBAAqB,CAAC;AAa5E;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,aAAa,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,SAAS,CAAC,CAwK9H"}
1
+ {"version":3,"file":"getRenderMapVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getRenderMapVisitor.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,OAAO,EASb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,qBAAqB,CAAC;AAa5E;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,aAAa,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,SAAS,CAAC,CA2K9H"}
@@ -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" | "instructionStatusNode" | "pdaNode" | "programNode" | "pdaSeedValueNode" | "fixedCountNode" | "prefixedCountNode" | "remainderCountNode" | "accountLinkNode" | "definedTypeLinkNode" | "instructionAccountLinkNode" | "instructionArgumentLinkNode" | "instructionLinkNode" | "pdaLinkNode" | "constantPdaSeedNode" | "variablePdaSeedNode" | "enumEmptyVariantTypeNode" | "enumStructVariantTypeNode" | "enumTupleVariantTypeNode" | "structFieldTypeNode" | "mapEntryValueNode" | "structFieldValueNode" | "rootNode">;
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,6xDAmnBA"}
1
+ {"version":3,"file":"getTypeManifestVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getTypeManifestVisitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EAgCrB,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;AAG3E;;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,8kEA8mBA"}
@@ -1 +1 @@
1
- {"version":3,"file":"renderVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/renderVisitor.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,aAAkB,6DAqE5B"}
1
+ {"version":3,"file":"renderVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/renderVisitor.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,aAAkB,6DAmE5B"}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "dependencies": {
3
- "@codama/errors": "^1.10.0",
4
- "@codama/nodes": "^1.10.0",
5
- "@codama/renderers-core": "^1.3.12",
6
- "@codama/visitors-core": "^1.10.0"
3
+ "@codama/errors": "^1.10.2",
4
+ "@codama/nodes": "^1.10.2",
5
+ "@codama/renderers-core": "^1.4.0",
6
+ "@codama/visitors-core": "^1.10.2"
7
7
  },
8
8
  "description": "Codama renderer for generating Dart code targeting the solana_kit SDK",
9
9
  "devDependencies": {
10
- "@codama/nodes-from-anchor": "^1.5.4",
11
- "@codama/renderers-js": "^2.3.1",
10
+ "@codama/nodes-from-anchor": "^1.5.5",
11
+ "@codama/renderers-js": "^2.4.0",
12
12
  "@types/node": "25.3.0",
13
13
  "@vitest/coverage-v8": "4.1.0",
14
14
  "rimraf": "6.1.3",
@@ -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.1",
58
+ "version": "0.5.4",
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",
@@ -16,6 +16,7 @@ import { getDiscriminatorValidationFragment } from "../utils/discriminators.js";
16
16
  import { getTopLevelDecoderFragment } from "../utils/exactDecoder.js";
17
17
  import {
18
18
  getDartValueFragment,
19
+ getNonNegativeInteger,
19
20
  isConstDartValueNode,
20
21
  } from "../utils/valueNodes.js";
21
22
  import { getDiscriminatorConstantsFragment } from "./discriminatorConstants.js";
@@ -160,7 +161,7 @@ export function getAccountPageFragment(
160
161
  const sizeFragment = node.size != null
161
162
  ? fragment`
162
163
  /// The size of the [${fragmentFromString(typeName)}] account data in bytes.
163
- const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(String(node.size))};`
164
+ const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(getNonNegativeInteger(node.size))};`
164
165
  : emptyFragment();
165
166
 
166
167
  // Discriminator
@@ -8,6 +8,7 @@ import {
8
8
  mergeFragments,
9
9
  use,
10
10
  } from "../utils/fragment.js";
11
+ import { getNonNegativeInteger, toDartStringLiteral } from "../utils/valueNodes.js";
11
12
  import type { RenderScope } from "../utils/options.js";
12
13
  import { camelCase, pascalCase, screamingSnakeCase } from "../utils/nameTransformers.js";
13
14
 
@@ -30,11 +31,12 @@ export function getErrorPageFragment(
30
31
  programName,
31
32
  err.name as string,
32
33
  );
34
+ getNonNegativeInteger(err.code);
33
35
  const hexCode = `0x${err.code.toString(16)}`;
34
36
  const docs = err.docs?.length
35
- ? err.docs.map((d) => `/// ${d}`).join("\n") + "\n"
37
+ ? err.docs.map(toDartDocComment).join("\n") + "\n"
36
38
  : "";
37
- const message = err.message ? `/// Message: "${err.message}"\n` : "";
39
+ const message = err.message ? `${toDartDocComment(`Message: "${err.message}"`)}\n` : "";
38
40
  return `${docs}${message}const int ${constName} = ${hexCode}; // ${err.code}`;
39
41
  })
40
42
  .join("\n\n");
@@ -47,7 +49,7 @@ export function getErrorPageFragment(
47
49
  err.name as string,
48
50
  );
49
51
  const message = err.message ?? err.name as string;
50
- return ` ${constName}: '${escapeString(message)}',`;
52
+ return ` ${constName}: ${toDartStringLiteral(message)},`;
51
53
  })
52
54
  .join("\n");
53
55
 
@@ -77,6 +79,7 @@ bool ${fragmentFromString(isErrorFnName)}(int code) {
77
79
  }`;
78
80
  }
79
81
 
80
- function escapeString(str: string): string {
81
- return str.replace(/'/g, "\\'").replace(/\n/g, "\\n");
82
+ /** Prefix every physical line so IDL documentation cannot become Dart code. */
83
+ function toDartDocComment(value: string): string {
84
+ return value.split(/\r\n?|\n/).map((line) => `/// ${line}`).join("\n");
82
85
  }
@@ -172,11 +172,35 @@ export function getInstructionPageFragment(
172
172
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
173
173
  const dataCodecName = `get${typeName}InstructionDataCodec`;
174
174
 
175
- // Determine whether any arg or account name collides with 'programAddress'
176
- const argNames = new Set(args.map((a) => camelCase(a.name as string)));
177
- const accountNames = new Set(accounts.map((a) => camelCase(a.name as string)));
178
- const hasProgramAddressCollision = argNames.has('programAddress') || accountNames.has('programAddress');
179
- const instrProgramParam = hasProgramAddressCollision ? 'instructionProgramAddress' : 'programAddress';
175
+ // Generated parameters and locals must not shadow IDL account or argument names.
176
+ const usedNames = new Set([
177
+ ...args.map((arg) => camelCase(arg.name as string)),
178
+ ...accounts.map((account) => camelCase(account.name as string)),
179
+ ]);
180
+ const instrProgramParam = reserveName(
181
+ usedNames.has("programAddress") ? "instructionProgramAddress" : "programAddress",
182
+ usedNames,
183
+ );
184
+ const instructionDataLocal = reserveName("instructionData", usedNames);
185
+ const signerParams = new Map<InstructionAccountNode, string>();
186
+
187
+ for (const account of accounts) {
188
+ if (account.isSigner === "either") {
189
+ signerParams.set(
190
+ account,
191
+ reserveName(`${camelCase(account.name as string)}IsSigner`, usedNames),
192
+ );
193
+ }
194
+ }
195
+
196
+ const signerParamDeclarations = [...signerParams.values()]
197
+ .map((parameter) => ` bool ${parameter} = true,`)
198
+ .join("\n");
199
+ const signerParamDocs = [...signerParams.entries()]
200
+ .map(([account, parameter]) =>
201
+ `/// Set [${parameter}] to false when [${camelCase(account.name as string)}] does not sign (for example, a multisig authority).`
202
+ )
203
+ .join("\n");
180
204
 
181
205
  // Build the instruction builder function
182
206
  const accountParams = accounts
@@ -214,7 +238,7 @@ export function getInstructionPageFragment(
214
238
  const accountMetas = accounts
215
239
  .map((acc) => {
216
240
  const fieldName = camelCase(acc.name as string);
217
- const role = getAccountRole(acc);
241
+ const role = getAccountRole(acc, signerParams.get(acc));
218
242
  const isOptional = acc.isOptional ?? false;
219
243
  if (isOptional) {
220
244
  if (optionalAccountStrategy === "omitted") {
@@ -316,13 +340,13 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
316
340
 
317
341
  // Instruction builder
318
342
  parts.push(fragment`
319
- /// Creates a [${fragmentFromString(typeName)}] instruction.
343
+ /// Creates a [${fragmentFromString(typeName)}] instruction.${fragmentFromString(signerParamDocs ? `\n${signerParamDocs}` : "")}
320
344
  Instruction ${fragmentFromString(instrFnName)}({
321
345
  required Address ${fragmentFromString(instrProgramParam)},
322
346
  ${fragmentFromString(accountParams)}
323
- ${fragmentFromString(argParams)}
347
+ ${fragmentFromString(argParams)}${fragmentFromString(signerParamDeclarations ? `\n${signerParamDeclarations}` : "")}
324
348
  }) {
325
- final instructionData = ${fragmentFromString(dataClassName)}(
349
+ final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
326
350
  ${fragmentFromString(dataConstruction)}
327
351
  );
328
352
 
@@ -331,7 +355,7 @@ ${fragmentFromString(dataConstruction)}
331
355
  accounts: [
332
356
  ${fragmentFromString(accountMetas)}
333
357
  ],
334
- data: ${fragmentFromString(dataEncoderName)}().encode(instructionData),
358
+ data: ${fragmentFromString(dataEncoderName)}().encode(${instructionDataLocal}),
335
359
  );
336
360
  }`);
337
361
 
@@ -357,14 +381,27 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
357
381
  return result;
358
382
  }
359
383
 
360
- function getAccountRole(acc: InstructionAccountNode): string {
361
- const isSigner = acc.isSigner === true || acc.isSigner === "either";
362
- const isWritable = acc.isWritable ?? false;
384
+ /** Reserves an identifier without shadowing existing builder parameters or locals. */
385
+ function reserveName(preferredName: string, usedNames: Set<string>): string {
386
+ let name = preferredName;
387
+
388
+ while (usedNames.has(name)) {
389
+ name = `${name}_`;
390
+ }
391
+
392
+ usedNames.add(name);
393
+ return name;
394
+ }
395
+
396
+ /** Preserves fixed permissions and lets either-signer accounts choose their role. */
397
+ function getAccountRole(acc: InstructionAccountNode, signerParameter?: string): string {
398
+ const role = acc.isWritable ? "AccountRole.writable" : "AccountRole.readonly";
399
+
400
+ if (signerParameter) {
401
+ return `${signerParameter} ? ${role}Signer : ${role}`;
402
+ }
363
403
 
364
- if (isSigner && isWritable) return "AccountRole.writableSigner";
365
- if (isSigner) return "AccountRole.readonlySigner";
366
- if (isWritable) return "AccountRole.writable";
367
- return "AccountRole.readonly";
404
+ return acc.isSigner === true ? `${role}Signer` : role;
368
405
  }
369
406
 
370
407
  function isDiscriminatorArg(
@@ -10,6 +10,7 @@ import {
10
10
  } from "../utils/fragment.js";
11
11
  import type { RenderScope } from "../utils/options.js";
12
12
  import { camelCase, pascalCase } from "../utils/nameTransformers.js";
13
+ import { getDartValueFragment, toDartStringLiteral } from "../utils/valueNodes.js";
13
14
  import type { TypeManifest } from "../utils/typeManifest.js";
14
15
  import { WELL_KNOWN_ADDRESSES } from "../utils/wellKnownAddresses.js";
15
16
 
@@ -66,9 +67,9 @@ export function getPdaPageFragment(
66
67
  for (const seed of seeds) {
67
68
  if (seed.kind === "constantPdaSeedNode") {
68
69
  if (seed.value.kind === "bytesValueNode") {
69
- seedValues.push(` Uint8List.fromList(${bytesValueToDart(seed.value.data)}),`);
70
+ seedValues.push(` ${getDartValueFragment(seed.value).content},`);
70
71
  } else if (seed.value.kind === "stringValueNode") {
71
- seedValues.push(` '${seed.value.string}',`);
72
+ seedValues.push(` ${toDartStringLiteral(seed.value.string)},`);
72
73
  } else if (seed.value.kind === "publicKeyValueNode") {
73
74
  const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
74
75
  if (wellKnownSeedName) {
@@ -78,13 +79,14 @@ export function getPdaPageFragment(
78
79
  );
79
80
  } else {
80
81
  seedValues.push(
81
- ` getAddressEncoder().encode(Address('${seed.value.publicKey}')),`,
82
+ ` getAddressEncoder().encode(Address(${toDartStringLiteral(seed.value.publicKey)})),`,
82
83
  );
83
84
  }
84
85
  } else if (seed.value.kind === "numberValueNode") {
85
86
  const manifest = visit(seed.type, scope.typeManifestVisitor);
86
87
  seedManifests.push(manifest);
87
- seedValues.push(` ${manifest.encoder.content}.encode(${seed.value.number}),`);
88
+ const value = getDartValueFragment(seed.value, manifest.type.content);
89
+ seedValues.push(` ${manifest.encoder.content}.encode(${value.content}),`);
88
90
  }
89
91
  } else if (seed.kind === "variablePdaSeedNode") {
90
92
  const manifest = visit(seed.type, scope.typeManifestVisitor);
@@ -105,7 +107,7 @@ export function getPdaPageFragment(
105
107
  const programIdParam = node.programId
106
108
  ? wellKnownProgramName
107
109
  ? `Address programAddress = ${wellKnownProgramName}`
108
- : `Address programAddress = const Address('${node.programId}')`
110
+ : `Address programAddress = const Address(${toDartStringLiteral(node.programId)})`
109
111
  : "required Address programAddress";
110
112
 
111
113
  const typedDataImport = hasByteSeeds
@@ -167,12 +169,3 @@ ${fragmentFromString(seedValues.join("\n"))}
167
169
 
168
170
  return result;
169
171
  }
170
-
171
- function bytesValueToDart(hex: string): string {
172
- const clean = hex.replace(/^0x/, "");
173
- const pairs: string[] = [];
174
- for (let i = 0; i < clean.length; i += 2) {
175
- pairs.push(`0x${clean.slice(i, i + 2)}`);
176
- }
177
- return `[${pairs.join(", ")}]`;
178
- }
@@ -16,6 +16,7 @@ import {
16
16
  } from "../utils/fragment.js";
17
17
  import type { RenderScope } from "../utils/options.js";
18
18
  import { camelCase, pascalCase } from "../utils/nameTransformers.js";
19
+ import { toDartStringLiteral } from "../utils/valueNodes.js";
19
20
  import { WELL_KNOWN_ADDRESSES } from "../utils/wellKnownAddresses.js";
20
21
 
21
22
  /**
@@ -55,7 +56,7 @@ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownNa
55
56
  ${use("Address", "solanaAddresses")}
56
57
 
57
58
  /// The address of the ${fragmentFromString(pascalCase(name))} program.
58
- const ${fragmentFromString(addressConstName)} = Address('${fragmentFromString(node.publicKey)}');`;
59
+ const ${fragmentFromString(addressConstName)} = Address(${fragmentFromString(toDartStringLiteral(node.publicKey))});`;
59
60
 
60
61
  const parts: Fragment[] = [addressDeclaration];
61
62
 
@@ -232,7 +233,7 @@ function getValueFragment(
232
233
  case "booleanValueNode":
233
234
  return fragmentFromString(String(value.boolean));
234
235
  case "stringValueNode":
235
- return fragmentFromString(`'${value.string.replaceAll("'", "\\'")}'`);
236
+ return fragmentFromString(toDartStringLiteral(value.string));
236
237
  case "bytesValueNode": {
237
238
  const clean = value.data.replace(/^0x/, "");
238
239
  const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
@@ -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) => `${camelCase(f.name as string)} == other.${camelCase(f.name as string)}`)
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 = getHashExpression(
219
- fields.map((f: StructFieldTypeNode) => camelCase(f.name as string)),
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
- : fields
433
- .map(
434
- (f: StructFieldTypeNode) =>
435
- `${camelCase(f.name as string)} == other.${camelCase(f.name as string)}`,
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
- const hashFields =
440
- fields.length === 0
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(getHashExpression(fields.map((f: StructFieldTypeNode) => camelCase(f.name as string))))};
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`;
@@ -1,4 +1,4 @@
1
- import { execSync } from "node:child_process";
1
+ import { execFileSync, execSync } from "node:child_process";
2
2
 
3
3
  /**
4
4
  * Format Dart code using `dart format`.
@@ -23,7 +23,7 @@ export function formatDartCode(code: string): string {
23
23
  * Format all Dart files in a directory using `dart format`.
24
24
  */
25
25
  export function formatDartDirectory(dir: string): void {
26
- execSync(`dart format "${dir}"`, {
26
+ execFileSync("dart", ["format", "--", dir], {
27
27
  encoding: "utf-8",
28
28
  timeout: 60_000,
29
29
  stdio: ["pipe", "pipe", "pipe"],