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.
- package/{readme.md → README.md} +16 -43
- package/dist/index.browser.cjs +345 -124
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +346 -125
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +345 -124
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +346 -125
- package/dist/index.node.js.map +1 -1
- package/dist/index.react-native.js +346 -125
- package/dist/index.react-native.js.map +1 -1
- package/dist/types/fragments/accountPage.d.ts.map +1 -1
- package/dist/types/fragments/errorPage.d.ts.map +1 -1
- package/dist/types/fragments/instructionPage.d.ts.map +1 -1
- package/dist/types/fragments/pdaPage.d.ts.map +1 -1
- package/dist/types/fragments/programPage.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/utils/valueNodes.d.ts +4 -0
- package/dist/types/utils/valueNodes.d.ts.map +1 -1
- package/dist/types/visitors/getRenderMapVisitor.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/dist/types/visitors/renderVisitor.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/fragments/accountPage.ts +2 -1
- package/src/fragments/errorPage.ts +8 -5
- package/src/fragments/instructionPage.ts +54 -17
- package/src/fragments/pdaPage.ts +7 -14
- package/src/fragments/programPage.ts +3 -2
- package/src/fragments/typePage.ts +102 -19
- package/src/utils/formatCode.ts +2 -2
- package/src/utils/nameTransformers.ts +39 -1
- package/src/utils/valueNodes.ts +24 -1
- package/src/visitors/getRenderMapVisitor.ts +30 -5
- package/src/visitors/getTypeManifestVisitor.ts +123 -81
- package/src/visitors/renderVisitor.ts +11 -13
package/dist/index.browser.js
CHANGED
|
@@ -2,13 +2,13 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
|
2
2
|
import { join, dirname, posix } from 'path';
|
|
3
3
|
import { staticVisitor, extendVisitor, visit, NodeStack, LinkableDictionary, pipe, recordNodeStackVisitor, recordLinkablesOnFirstVisitVisitor, rootNodeVisitor } from '@codama/visitors-core';
|
|
4
4
|
import { createRenderMap, addToRenderMap, mergeRenderMaps, deleteDirectory } from '@codama/renderers-core';
|
|
5
|
-
import {
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
6
|
import { resolveNestedTypeNode, getAllInstructionsWithSubs } from '@codama/nodes';
|
|
7
7
|
import { CodamaError, CODAMA_ERROR__UNEXPECTED_NODE_KIND } from '@codama/errors';
|
|
8
8
|
|
|
9
9
|
// src/visitors/renderVisitor.ts
|
|
10
10
|
function formatDartDirectory(dir) {
|
|
11
|
-
|
|
11
|
+
execFileSync("dart", ["format", "--", dir], {
|
|
12
12
|
encoding: "utf-8",
|
|
13
13
|
timeout: 6e4,
|
|
14
14
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -78,13 +78,89 @@ function setEmptyCollection(record, key) {
|
|
|
78
78
|
|
|
79
79
|
// src/utils/nameTransformers.ts
|
|
80
80
|
function pascalCase(str) {
|
|
81
|
+
assertSafeName(str);
|
|
81
82
|
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
|
|
82
83
|
}
|
|
84
|
+
var DART_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
85
|
+
"abstract",
|
|
86
|
+
"as",
|
|
87
|
+
"assert",
|
|
88
|
+
"async",
|
|
89
|
+
"augment",
|
|
90
|
+
"await",
|
|
91
|
+
"base",
|
|
92
|
+
"break",
|
|
93
|
+
"case",
|
|
94
|
+
"catch",
|
|
95
|
+
"class",
|
|
96
|
+
"const",
|
|
97
|
+
"continue",
|
|
98
|
+
"covariant",
|
|
99
|
+
"default",
|
|
100
|
+
"deferred",
|
|
101
|
+
"do",
|
|
102
|
+
"dynamic",
|
|
103
|
+
"else",
|
|
104
|
+
"enum",
|
|
105
|
+
"export",
|
|
106
|
+
"extends",
|
|
107
|
+
"extension",
|
|
108
|
+
"external",
|
|
109
|
+
"factory",
|
|
110
|
+
"false",
|
|
111
|
+
"final",
|
|
112
|
+
"finally",
|
|
113
|
+
"for",
|
|
114
|
+
"get",
|
|
115
|
+
"hide",
|
|
116
|
+
"if",
|
|
117
|
+
"implements",
|
|
118
|
+
"import",
|
|
119
|
+
"in",
|
|
120
|
+
"interface",
|
|
121
|
+
"is",
|
|
122
|
+
"late",
|
|
123
|
+
"library",
|
|
124
|
+
"mixin",
|
|
125
|
+
"native",
|
|
126
|
+
"new",
|
|
127
|
+
"null",
|
|
128
|
+
"of",
|
|
129
|
+
"on",
|
|
130
|
+
"operator",
|
|
131
|
+
"part",
|
|
132
|
+
"required",
|
|
133
|
+
"rethrow",
|
|
134
|
+
"return",
|
|
135
|
+
"sealed",
|
|
136
|
+
"set",
|
|
137
|
+
"show",
|
|
138
|
+
"static",
|
|
139
|
+
"super",
|
|
140
|
+
"switch",
|
|
141
|
+
"sync",
|
|
142
|
+
"this",
|
|
143
|
+
"throw",
|
|
144
|
+
"true",
|
|
145
|
+
"try",
|
|
146
|
+
"typedef",
|
|
147
|
+
"var",
|
|
148
|
+
"void",
|
|
149
|
+
"when",
|
|
150
|
+
"while",
|
|
151
|
+
"with",
|
|
152
|
+
"yield"
|
|
153
|
+
]);
|
|
154
|
+
function escapeDartIdentifier(name) {
|
|
155
|
+
return DART_RESERVED_WORDS.has(name) ? `${name}_` : name;
|
|
156
|
+
}
|
|
83
157
|
function camelCase(str) {
|
|
84
158
|
const pascal = pascalCase(str);
|
|
85
|
-
|
|
159
|
+
const cased = pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
160
|
+
return escapeDartIdentifier(cased);
|
|
86
161
|
}
|
|
87
162
|
function snakeCase(str) {
|
|
163
|
+
assertSafeName(str);
|
|
88
164
|
return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").replace(/^_/, "").replace(/_+/g, "_").toLowerCase();
|
|
89
165
|
}
|
|
90
166
|
function createDartNameApi() {
|
|
@@ -110,6 +186,11 @@ function createDartNameApi() {
|
|
|
110
186
|
discriminatorConstant: (name) => `${camelCase(name)}Discriminator`
|
|
111
187
|
};
|
|
112
188
|
}
|
|
189
|
+
function assertSafeName(name) {
|
|
190
|
+
if (!/^[a-zA-Z0-9_ -]*$/.test(name)) {
|
|
191
|
+
throw new Error(`Invalid Dart node name: ${JSON.stringify(name)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
113
194
|
|
|
114
195
|
// src/utils/codecs.ts
|
|
115
196
|
function bytesToDartHexList(bytes) {
|
|
@@ -320,10 +401,19 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
|
|
|
320
401
|
function getDartValueFragment(value, dartType) {
|
|
321
402
|
switch (value.kind) {
|
|
322
403
|
case "numberValueNode":
|
|
404
|
+
if (typeof value.number !== "number" || !Number.isFinite(value.number)) {
|
|
405
|
+
throw new Error("Invalid Dart number: expected a finite number");
|
|
406
|
+
}
|
|
407
|
+
if (dartType === "BigInt" && !Number.isSafeInteger(value.number)) {
|
|
408
|
+
throw new Error("Invalid Dart BigInt: expected a safe integer");
|
|
409
|
+
}
|
|
323
410
|
return fragmentFromString(
|
|
324
411
|
dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
|
|
325
412
|
);
|
|
326
413
|
case "booleanValueNode":
|
|
414
|
+
if (typeof value.boolean !== "boolean") {
|
|
415
|
+
throw new Error("Invalid Dart boolean: expected a boolean");
|
|
416
|
+
}
|
|
327
417
|
return fragmentFromString(String(value.boolean));
|
|
328
418
|
case "stringValueNode":
|
|
329
419
|
return fragmentFromString(toDartStringLiteral(value.string));
|
|
@@ -367,6 +457,12 @@ function isConstDartValueNode(value) {
|
|
|
367
457
|
function toDartStringLiteral(value) {
|
|
368
458
|
return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
|
|
369
459
|
}
|
|
460
|
+
function getNonNegativeInteger(value) {
|
|
461
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
462
|
+
throw new Error("Expected a non-negative safe integer value");
|
|
463
|
+
}
|
|
464
|
+
return String(value);
|
|
465
|
+
}
|
|
370
466
|
|
|
371
467
|
// src/utils/discriminators.ts
|
|
372
468
|
function getDiscriminatorValidationFragment(node, scope) {
|
|
@@ -567,23 +663,12 @@ function getTypeManifestVisitor(input) {
|
|
|
567
663
|
}
|
|
568
664
|
);
|
|
569
665
|
return extendVisitor(baseVisitor, {
|
|
570
|
-
visitNumberType(node
|
|
571
|
-
const { format, endian } = node;
|
|
572
|
-
const endianSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
|
|
573
|
-
const endianDecoderSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
|
|
574
|
-
const dartType = getNumberDartType(format);
|
|
575
|
-
const codecName = getNumberCodecName(format);
|
|
666
|
+
visitNumberType(node) {
|
|
576
667
|
return {
|
|
577
668
|
// `BigInt` and other number types live in `dart:core` and need no import.
|
|
578
|
-
type: fragmentFromString(
|
|
579
|
-
encoder:
|
|
580
|
-
|
|
581
|
-
"solanaCodecsNumbers"
|
|
582
|
-
)}(${endianSuffix ? fragmentFromString(endianSuffix.slice(2)) : fragmentFromString("")})`,
|
|
583
|
-
decoder: fragment`${use(
|
|
584
|
-
`get${codecName}Decoder`,
|
|
585
|
-
"solanaCodecsNumbers"
|
|
586
|
-
)}(${endianDecoderSuffix ? fragmentFromString(endianDecoderSuffix.slice(2)) : fragmentFromString("")})`,
|
|
669
|
+
type: fragmentFromString(getNumberDartType(node.format)),
|
|
670
|
+
encoder: getNumberCodecExpression(node, "Encoder"),
|
|
671
|
+
decoder: getNumberCodecExpression(node, "Decoder"),
|
|
587
672
|
value: emptyTypeManifest().value,
|
|
588
673
|
isEnum: false
|
|
589
674
|
};
|
|
@@ -670,7 +755,7 @@ function getTypeManifestVisitor(input) {
|
|
|
670
755
|
encoder: fragment`${use(
|
|
671
756
|
"getArrayEncoder",
|
|
672
757
|
"solanaCodecsDataStructures"
|
|
673
|
-
)}
|
|
758
|
+
)}(${itemEncoder}${encoderSizeExpr})`,
|
|
674
759
|
decoder: fragment`${use(
|
|
675
760
|
"getArrayDecoder",
|
|
676
761
|
"solanaCodecsDataStructures"
|
|
@@ -726,18 +811,28 @@ function getTypeManifestVisitor(input) {
|
|
|
726
811
|
typeFragment.imports.mergeWith(m.type.imports);
|
|
727
812
|
}
|
|
728
813
|
const encoderListStr = encoderList.map((e) => e.content).join(", ");
|
|
814
|
+
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
815
|
+
let tupleEncoderFn = "getTupleEncoder";
|
|
816
|
+
let tupleDecoderFn = "getTupleDecoder";
|
|
817
|
+
let tupleEncoderArgs = `[${encoderListStr}]`;
|
|
818
|
+
let tupleDecoderArgs = `[${decoderListStr}]`;
|
|
819
|
+
if (itemManifests.length === 2) {
|
|
820
|
+
tupleEncoderFn = "getTuple2Encoder";
|
|
821
|
+
tupleDecoderFn = "getTuple2Decoder";
|
|
822
|
+
tupleEncoderArgs = encoderListStr;
|
|
823
|
+
tupleDecoderArgs = decoderListStr;
|
|
824
|
+
}
|
|
729
825
|
const encoderFrag = fragment`${use(
|
|
730
|
-
|
|
826
|
+
tupleEncoderFn,
|
|
731
827
|
"solanaCodecsDataStructures"
|
|
732
|
-
)}(
|
|
828
|
+
)}(${tupleEncoderArgs})`;
|
|
733
829
|
for (const e of encoderList) {
|
|
734
830
|
encoderFrag.imports.mergeWith(e.imports);
|
|
735
831
|
}
|
|
736
|
-
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
737
832
|
const decoderFrag = fragment`${use(
|
|
738
|
-
|
|
833
|
+
tupleDecoderFn,
|
|
739
834
|
"solanaCodecsDataStructures"
|
|
740
|
-
)}(
|
|
835
|
+
)}(${tupleDecoderArgs})`;
|
|
741
836
|
for (const d of decoderList) {
|
|
742
837
|
decoderFrag.imports.mergeWith(d.imports);
|
|
743
838
|
}
|
|
@@ -754,7 +849,7 @@ function getTypeManifestVisitor(input) {
|
|
|
754
849
|
const fields = nodeFields.map((f) => visit(f, self));
|
|
755
850
|
const fieldNames = nodeFields.map((f) => f.name);
|
|
756
851
|
const typeStr = "Map<String, Object?>";
|
|
757
|
-
const encoderFields = fields.map((f, i) => `(
|
|
852
|
+
const encoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.encoder.content})`).join(", ");
|
|
758
853
|
const encoderFrag = fragment`${use(
|
|
759
854
|
"getStructEncoder",
|
|
760
855
|
"solanaCodecsDataStructures"
|
|
@@ -762,7 +857,7 @@ function getTypeManifestVisitor(input) {
|
|
|
762
857
|
for (const f of fields) {
|
|
763
858
|
encoderFrag.imports.mergeWith(f.encoder.imports);
|
|
764
859
|
}
|
|
765
|
-
const decoderFields = fields.map((f, i) => `(
|
|
860
|
+
const decoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.decoder.content})`).join(", ");
|
|
766
861
|
const decoderFrag = fragment`${use(
|
|
767
862
|
"getStructDecoder",
|
|
768
863
|
"solanaCodecsDataStructures"
|
|
@@ -816,19 +911,22 @@ function getTypeManifestVisitor(input) {
|
|
|
816
911
|
const itemManifest = visit(node.item, self);
|
|
817
912
|
const itemTypeStr = itemManifest.type.content;
|
|
818
913
|
const resolvedPrefix = resolveNestedTypeNode(node.prefix);
|
|
819
|
-
const prefixExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix
|
|
820
|
-
const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix
|
|
914
|
+
const prefixExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Encoder")}` : "";
|
|
915
|
+
const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Decoder")}` : "";
|
|
821
916
|
const noneValueExpr = node.fixed ? ", noneValue: const ZeroesNoneValue()" : "";
|
|
822
917
|
return {
|
|
823
918
|
type: fragment`${itemManifest.type}?`,
|
|
824
919
|
encoder: fragment`${use(
|
|
825
920
|
"getNullableEncoder",
|
|
826
921
|
"solanaCodecsDataStructures"
|
|
827
|
-
)}<${fragmentFromString(itemTypeStr)}>(${
|
|
922
|
+
)}<${fragmentFromString(itemTypeStr)}>(${use(
|
|
923
|
+
"transformEncoder",
|
|
924
|
+
"solanaCodecsCore"
|
|
925
|
+
)}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${prefixExpr}${fragmentFromString(noneValueExpr)})`,
|
|
828
926
|
decoder: fragment`${use(
|
|
829
927
|
"getNullableDecoder",
|
|
830
928
|
"solanaCodecsDataStructures"
|
|
831
|
-
)}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${
|
|
929
|
+
)}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${prefixDecoderExpr}${fragmentFromString(noneValueExpr)})`,
|
|
832
930
|
value: emptyTypeManifest().value,
|
|
833
931
|
isEnum: false
|
|
834
932
|
};
|
|
@@ -874,11 +972,11 @@ function getTypeManifestVisitor(input) {
|
|
|
874
972
|
encoder: fragment`${use(
|
|
875
973
|
"fixEncoderSize",
|
|
876
974
|
"solanaCodecsCore"
|
|
877
|
-
)}(${innerManifest.encoder}, ${fragmentFromString(
|
|
975
|
+
)}(${innerManifest.encoder}, ${fragmentFromString(getNonNegativeInteger(node.size))}, allowTruncation: false)`,
|
|
878
976
|
decoder: fragment`${use(
|
|
879
977
|
"fixDecoderSize",
|
|
880
978
|
"solanaCodecsCore"
|
|
881
|
-
)}(${innerManifest.decoder}, ${fragmentFromString(
|
|
979
|
+
)}(${innerManifest.decoder}, ${fragmentFromString(getNonNegativeInteger(node.size))})`,
|
|
882
980
|
value: innerManifest.value,
|
|
883
981
|
isEnum: innerManifest.isEnum
|
|
884
982
|
};
|
|
@@ -888,10 +986,10 @@ function getTypeManifestVisitor(input) {
|
|
|
888
986
|
const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
|
|
889
987
|
let prefixManifest;
|
|
890
988
|
if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
|
|
891
|
-
const
|
|
989
|
+
const numberManifest = visit(node.prefix, self);
|
|
892
990
|
prefixManifest = {
|
|
893
|
-
encoder: fragment
|
|
894
|
-
decoder: fragment
|
|
991
|
+
encoder: fragment`${use("transformEncoder", "solanaCodecsCore")}(${numberManifest.encoder}, (size) => BigInt.from(size))`,
|
|
992
|
+
decoder: fragment`${use("transformDecoder", "solanaCodecsCore")}(${numberManifest.decoder}, (size, _, __) => size.toInt())`
|
|
895
993
|
};
|
|
896
994
|
} else {
|
|
897
995
|
prefixManifest = visit(node.prefix, self);
|
|
@@ -982,10 +1080,10 @@ function getTypeManifestVisitor(input) {
|
|
|
982
1080
|
};
|
|
983
1081
|
},
|
|
984
1082
|
visitPreOffsetType(node, { self }) {
|
|
985
|
-
return
|
|
1083
|
+
return getOffsetManifest(node, self);
|
|
986
1084
|
},
|
|
987
1085
|
visitPostOffsetType(node, { self }) {
|
|
988
|
-
return
|
|
1086
|
+
return getOffsetManifest(node, self);
|
|
989
1087
|
},
|
|
990
1088
|
visitSolAmountType(node, { self }) {
|
|
991
1089
|
return visit(node.number, self);
|
|
@@ -1074,16 +1172,17 @@ function getNumberCodecName(format) {
|
|
|
1074
1172
|
return "U8";
|
|
1075
1173
|
}
|
|
1076
1174
|
}
|
|
1077
|
-
function getNumberCodecExpression(
|
|
1078
|
-
const name = getNumberCodecName(format);
|
|
1079
|
-
|
|
1175
|
+
function getNumberCodecExpression(node, codecType) {
|
|
1176
|
+
const name = getNumberCodecName(node.format);
|
|
1177
|
+
const config = node.endian === "be" ? fragment`${use("NumberCodecConfig", "solanaCodecsNumbers")}(endian: ${use("Endian", "dartTypedData")}.big)` : "";
|
|
1178
|
+
return fragment`${use(`get${name}${codecType}`, "solanaCodecsNumbers")}(${config})`;
|
|
1080
1179
|
}
|
|
1081
1180
|
function getArraySizeExpression(node, codecType) {
|
|
1082
1181
|
if (!("count" in node) || !node.count) return "";
|
|
1083
1182
|
const count = node.count;
|
|
1084
1183
|
switch (count.kind) {
|
|
1085
1184
|
case "fixedCountNode":
|
|
1086
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1185
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1087
1186
|
case "remainderCountNode":
|
|
1088
1187
|
return ", size: RemainderArraySize()";
|
|
1089
1188
|
case "prefixedCountNode": {
|
|
@@ -1091,7 +1190,7 @@ function getArraySizeExpression(node, codecType) {
|
|
|
1091
1190
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1092
1191
|
return "";
|
|
1093
1192
|
}
|
|
1094
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1193
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1095
1194
|
}
|
|
1096
1195
|
default:
|
|
1097
1196
|
return "";
|
|
@@ -1102,7 +1201,7 @@ function getMapSizeExpression(node, codecType) {
|
|
|
1102
1201
|
const count = node.count;
|
|
1103
1202
|
switch (count.kind) {
|
|
1104
1203
|
case "fixedCountNode":
|
|
1105
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1204
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1106
1205
|
case "remainderCountNode":
|
|
1107
1206
|
return ", size: RemainderArraySize()";
|
|
1108
1207
|
case "prefixedCountNode": {
|
|
@@ -1110,7 +1209,7 @@ function getMapSizeExpression(node, codecType) {
|
|
|
1110
1209
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1111
1210
|
return "";
|
|
1112
1211
|
}
|
|
1113
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1212
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1114
1213
|
}
|
|
1115
1214
|
default:
|
|
1116
1215
|
return "";
|
|
@@ -1121,7 +1220,7 @@ function getSetSizeExpression(node, codecType) {
|
|
|
1121
1220
|
const count = node.count;
|
|
1122
1221
|
switch (count.kind) {
|
|
1123
1222
|
case "fixedCountNode":
|
|
1124
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1223
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1125
1224
|
case "remainderCountNode":
|
|
1126
1225
|
return ", size: RemainderArraySize()";
|
|
1127
1226
|
case "prefixedCountNode": {
|
|
@@ -1129,7 +1228,7 @@ function getSetSizeExpression(node, codecType) {
|
|
|
1129
1228
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1130
1229
|
return "";
|
|
1131
1230
|
}
|
|
1132
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1231
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1133
1232
|
}
|
|
1134
1233
|
default:
|
|
1135
1234
|
return "";
|
|
@@ -1140,36 +1239,61 @@ function getHiddenAffixManifest(node, self) {
|
|
|
1140
1239
|
const manifest = visit(node, self);
|
|
1141
1240
|
return { encoder: manifest.encoder, decoder: manifest.decoder };
|
|
1142
1241
|
}
|
|
1143
|
-
const constantBytes = getConstantBytesExpression(node);
|
|
1242
|
+
const constantBytes = getConstantBytesExpression(node, self);
|
|
1144
1243
|
return {
|
|
1145
1244
|
encoder: fragment`${use("getConstantEncoder", "solanaCodecsDataStructures")}(${constantBytes})`,
|
|
1146
1245
|
decoder: fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(${constantBytes})`
|
|
1147
1246
|
};
|
|
1148
1247
|
}
|
|
1149
|
-
function getConstantBytesExpression(node) {
|
|
1150
|
-
|
|
1151
|
-
const typeNode = node.type;
|
|
1152
|
-
if (valueNode?.kind !== "numberValueNode") {
|
|
1248
|
+
function getConstantBytesExpression(node, self) {
|
|
1249
|
+
if (node.value.kind !== "numberValueNode") {
|
|
1153
1250
|
throw new Error(
|
|
1154
|
-
`Unsupported hidden affix constant value kind: ${
|
|
1251
|
+
`Unsupported hidden affix constant value kind: ${node.value.kind}`
|
|
1155
1252
|
);
|
|
1156
1253
|
}
|
|
1157
|
-
const
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1254
|
+
const manifest = visit(node.type, self);
|
|
1255
|
+
if (!["int", "BigInt", "double"].includes(manifest.type.content)) {
|
|
1256
|
+
throw new Error(`Unsupported hidden affix constant type kind: ${node.type.kind}`);
|
|
1160
1257
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
}
|
|
1168
|
-
return fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${use(`get${getNumberCodecName(inner.format)}Encoder`, "solanaCodecsNumbers")}(), ${typeNode.offset}).encode(${value})`;
|
|
1258
|
+
const value = getDartValueFragment(node.value, manifest.type.content);
|
|
1259
|
+
return fragment`${manifest.encoder}.encode(${value})`;
|
|
1260
|
+
}
|
|
1261
|
+
function getOffsetManifest(node, self) {
|
|
1262
|
+
if (!Number.isSafeInteger(node.offset)) {
|
|
1263
|
+
throw new Error("Expected a safe integer for a codec offset");
|
|
1169
1264
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1265
|
+
const manifest = visit(node.type, self);
|
|
1266
|
+
const pre = node.kind === "preOffsetTypeNode";
|
|
1267
|
+
const key = pre ? "preOffset" : "postOffset";
|
|
1268
|
+
if (node.strategy === "padded") {
|
|
1269
|
+
const side = pre ? "Left" : "Right";
|
|
1270
|
+
const offset = getNonNegativeInteger(node.offset);
|
|
1271
|
+
return {
|
|
1272
|
+
...manifest,
|
|
1273
|
+
encoder: fragment`${use(`pad${side}Encoder`, "solanaCodecsCore")}(${manifest.encoder}, ${offset})`,
|
|
1274
|
+
decoder: fragment`${use(`pad${side}Decoder`, "solanaCodecsCore")}(${manifest.decoder}, ${offset})`
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
let expression;
|
|
1278
|
+
switch (node.strategy) {
|
|
1279
|
+
case "absolute":
|
|
1280
|
+
expression = node.offset < 0 ? `scope.wrapBytes(${node.offset})` : String(node.offset);
|
|
1281
|
+
break;
|
|
1282
|
+
case "relative":
|
|
1283
|
+
expression = `scope.${key} + ${node.offset}`;
|
|
1284
|
+
break;
|
|
1285
|
+
case "preOffset":
|
|
1286
|
+
expression = `scope.preOffset + ${node.offset}`;
|
|
1287
|
+
break;
|
|
1288
|
+
default:
|
|
1289
|
+
throw new Error(`Unsupported offset strategy: ${node.strategy}`);
|
|
1290
|
+
}
|
|
1291
|
+
const config = fragment`${use("OffsetConfig", "solanaCodecsCore")}(${key}: (scope) => ${expression})`;
|
|
1292
|
+
return {
|
|
1293
|
+
...manifest,
|
|
1294
|
+
encoder: fragment`${use("offsetEncoder", "solanaCodecsCore")}(${manifest.encoder}, ${config})`,
|
|
1295
|
+
decoder: fragment`${use("offsetDecoder", "solanaCodecsCore")}(${manifest.decoder}, ${config})`
|
|
1296
|
+
};
|
|
1173
1297
|
}
|
|
1174
1298
|
|
|
1175
1299
|
// src/fragments/discriminatorConstants.ts
|
|
@@ -1302,7 +1426,7 @@ ${ctorParams}
|
|
|
1302
1426
|
const decodeFnName = scope.nameApi.accountDecodeFunction(name);
|
|
1303
1427
|
const sizeFragment = node.size != null ? fragment`
|
|
1304
1428
|
/// The size of the [${fragmentFromString(typeName)}] account data in bytes.
|
|
1305
|
-
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(
|
|
1429
|
+
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(getNonNegativeInteger(node.size))};` : emptyFragment();
|
|
1306
1430
|
const discFragment = getDiscriminatorConstantsFragment(node, scope);
|
|
1307
1431
|
const discriminatorValidation = getDiscriminatorValidationFragment(
|
|
1308
1432
|
node,
|
|
@@ -1416,9 +1540,10 @@ function getErrorPageFragment(programNode, scope) {
|
|
|
1416
1540
|
programName,
|
|
1417
1541
|
err.name
|
|
1418
1542
|
);
|
|
1543
|
+
getNonNegativeInteger(err.code);
|
|
1419
1544
|
const hexCode = `0x${err.code.toString(16)}`;
|
|
1420
|
-
const docs = err.docs?.length ? err.docs.map(
|
|
1421
|
-
const message = err.message ?
|
|
1545
|
+
const docs = err.docs?.length ? err.docs.map(toDartDocComment).join("\n") + "\n" : "";
|
|
1546
|
+
const message = err.message ? `${toDartDocComment(`Message: "${err.message}"`)}
|
|
1422
1547
|
` : "";
|
|
1423
1548
|
return `${docs}${message}const int ${constName} = ${hexCode}; // ${err.code}`;
|
|
1424
1549
|
}).join("\n\n");
|
|
@@ -1428,7 +1553,7 @@ function getErrorPageFragment(programNode, scope) {
|
|
|
1428
1553
|
err.name
|
|
1429
1554
|
);
|
|
1430
1555
|
const message = err.message ?? err.name;
|
|
1431
|
-
return ` ${constName}:
|
|
1556
|
+
return ` ${constName}: ${toDartStringLiteral(message)},`;
|
|
1432
1557
|
}).join("\n");
|
|
1433
1558
|
const errorMessageFnName = scope.nameApi.errorMessageFunction(programName);
|
|
1434
1559
|
const isErrorFnName = `is${pascalCase(programName)}Error`;
|
|
@@ -1454,8 +1579,8 @@ bool ${fragmentFromString(isErrorFnName)}(int code) {
|
|
|
1454
1579
|
return _${fragmentFromString(camelCase(programName))}ErrorMessages.containsKey(code);
|
|
1455
1580
|
}`;
|
|
1456
1581
|
}
|
|
1457
|
-
function
|
|
1458
|
-
return
|
|
1582
|
+
function toDartDocComment(value) {
|
|
1583
|
+
return value.split(/\r\n?|\n/).map((line) => `/// ${line}`).join("\n");
|
|
1459
1584
|
}
|
|
1460
1585
|
|
|
1461
1586
|
// src/fragments/indexPage.ts
|
|
@@ -1581,10 +1706,28 @@ ${dataCtorInitializers}` : ""};`;
|
|
|
1581
1706
|
const dataEncoderName = `get${typeName}InstructionDataEncoder`;
|
|
1582
1707
|
const dataDecoderName = `get${typeName}InstructionDataDecoder`;
|
|
1583
1708
|
const dataCodecName = `get${typeName}InstructionDataCodec`;
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1709
|
+
const usedNames = /* @__PURE__ */ new Set([
|
|
1710
|
+
...args.map((arg) => camelCase(arg.name)),
|
|
1711
|
+
...accounts.map((account) => camelCase(account.name))
|
|
1712
|
+
]);
|
|
1713
|
+
const instrProgramParam = reserveName(
|
|
1714
|
+
usedNames.has("programAddress") ? "instructionProgramAddress" : "programAddress",
|
|
1715
|
+
usedNames
|
|
1716
|
+
);
|
|
1717
|
+
const instructionDataLocal = reserveName("instructionData", usedNames);
|
|
1718
|
+
const signerParams = /* @__PURE__ */ new Map();
|
|
1719
|
+
for (const account of accounts) {
|
|
1720
|
+
if (account.isSigner === "either") {
|
|
1721
|
+
signerParams.set(
|
|
1722
|
+
account,
|
|
1723
|
+
reserveName(`${camelCase(account.name)}IsSigner`, usedNames)
|
|
1724
|
+
);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
const signerParamDeclarations = [...signerParams.values()].map((parameter) => ` bool ${parameter} = true,`).join("\n");
|
|
1728
|
+
const signerParamDocs = [...signerParams.entries()].map(
|
|
1729
|
+
([account, parameter]) => `/// Set [${parameter}] to false when [${camelCase(account.name)}] does not sign (for example, a multisig authority).`
|
|
1730
|
+
).join("\n");
|
|
1588
1731
|
const accountParams = accounts.map((acc) => {
|
|
1589
1732
|
const fieldName = camelCase(acc.name);
|
|
1590
1733
|
const isRequired = !(acc.isOptional ?? false);
|
|
@@ -1604,7 +1747,7 @@ ${dataCtorInitializers}` : ""};`;
|
|
|
1604
1747
|
}).join("\n");
|
|
1605
1748
|
const accountMetas = accounts.map((acc) => {
|
|
1606
1749
|
const fieldName = camelCase(acc.name);
|
|
1607
|
-
const role = getAccountRole(acc);
|
|
1750
|
+
const role = getAccountRole(acc, signerParams.get(acc));
|
|
1608
1751
|
const isOptional = acc.isOptional ?? false;
|
|
1609
1752
|
if (isOptional) {
|
|
1610
1753
|
if (optionalAccountStrategy === "omitted") {
|
|
@@ -1687,13 +1830,15 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
|
|
|
1687
1830
|
return combineCodec(${fragmentFromString(dataEncoderName)}(), ${fragmentFromString(dataDecoderName)}());
|
|
1688
1831
|
}`);
|
|
1689
1832
|
parts.push(fragment`
|
|
1690
|
-
/// Creates a [${fragmentFromString(typeName)}] instruction
|
|
1833
|
+
/// Creates a [${fragmentFromString(typeName)}] instruction.${fragmentFromString(signerParamDocs ? `
|
|
1834
|
+
${signerParamDocs}` : "")}
|
|
1691
1835
|
Instruction ${fragmentFromString(instrFnName)}({
|
|
1692
1836
|
required Address ${fragmentFromString(instrProgramParam)},
|
|
1693
1837
|
${fragmentFromString(accountParams)}
|
|
1694
|
-
${fragmentFromString(argParams)}
|
|
1838
|
+
${fragmentFromString(argParams)}${fragmentFromString(signerParamDeclarations ? `
|
|
1839
|
+
${signerParamDeclarations}` : "")}
|
|
1695
1840
|
}) {
|
|
1696
|
-
final
|
|
1841
|
+
final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
|
|
1697
1842
|
${fragmentFromString(dataConstruction)}
|
|
1698
1843
|
);
|
|
1699
1844
|
|
|
@@ -1702,7 +1847,7 @@ ${fragmentFromString(dataConstruction)}
|
|
|
1702
1847
|
accounts: [
|
|
1703
1848
|
${fragmentFromString(accountMetas)}
|
|
1704
1849
|
],
|
|
1705
|
-
data: ${fragmentFromString(dataEncoderName)}().encode(
|
|
1850
|
+
data: ${fragmentFromString(dataEncoderName)}().encode(${instructionDataLocal}),
|
|
1706
1851
|
);
|
|
1707
1852
|
}`);
|
|
1708
1853
|
parts.push(fragment`
|
|
@@ -1721,13 +1866,20 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
|
|
|
1721
1866
|
}
|
|
1722
1867
|
return result;
|
|
1723
1868
|
}
|
|
1724
|
-
function
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
return
|
|
1869
|
+
function reserveName(preferredName, usedNames) {
|
|
1870
|
+
let name = preferredName;
|
|
1871
|
+
while (usedNames.has(name)) {
|
|
1872
|
+
name = `${name}_`;
|
|
1873
|
+
}
|
|
1874
|
+
usedNames.add(name);
|
|
1875
|
+
return name;
|
|
1876
|
+
}
|
|
1877
|
+
function getAccountRole(acc, signerParameter) {
|
|
1878
|
+
const role = acc.isWritable ? "AccountRole.writable" : "AccountRole.readonly";
|
|
1879
|
+
if (signerParameter) {
|
|
1880
|
+
return `${signerParameter} ? ${role}Signer : ${role}`;
|
|
1881
|
+
}
|
|
1882
|
+
return acc.isSigner === true ? `${role}Signer` : role;
|
|
1731
1883
|
}
|
|
1732
1884
|
function isDiscriminatorArg(arg, node) {
|
|
1733
1885
|
const discriminators = node.discriminators ?? [];
|
|
@@ -1780,9 +1932,9 @@ function getPdaPageFragment(node, scope) {
|
|
|
1780
1932
|
for (const seed of seeds) {
|
|
1781
1933
|
if (seed.kind === "constantPdaSeedNode") {
|
|
1782
1934
|
if (seed.value.kind === "bytesValueNode") {
|
|
1783
|
-
seedValues.push(`
|
|
1935
|
+
seedValues.push(` ${getDartValueFragment(seed.value).content},`);
|
|
1784
1936
|
} else if (seed.value.kind === "stringValueNode") {
|
|
1785
|
-
seedValues.push(`
|
|
1937
|
+
seedValues.push(` ${toDartStringLiteral(seed.value.string)},`);
|
|
1786
1938
|
} else if (seed.value.kind === "publicKeyValueNode") {
|
|
1787
1939
|
const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
|
|
1788
1940
|
if (wellKnownSeedName) {
|
|
@@ -1792,13 +1944,14 @@ function getPdaPageFragment(node, scope) {
|
|
|
1792
1944
|
);
|
|
1793
1945
|
} else {
|
|
1794
1946
|
seedValues.push(
|
|
1795
|
-
` getAddressEncoder().encode(Address(
|
|
1947
|
+
` getAddressEncoder().encode(Address(${toDartStringLiteral(seed.value.publicKey)})),`
|
|
1796
1948
|
);
|
|
1797
1949
|
}
|
|
1798
1950
|
} else if (seed.value.kind === "numberValueNode") {
|
|
1799
1951
|
const manifest = visit(seed.type, scope.typeManifestVisitor);
|
|
1800
1952
|
seedManifests.push(manifest);
|
|
1801
|
-
|
|
1953
|
+
const value = getDartValueFragment(seed.value, manifest.type.content);
|
|
1954
|
+
seedValues.push(` ${manifest.encoder.content}.encode(${value.content}),`);
|
|
1802
1955
|
}
|
|
1803
1956
|
} else if (seed.kind === "variablePdaSeedNode") {
|
|
1804
1957
|
const manifest = visit(seed.type, scope.typeManifestVisitor);
|
|
@@ -1812,7 +1965,7 @@ function getPdaPageFragment(node, scope) {
|
|
|
1812
1965
|
if (wellKnownProgramName) {
|
|
1813
1966
|
use(wellKnownProgramName, "solanaAddresses");
|
|
1814
1967
|
}
|
|
1815
|
-
const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(
|
|
1968
|
+
const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(${toDartStringLiteral(node.programId)})` : "required Address programAddress";
|
|
1816
1969
|
const typedDataImport = hasByteSeeds ? use("Uint8List", "dartTypedData") : fragmentFromString("");
|
|
1817
1970
|
const metaImport = hasSeedsClass ? use("immutable", "meta") : fragmentFromString("");
|
|
1818
1971
|
const parts = [
|
|
@@ -1859,14 +2012,6 @@ ${fragmentFromString(seedValues.join("\n"))}
|
|
|
1859
2012
|
}
|
|
1860
2013
|
return result;
|
|
1861
2014
|
}
|
|
1862
|
-
function bytesValueToDart(hex) {
|
|
1863
|
-
const clean = hex.replace(/^0x/, "");
|
|
1864
|
-
const pairs = [];
|
|
1865
|
-
for (let i = 0; i < clean.length; i += 2) {
|
|
1866
|
-
pairs.push(`0x${clean.slice(i, i + 2)}`);
|
|
1867
|
-
}
|
|
1868
|
-
return `[${pairs.join(", ")}]`;
|
|
1869
|
-
}
|
|
1870
2015
|
function getProgramPageFragment(node, scope) {
|
|
1871
2016
|
const name = node.name;
|
|
1872
2017
|
const addressConstName = scope.nameApi.programAddressConstant(name);
|
|
@@ -1887,7 +2032,7 @@ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownNa
|
|
|
1887
2032
|
${use("Address", "solanaAddresses")}
|
|
1888
2033
|
|
|
1889
2034
|
/// The address of the ${fragmentFromString(pascalCase(name))} program.
|
|
1890
|
-
const ${fragmentFromString(addressConstName)} = Address(
|
|
2035
|
+
const ${fragmentFromString(addressConstName)} = Address(${fragmentFromString(toDartStringLiteral(node.publicKey))});`;
|
|
1891
2036
|
const parts = [addressDeclaration];
|
|
1892
2037
|
const accounts = node.accounts ?? [];
|
|
1893
2038
|
if (accounts.length > 0) {
|
|
@@ -2028,7 +2173,7 @@ function getValueFragment(value, dartType) {
|
|
|
2028
2173
|
case "booleanValueNode":
|
|
2029
2174
|
return fragmentFromString(String(value.boolean));
|
|
2030
2175
|
case "stringValueNode":
|
|
2031
|
-
return fragmentFromString(
|
|
2176
|
+
return fragmentFromString(toDartStringLiteral(value.string));
|
|
2032
2177
|
case "bytesValueNode": {
|
|
2033
2178
|
const clean = value.data.replace(/^0x/, "");
|
|
2034
2179
|
const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
|
|
@@ -2126,6 +2271,7 @@ function getDataEnumPageFragment(node, variants, scope) {
|
|
|
2126
2271
|
const decoderVariants = [];
|
|
2127
2272
|
const encodeCases = [];
|
|
2128
2273
|
const decodeCases = [];
|
|
2274
|
+
let pageHasListEnums = false;
|
|
2129
2275
|
const allVariantManifests = [];
|
|
2130
2276
|
for (let i = 0; i < variants.length; i++) {
|
|
2131
2277
|
const variant = variants[i];
|
|
@@ -2173,10 +2319,14 @@ function getDataEnumPageFragment(node, variants, scope) {
|
|
|
2173
2319
|
const ctorSignature = fields.length === 0 ? ` const ${variantClassName}();` : ` const ${variantClassName}({
|
|
2174
2320
|
${ctorParams}
|
|
2175
2321
|
});`;
|
|
2176
|
-
const eqChecks = fields.length === 0 ? "true" : fields.map((f) =>
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
);
|
|
2322
|
+
const eqChecks = fields.length === 0 ? "true" : fields.map((f, index) => {
|
|
2323
|
+
const fieldName = camelCase(f.name);
|
|
2324
|
+
return isListManifest(fieldManifests[index].manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
|
|
2325
|
+
}).join(" &&\n ");
|
|
2326
|
+
const hashExpression = getValueHashExpression(fields, fieldManifests);
|
|
2327
|
+
if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
|
|
2328
|
+
pageHasListEnums = true;
|
|
2329
|
+
}
|
|
2180
2330
|
const toStringFields = fields.map((f) => `${camelCase(f.name)}: $${camelCase(f.name)}`).join(", ");
|
|
2181
2331
|
variantClasses.push(`final class ${variantClassName} extends ${typeName} {
|
|
2182
2332
|
${ctorSignature}
|
|
@@ -2282,7 +2432,7 @@ sealed class ${fragmentFromString(typeName)} {
|
|
|
2282
2432
|
const ${fragmentFromString(typeName)}();
|
|
2283
2433
|
}
|
|
2284
2434
|
|
|
2285
|
-
${fragmentFromString(variantClasses.join("\n\n"))}
|
|
2435
|
+
${fragmentFromString(variantClasses.join("\n\n"))}${pageHasListEnums ? LIST_VALUE_HELPERS : ""}
|
|
2286
2436
|
|
|
2287
2437
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
2288
2438
|
return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
|
|
@@ -2325,6 +2475,7 @@ function getStructPageFragment(node, scope) {
|
|
|
2325
2475
|
const structNode = node.type;
|
|
2326
2476
|
if (structNode.kind !== "structTypeNode") return emptyFragment();
|
|
2327
2477
|
const fields = structNode.fields ?? [];
|
|
2478
|
+
let pageHasListStructs = false;
|
|
2328
2479
|
const fieldManifests = fields.map((f) => ({
|
|
2329
2480
|
field: f,
|
|
2330
2481
|
manifest: visit(f.type, scope.typeManifestVisitor)
|
|
@@ -2336,10 +2487,13 @@ function getStructPageFragment(node, scope) {
|
|
|
2336
2487
|
const ctorSignature = fields.length === 0 ? ` const ${typeName}();` : ` const ${typeName}({
|
|
2337
2488
|
${ctorParams}
|
|
2338
2489
|
});`;
|
|
2339
|
-
const eqChecks = fields.length === 0 ? "true" :
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2490
|
+
const eqChecks = fields.length === 0 ? "true" : fieldManifests.map(({ field: f, manifest }, index) => {
|
|
2491
|
+
const fieldName = camelCase(f.name);
|
|
2492
|
+
return isListManifest(manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
|
|
2493
|
+
}).join(" &&\n ");
|
|
2494
|
+
if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
|
|
2495
|
+
pageHasListStructs = true;
|
|
2496
|
+
}
|
|
2343
2497
|
const toStringFields = fields.map(
|
|
2344
2498
|
(f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
|
|
2345
2499
|
).join(", ");
|
|
@@ -2389,12 +2543,14 @@ ${fragmentFromString(fieldDecls)}
|
|
|
2389
2543
|
${fragmentFromString(eqChecks)};
|
|
2390
2544
|
|
|
2391
2545
|
@override
|
|
2392
|
-
int get hashCode => ${fragmentFromString(
|
|
2546
|
+
int get hashCode => ${fragmentFromString(
|
|
2547
|
+
getValueHashExpression(fields, fieldManifests)
|
|
2548
|
+
)};
|
|
2393
2549
|
|
|
2394
2550
|
@override
|
|
2395
2551
|
String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
|
|
2396
2552
|
}
|
|
2397
|
-
|
|
2553
|
+
${pageHasListStructs ? LIST_VALUE_HELPERS : ""}
|
|
2398
2554
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
2399
2555
|
final structEncoder = getStructEncoder(<(String, Encoder<Object?>)>[
|
|
2400
2556
|
${fragmentFromString(encFields)}
|
|
@@ -2460,6 +2616,56 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
|
|
|
2460
2616
|
return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
|
|
2461
2617
|
}`;
|
|
2462
2618
|
}
|
|
2619
|
+
function isListManifest(manifest) {
|
|
2620
|
+
return manifest.type.content.startsWith("List<") || manifest.type.content === "Uint8List";
|
|
2621
|
+
}
|
|
2622
|
+
var LIST_VALUE_HELPERS = `
|
|
2623
|
+
bool _listEquals<T>(List<T>? a, List<T>? b) {
|
|
2624
|
+
if (identical(a, b)) return true;
|
|
2625
|
+
if (a == null || b == null) return a == b;
|
|
2626
|
+
if (a.length != b.length) return false;
|
|
2627
|
+
for (var i = 0; i < a.length; i++) {
|
|
2628
|
+
final left = a[i];
|
|
2629
|
+
final right = b[i];
|
|
2630
|
+
if (left is List<Object?> && right is List<Object?>) {
|
|
2631
|
+
if (!_listEquals(left, right)) return false;
|
|
2632
|
+
} else if (left != right) {
|
|
2633
|
+
return false;
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return true;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
Object? _deepHash(Object? value) {
|
|
2640
|
+
if (value is List<Object?>) {
|
|
2641
|
+
return Object.hashAll(value.map(_deepHash));
|
|
2642
|
+
}
|
|
2643
|
+
return value;
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
int _listHashCode<T>(List<T>? a) {
|
|
2647
|
+
if (a == null) return 0;
|
|
2648
|
+
return Object.hashAll(a.map(_deepHash));
|
|
2649
|
+
}
|
|
2650
|
+
`;
|
|
2651
|
+
function getValueHashExpression(fields, fieldManifests) {
|
|
2652
|
+
const fieldNames = fields.map(
|
|
2653
|
+
(f) => camelCase(f.name)
|
|
2654
|
+
);
|
|
2655
|
+
const hasListFields = fieldManifests.some(
|
|
2656
|
+
({ manifest }) => isListManifest(manifest)
|
|
2657
|
+
);
|
|
2658
|
+
if (!hasListFields) {
|
|
2659
|
+
return getHashExpression(fieldNames);
|
|
2660
|
+
}
|
|
2661
|
+
if (fieldNames.length === 1) {
|
|
2662
|
+
return `_listHashCode(${fieldNames[0]})`;
|
|
2663
|
+
}
|
|
2664
|
+
const hashArgs = fields.map(
|
|
2665
|
+
(f, index) => isListManifest(fieldManifests[index].manifest) ? `_listHashCode(${camelCase(f.name)})` : camelCase(f.name)
|
|
2666
|
+
);
|
|
2667
|
+
return `Object.hash(${hashArgs.join(", ")})`;
|
|
2668
|
+
}
|
|
2463
2669
|
function getHashExpression(fields) {
|
|
2464
2670
|
if (fields.length === 0) return "runtimeType.hashCode";
|
|
2465
2671
|
if (fields.length === 1) return `${fields[0]}.hashCode`;
|
|
@@ -2533,9 +2739,12 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2533
2739
|
visit(node.program, self),
|
|
2534
2740
|
...(node.additionalPrograms ?? []).map((p) => visit(p, self))
|
|
2535
2741
|
];
|
|
2536
|
-
const mergedProgramMap =
|
|
2742
|
+
const mergedProgramMap = mergeRenderMapsWithoutCollisions(programMaps);
|
|
2537
2743
|
const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
|
|
2538
|
-
return
|
|
2744
|
+
return mergeRenderMapsWithoutCollisions([
|
|
2745
|
+
mergedProgramMap,
|
|
2746
|
+
...categoryMaps
|
|
2747
|
+
]);
|
|
2539
2748
|
},
|
|
2540
2749
|
visitProgram(node, { self }) {
|
|
2541
2750
|
const programName = node.name;
|
|
@@ -2597,7 +2806,7 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2597
2806
|
);
|
|
2598
2807
|
maps.push(rootMap);
|
|
2599
2808
|
}
|
|
2600
|
-
return
|
|
2809
|
+
return mergeRenderMapsWithoutCollisions(maps);
|
|
2601
2810
|
},
|
|
2602
2811
|
visitAccount(node, { self }) {
|
|
2603
2812
|
const fileName = `${snakeCase(node.name)}.dart`;
|
|
@@ -2632,6 +2841,18 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2632
2841
|
(v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
|
|
2633
2842
|
);
|
|
2634
2843
|
}
|
|
2844
|
+
function mergeRenderMapsWithoutCollisions(maps) {
|
|
2845
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2846
|
+
for (const map of maps) {
|
|
2847
|
+
for (const path of map.keys()) {
|
|
2848
|
+
if (paths.has(path)) {
|
|
2849
|
+
throw new Error(`Duplicate generated path "${path}"`);
|
|
2850
|
+
}
|
|
2851
|
+
paths.add(path);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
return mergeRenderMaps(maps);
|
|
2855
|
+
}
|
|
2635
2856
|
function getSharedCategoryIndexMaps(renderMap) {
|
|
2636
2857
|
const categories = /* @__PURE__ */ new Map();
|
|
2637
2858
|
const categoryIndexNames = {
|
|
@@ -2648,7 +2869,6 @@ function getSharedCategoryIndexMaps(renderMap) {
|
|
|
2648
2869
|
);
|
|
2649
2870
|
if (!match) continue;
|
|
2650
2871
|
const [, category, fileName] = match;
|
|
2651
|
-
if (fileName === categoryIndexNames[category]) continue;
|
|
2652
2872
|
if (!categories.has(category)) {
|
|
2653
2873
|
categories.set(category, /* @__PURE__ */ new Set());
|
|
2654
2874
|
}
|
|
@@ -2678,9 +2898,6 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2678
2898
|
} = options;
|
|
2679
2899
|
return rootNodeVisitor((root) => {
|
|
2680
2900
|
const normalizedRoot = normalizeRootNode(root);
|
|
2681
|
-
if (deleteFolderBeforeRendering && existsSync(outputDir)) {
|
|
2682
|
-
deleteDirectory(outputDir);
|
|
2683
|
-
}
|
|
2684
2901
|
const renderMap = visit(
|
|
2685
2902
|
normalizedRoot,
|
|
2686
2903
|
getRenderMapVisitor({ nameApi, dependencyMap })
|
|
@@ -2692,12 +2909,9 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2692
2909
|
typePathMap[`definedType:${match[1]}`] = renderPath;
|
|
2693
2910
|
}
|
|
2694
2911
|
}
|
|
2912
|
+
const files = [];
|
|
2695
2913
|
for (const [filePath, frag] of renderMap.entries()) {
|
|
2696
2914
|
const fullPath = join(outputDir, filePath);
|
|
2697
|
-
const dir = dirname(fullPath);
|
|
2698
|
-
if (!existsSync(dir)) {
|
|
2699
|
-
mkdirSync(dir, { recursive: true });
|
|
2700
|
-
}
|
|
2701
2915
|
const fileDir = dirname(filePath);
|
|
2702
2916
|
const internalMap = {};
|
|
2703
2917
|
for (const [key, typePath] of Object.entries(typePathMap)) {
|
|
@@ -2712,6 +2926,13 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2712
2926
|
internalMap[key] = rel;
|
|
2713
2927
|
}
|
|
2714
2928
|
const content = resolveFragmentContent(frag, dependencyMap ?? {}, internalMap);
|
|
2929
|
+
files.push([fullPath, content]);
|
|
2930
|
+
}
|
|
2931
|
+
if (deleteFolderBeforeRendering && existsSync(outputDir)) {
|
|
2932
|
+
deleteDirectory(outputDir);
|
|
2933
|
+
}
|
|
2934
|
+
for (const [fullPath, content] of files) {
|
|
2935
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
2715
2936
|
writeFileSync(fullPath, content, "utf-8");
|
|
2716
2937
|
}
|
|
2717
2938
|
if (formatCode) {
|