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.cjs
CHANGED
|
@@ -12,7 +12,7 @@ var errors = require('@codama/errors');
|
|
|
12
12
|
|
|
13
13
|
// src/visitors/renderVisitor.ts
|
|
14
14
|
function formatDartDirectory(dir) {
|
|
15
|
-
child_process.
|
|
15
|
+
child_process.execFileSync("dart", ["format", "--", dir], {
|
|
16
16
|
encoding: "utf-8",
|
|
17
17
|
timeout: 6e4,
|
|
18
18
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -82,13 +82,89 @@ function setEmptyCollection(record, key) {
|
|
|
82
82
|
|
|
83
83
|
// src/utils/nameTransformers.ts
|
|
84
84
|
function pascalCase(str) {
|
|
85
|
+
assertSafeName(str);
|
|
85
86
|
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
|
|
86
87
|
}
|
|
88
|
+
var DART_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
89
|
+
"abstract",
|
|
90
|
+
"as",
|
|
91
|
+
"assert",
|
|
92
|
+
"async",
|
|
93
|
+
"augment",
|
|
94
|
+
"await",
|
|
95
|
+
"base",
|
|
96
|
+
"break",
|
|
97
|
+
"case",
|
|
98
|
+
"catch",
|
|
99
|
+
"class",
|
|
100
|
+
"const",
|
|
101
|
+
"continue",
|
|
102
|
+
"covariant",
|
|
103
|
+
"default",
|
|
104
|
+
"deferred",
|
|
105
|
+
"do",
|
|
106
|
+
"dynamic",
|
|
107
|
+
"else",
|
|
108
|
+
"enum",
|
|
109
|
+
"export",
|
|
110
|
+
"extends",
|
|
111
|
+
"extension",
|
|
112
|
+
"external",
|
|
113
|
+
"factory",
|
|
114
|
+
"false",
|
|
115
|
+
"final",
|
|
116
|
+
"finally",
|
|
117
|
+
"for",
|
|
118
|
+
"get",
|
|
119
|
+
"hide",
|
|
120
|
+
"if",
|
|
121
|
+
"implements",
|
|
122
|
+
"import",
|
|
123
|
+
"in",
|
|
124
|
+
"interface",
|
|
125
|
+
"is",
|
|
126
|
+
"late",
|
|
127
|
+
"library",
|
|
128
|
+
"mixin",
|
|
129
|
+
"native",
|
|
130
|
+
"new",
|
|
131
|
+
"null",
|
|
132
|
+
"of",
|
|
133
|
+
"on",
|
|
134
|
+
"operator",
|
|
135
|
+
"part",
|
|
136
|
+
"required",
|
|
137
|
+
"rethrow",
|
|
138
|
+
"return",
|
|
139
|
+
"sealed",
|
|
140
|
+
"set",
|
|
141
|
+
"show",
|
|
142
|
+
"static",
|
|
143
|
+
"super",
|
|
144
|
+
"switch",
|
|
145
|
+
"sync",
|
|
146
|
+
"this",
|
|
147
|
+
"throw",
|
|
148
|
+
"true",
|
|
149
|
+
"try",
|
|
150
|
+
"typedef",
|
|
151
|
+
"var",
|
|
152
|
+
"void",
|
|
153
|
+
"when",
|
|
154
|
+
"while",
|
|
155
|
+
"with",
|
|
156
|
+
"yield"
|
|
157
|
+
]);
|
|
158
|
+
function escapeDartIdentifier(name) {
|
|
159
|
+
return DART_RESERVED_WORDS.has(name) ? `${name}_` : name;
|
|
160
|
+
}
|
|
87
161
|
function camelCase(str) {
|
|
88
162
|
const pascal = pascalCase(str);
|
|
89
|
-
|
|
163
|
+
const cased = pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
164
|
+
return escapeDartIdentifier(cased);
|
|
90
165
|
}
|
|
91
166
|
function snakeCase(str) {
|
|
167
|
+
assertSafeName(str);
|
|
92
168
|
return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").replace(/^_/, "").replace(/_+/g, "_").toLowerCase();
|
|
93
169
|
}
|
|
94
170
|
function createDartNameApi() {
|
|
@@ -114,6 +190,11 @@ function createDartNameApi() {
|
|
|
114
190
|
discriminatorConstant: (name) => `${camelCase(name)}Discriminator`
|
|
115
191
|
};
|
|
116
192
|
}
|
|
193
|
+
function assertSafeName(name) {
|
|
194
|
+
if (!/^[a-zA-Z0-9_ -]*$/.test(name)) {
|
|
195
|
+
throw new Error(`Invalid Dart node name: ${JSON.stringify(name)}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
117
198
|
|
|
118
199
|
// src/utils/codecs.ts
|
|
119
200
|
function bytesToDartHexList(bytes) {
|
|
@@ -324,10 +405,19 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
|
|
|
324
405
|
function getDartValueFragment(value, dartType) {
|
|
325
406
|
switch (value.kind) {
|
|
326
407
|
case "numberValueNode":
|
|
408
|
+
if (typeof value.number !== "number" || !Number.isFinite(value.number)) {
|
|
409
|
+
throw new Error("Invalid Dart number: expected a finite number");
|
|
410
|
+
}
|
|
411
|
+
if (dartType === "BigInt" && !Number.isSafeInteger(value.number)) {
|
|
412
|
+
throw new Error("Invalid Dart BigInt: expected a safe integer");
|
|
413
|
+
}
|
|
327
414
|
return fragmentFromString(
|
|
328
415
|
dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
|
|
329
416
|
);
|
|
330
417
|
case "booleanValueNode":
|
|
418
|
+
if (typeof value.boolean !== "boolean") {
|
|
419
|
+
throw new Error("Invalid Dart boolean: expected a boolean");
|
|
420
|
+
}
|
|
331
421
|
return fragmentFromString(String(value.boolean));
|
|
332
422
|
case "stringValueNode":
|
|
333
423
|
return fragmentFromString(toDartStringLiteral(value.string));
|
|
@@ -371,6 +461,12 @@ function isConstDartValueNode(value) {
|
|
|
371
461
|
function toDartStringLiteral(value) {
|
|
372
462
|
return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
|
|
373
463
|
}
|
|
464
|
+
function getNonNegativeInteger(value) {
|
|
465
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
466
|
+
throw new Error("Expected a non-negative safe integer value");
|
|
467
|
+
}
|
|
468
|
+
return String(value);
|
|
469
|
+
}
|
|
374
470
|
|
|
375
471
|
// src/utils/discriminators.ts
|
|
376
472
|
function getDiscriminatorValidationFragment(node, scope) {
|
|
@@ -571,23 +667,12 @@ function getTypeManifestVisitor(input) {
|
|
|
571
667
|
}
|
|
572
668
|
);
|
|
573
669
|
return visitorsCore.extendVisitor(baseVisitor, {
|
|
574
|
-
visitNumberType(node
|
|
575
|
-
const { format, endian } = node;
|
|
576
|
-
const endianSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
|
|
577
|
-
const endianDecoderSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
|
|
578
|
-
const dartType = getNumberDartType(format);
|
|
579
|
-
const codecName = getNumberCodecName(format);
|
|
670
|
+
visitNumberType(node) {
|
|
580
671
|
return {
|
|
581
672
|
// `BigInt` and other number types live in `dart:core` and need no import.
|
|
582
|
-
type: fragmentFromString(
|
|
583
|
-
encoder:
|
|
584
|
-
|
|
585
|
-
"solanaCodecsNumbers"
|
|
586
|
-
)}(${endianSuffix ? fragmentFromString(endianSuffix.slice(2)) : fragmentFromString("")})`,
|
|
587
|
-
decoder: fragment`${use(
|
|
588
|
-
`get${codecName}Decoder`,
|
|
589
|
-
"solanaCodecsNumbers"
|
|
590
|
-
)}(${endianDecoderSuffix ? fragmentFromString(endianDecoderSuffix.slice(2)) : fragmentFromString("")})`,
|
|
673
|
+
type: fragmentFromString(getNumberDartType(node.format)),
|
|
674
|
+
encoder: getNumberCodecExpression(node, "Encoder"),
|
|
675
|
+
decoder: getNumberCodecExpression(node, "Decoder"),
|
|
591
676
|
value: emptyTypeManifest().value,
|
|
592
677
|
isEnum: false
|
|
593
678
|
};
|
|
@@ -674,7 +759,7 @@ function getTypeManifestVisitor(input) {
|
|
|
674
759
|
encoder: fragment`${use(
|
|
675
760
|
"getArrayEncoder",
|
|
676
761
|
"solanaCodecsDataStructures"
|
|
677
|
-
)}
|
|
762
|
+
)}(${itemEncoder}${encoderSizeExpr})`,
|
|
678
763
|
decoder: fragment`${use(
|
|
679
764
|
"getArrayDecoder",
|
|
680
765
|
"solanaCodecsDataStructures"
|
|
@@ -730,18 +815,28 @@ function getTypeManifestVisitor(input) {
|
|
|
730
815
|
typeFragment.imports.mergeWith(m.type.imports);
|
|
731
816
|
}
|
|
732
817
|
const encoderListStr = encoderList.map((e) => e.content).join(", ");
|
|
818
|
+
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
819
|
+
let tupleEncoderFn = "getTupleEncoder";
|
|
820
|
+
let tupleDecoderFn = "getTupleDecoder";
|
|
821
|
+
let tupleEncoderArgs = `[${encoderListStr}]`;
|
|
822
|
+
let tupleDecoderArgs = `[${decoderListStr}]`;
|
|
823
|
+
if (itemManifests.length === 2) {
|
|
824
|
+
tupleEncoderFn = "getTuple2Encoder";
|
|
825
|
+
tupleDecoderFn = "getTuple2Decoder";
|
|
826
|
+
tupleEncoderArgs = encoderListStr;
|
|
827
|
+
tupleDecoderArgs = decoderListStr;
|
|
828
|
+
}
|
|
733
829
|
const encoderFrag = fragment`${use(
|
|
734
|
-
|
|
830
|
+
tupleEncoderFn,
|
|
735
831
|
"solanaCodecsDataStructures"
|
|
736
|
-
)}(
|
|
832
|
+
)}(${tupleEncoderArgs})`;
|
|
737
833
|
for (const e of encoderList) {
|
|
738
834
|
encoderFrag.imports.mergeWith(e.imports);
|
|
739
835
|
}
|
|
740
|
-
const decoderListStr = decoderList.map((d) => d.content).join(", ");
|
|
741
836
|
const decoderFrag = fragment`${use(
|
|
742
|
-
|
|
837
|
+
tupleDecoderFn,
|
|
743
838
|
"solanaCodecsDataStructures"
|
|
744
|
-
)}(
|
|
839
|
+
)}(${tupleDecoderArgs})`;
|
|
745
840
|
for (const d of decoderList) {
|
|
746
841
|
decoderFrag.imports.mergeWith(d.imports);
|
|
747
842
|
}
|
|
@@ -758,7 +853,7 @@ function getTypeManifestVisitor(input) {
|
|
|
758
853
|
const fields = nodeFields.map((f) => visitorsCore.visit(f, self));
|
|
759
854
|
const fieldNames = nodeFields.map((f) => f.name);
|
|
760
855
|
const typeStr = "Map<String, Object?>";
|
|
761
|
-
const encoderFields = fields.map((f, i) => `(
|
|
856
|
+
const encoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.encoder.content})`).join(", ");
|
|
762
857
|
const encoderFrag = fragment`${use(
|
|
763
858
|
"getStructEncoder",
|
|
764
859
|
"solanaCodecsDataStructures"
|
|
@@ -766,7 +861,7 @@ function getTypeManifestVisitor(input) {
|
|
|
766
861
|
for (const f of fields) {
|
|
767
862
|
encoderFrag.imports.mergeWith(f.encoder.imports);
|
|
768
863
|
}
|
|
769
|
-
const decoderFields = fields.map((f, i) => `(
|
|
864
|
+
const decoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.decoder.content})`).join(", ");
|
|
770
865
|
const decoderFrag = fragment`${use(
|
|
771
866
|
"getStructDecoder",
|
|
772
867
|
"solanaCodecsDataStructures"
|
|
@@ -820,19 +915,22 @@ function getTypeManifestVisitor(input) {
|
|
|
820
915
|
const itemManifest = visitorsCore.visit(node.item, self);
|
|
821
916
|
const itemTypeStr = itemManifest.type.content;
|
|
822
917
|
const resolvedPrefix = nodes.resolveNestedTypeNode(node.prefix);
|
|
823
|
-
const prefixExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix
|
|
824
|
-
const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix
|
|
918
|
+
const prefixExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Encoder")}` : "";
|
|
919
|
+
const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Decoder")}` : "";
|
|
825
920
|
const noneValueExpr = node.fixed ? ", noneValue: const ZeroesNoneValue()" : "";
|
|
826
921
|
return {
|
|
827
922
|
type: fragment`${itemManifest.type}?`,
|
|
828
923
|
encoder: fragment`${use(
|
|
829
924
|
"getNullableEncoder",
|
|
830
925
|
"solanaCodecsDataStructures"
|
|
831
|
-
)}<${fragmentFromString(itemTypeStr)}>(${
|
|
926
|
+
)}<${fragmentFromString(itemTypeStr)}>(${use(
|
|
927
|
+
"transformEncoder",
|
|
928
|
+
"solanaCodecsCore"
|
|
929
|
+
)}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${prefixExpr}${fragmentFromString(noneValueExpr)})`,
|
|
832
930
|
decoder: fragment`${use(
|
|
833
931
|
"getNullableDecoder",
|
|
834
932
|
"solanaCodecsDataStructures"
|
|
835
|
-
)}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${
|
|
933
|
+
)}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${prefixDecoderExpr}${fragmentFromString(noneValueExpr)})`,
|
|
836
934
|
value: emptyTypeManifest().value,
|
|
837
935
|
isEnum: false
|
|
838
936
|
};
|
|
@@ -878,11 +976,11 @@ function getTypeManifestVisitor(input) {
|
|
|
878
976
|
encoder: fragment`${use(
|
|
879
977
|
"fixEncoderSize",
|
|
880
978
|
"solanaCodecsCore"
|
|
881
|
-
)}(${innerManifest.encoder}, ${fragmentFromString(
|
|
979
|
+
)}(${innerManifest.encoder}, ${fragmentFromString(getNonNegativeInteger(node.size))}, allowTruncation: false)`,
|
|
882
980
|
decoder: fragment`${use(
|
|
883
981
|
"fixDecoderSize",
|
|
884
982
|
"solanaCodecsCore"
|
|
885
|
-
)}(${innerManifest.decoder}, ${fragmentFromString(
|
|
983
|
+
)}(${innerManifest.decoder}, ${fragmentFromString(getNonNegativeInteger(node.size))})`,
|
|
886
984
|
value: innerManifest.value,
|
|
887
985
|
isEnum: innerManifest.isEnum
|
|
888
986
|
};
|
|
@@ -892,10 +990,10 @@ function getTypeManifestVisitor(input) {
|
|
|
892
990
|
const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
|
|
893
991
|
let prefixManifest;
|
|
894
992
|
if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
|
|
895
|
-
const
|
|
993
|
+
const numberManifest = visitorsCore.visit(node.prefix, self);
|
|
896
994
|
prefixManifest = {
|
|
897
|
-
encoder: fragment
|
|
898
|
-
decoder: fragment
|
|
995
|
+
encoder: fragment`${use("transformEncoder", "solanaCodecsCore")}(${numberManifest.encoder}, (size) => BigInt.from(size))`,
|
|
996
|
+
decoder: fragment`${use("transformDecoder", "solanaCodecsCore")}(${numberManifest.decoder}, (size, _, __) => size.toInt())`
|
|
899
997
|
};
|
|
900
998
|
} else {
|
|
901
999
|
prefixManifest = visitorsCore.visit(node.prefix, self);
|
|
@@ -986,10 +1084,10 @@ function getTypeManifestVisitor(input) {
|
|
|
986
1084
|
};
|
|
987
1085
|
},
|
|
988
1086
|
visitPreOffsetType(node, { self }) {
|
|
989
|
-
return
|
|
1087
|
+
return getOffsetManifest(node, self);
|
|
990
1088
|
},
|
|
991
1089
|
visitPostOffsetType(node, { self }) {
|
|
992
|
-
return
|
|
1090
|
+
return getOffsetManifest(node, self);
|
|
993
1091
|
},
|
|
994
1092
|
visitSolAmountType(node, { self }) {
|
|
995
1093
|
return visitorsCore.visit(node.number, self);
|
|
@@ -1078,16 +1176,17 @@ function getNumberCodecName(format) {
|
|
|
1078
1176
|
return "U8";
|
|
1079
1177
|
}
|
|
1080
1178
|
}
|
|
1081
|
-
function getNumberCodecExpression(
|
|
1082
|
-
const name = getNumberCodecName(format);
|
|
1083
|
-
|
|
1179
|
+
function getNumberCodecExpression(node, codecType) {
|
|
1180
|
+
const name = getNumberCodecName(node.format);
|
|
1181
|
+
const config = node.endian === "be" ? fragment`${use("NumberCodecConfig", "solanaCodecsNumbers")}(endian: ${use("Endian", "dartTypedData")}.big)` : "";
|
|
1182
|
+
return fragment`${use(`get${name}${codecType}`, "solanaCodecsNumbers")}(${config})`;
|
|
1084
1183
|
}
|
|
1085
1184
|
function getArraySizeExpression(node, codecType) {
|
|
1086
1185
|
if (!("count" in node) || !node.count) return "";
|
|
1087
1186
|
const count = node.count;
|
|
1088
1187
|
switch (count.kind) {
|
|
1089
1188
|
case "fixedCountNode":
|
|
1090
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1189
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1091
1190
|
case "remainderCountNode":
|
|
1092
1191
|
return ", size: RemainderArraySize()";
|
|
1093
1192
|
case "prefixedCountNode": {
|
|
@@ -1095,7 +1194,7 @@ function getArraySizeExpression(node, codecType) {
|
|
|
1095
1194
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1096
1195
|
return "";
|
|
1097
1196
|
}
|
|
1098
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1197
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1099
1198
|
}
|
|
1100
1199
|
default:
|
|
1101
1200
|
return "";
|
|
@@ -1106,7 +1205,7 @@ function getMapSizeExpression(node, codecType) {
|
|
|
1106
1205
|
const count = node.count;
|
|
1107
1206
|
switch (count.kind) {
|
|
1108
1207
|
case "fixedCountNode":
|
|
1109
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1208
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1110
1209
|
case "remainderCountNode":
|
|
1111
1210
|
return ", size: RemainderArraySize()";
|
|
1112
1211
|
case "prefixedCountNode": {
|
|
@@ -1114,7 +1213,7 @@ function getMapSizeExpression(node, codecType) {
|
|
|
1114
1213
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1115
1214
|
return "";
|
|
1116
1215
|
}
|
|
1117
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1216
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1118
1217
|
}
|
|
1119
1218
|
default:
|
|
1120
1219
|
return "";
|
|
@@ -1125,7 +1224,7 @@ function getSetSizeExpression(node, codecType) {
|
|
|
1125
1224
|
const count = node.count;
|
|
1126
1225
|
switch (count.kind) {
|
|
1127
1226
|
case "fixedCountNode":
|
|
1128
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1227
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1129
1228
|
case "remainderCountNode":
|
|
1130
1229
|
return ", size: RemainderArraySize()";
|
|
1131
1230
|
case "prefixedCountNode": {
|
|
@@ -1133,7 +1232,7 @@ function getSetSizeExpression(node, codecType) {
|
|
|
1133
1232
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1134
1233
|
return "";
|
|
1135
1234
|
}
|
|
1136
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1235
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1137
1236
|
}
|
|
1138
1237
|
default:
|
|
1139
1238
|
return "";
|
|
@@ -1144,36 +1243,61 @@ function getHiddenAffixManifest(node, self) {
|
|
|
1144
1243
|
const manifest = visitorsCore.visit(node, self);
|
|
1145
1244
|
return { encoder: manifest.encoder, decoder: manifest.decoder };
|
|
1146
1245
|
}
|
|
1147
|
-
const constantBytes = getConstantBytesExpression(node);
|
|
1246
|
+
const constantBytes = getConstantBytesExpression(node, self);
|
|
1148
1247
|
return {
|
|
1149
1248
|
encoder: fragment`${use("getConstantEncoder", "solanaCodecsDataStructures")}(${constantBytes})`,
|
|
1150
1249
|
decoder: fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(${constantBytes})`
|
|
1151
1250
|
};
|
|
1152
1251
|
}
|
|
1153
|
-
function getConstantBytesExpression(node) {
|
|
1154
|
-
|
|
1155
|
-
const typeNode = node.type;
|
|
1156
|
-
if (valueNode?.kind !== "numberValueNode") {
|
|
1252
|
+
function getConstantBytesExpression(node, self) {
|
|
1253
|
+
if (node.value.kind !== "numberValueNode") {
|
|
1157
1254
|
throw new Error(
|
|
1158
|
-
`Unsupported hidden affix constant value kind: ${
|
|
1255
|
+
`Unsupported hidden affix constant value kind: ${node.value.kind}`
|
|
1159
1256
|
);
|
|
1160
1257
|
}
|
|
1161
|
-
const
|
|
1162
|
-
if (
|
|
1163
|
-
|
|
1258
|
+
const manifest = visitorsCore.visit(node.type, self);
|
|
1259
|
+
if (!["int", "BigInt", "double"].includes(manifest.type.content)) {
|
|
1260
|
+
throw new Error(`Unsupported hidden affix constant type kind: ${node.type.kind}`);
|
|
1164
1261
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
return fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${use(`get${getNumberCodecName(inner.format)}Encoder`, "solanaCodecsNumbers")}(), ${typeNode.offset}).encode(${value})`;
|
|
1262
|
+
const value = getDartValueFragment(node.value, manifest.type.content);
|
|
1263
|
+
return fragment`${manifest.encoder}.encode(${value})`;
|
|
1264
|
+
}
|
|
1265
|
+
function getOffsetManifest(node, self) {
|
|
1266
|
+
if (!Number.isSafeInteger(node.offset)) {
|
|
1267
|
+
throw new Error("Expected a safe integer for a codec offset");
|
|
1173
1268
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1269
|
+
const manifest = visitorsCore.visit(node.type, self);
|
|
1270
|
+
const pre = node.kind === "preOffsetTypeNode";
|
|
1271
|
+
const key = pre ? "preOffset" : "postOffset";
|
|
1272
|
+
if (node.strategy === "padded") {
|
|
1273
|
+
const side = pre ? "Left" : "Right";
|
|
1274
|
+
const offset = getNonNegativeInteger(node.offset);
|
|
1275
|
+
return {
|
|
1276
|
+
...manifest,
|
|
1277
|
+
encoder: fragment`${use(`pad${side}Encoder`, "solanaCodecsCore")}(${manifest.encoder}, ${offset})`,
|
|
1278
|
+
decoder: fragment`${use(`pad${side}Decoder`, "solanaCodecsCore")}(${manifest.decoder}, ${offset})`
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
let expression;
|
|
1282
|
+
switch (node.strategy) {
|
|
1283
|
+
case "absolute":
|
|
1284
|
+
expression = node.offset < 0 ? `scope.wrapBytes(${node.offset})` : String(node.offset);
|
|
1285
|
+
break;
|
|
1286
|
+
case "relative":
|
|
1287
|
+
expression = `scope.${key} + ${node.offset}`;
|
|
1288
|
+
break;
|
|
1289
|
+
case "preOffset":
|
|
1290
|
+
expression = `scope.preOffset + ${node.offset}`;
|
|
1291
|
+
break;
|
|
1292
|
+
default:
|
|
1293
|
+
throw new Error(`Unsupported offset strategy: ${node.strategy}`);
|
|
1294
|
+
}
|
|
1295
|
+
const config = fragment`${use("OffsetConfig", "solanaCodecsCore")}(${key}: (scope) => ${expression})`;
|
|
1296
|
+
return {
|
|
1297
|
+
...manifest,
|
|
1298
|
+
encoder: fragment`${use("offsetEncoder", "solanaCodecsCore")}(${manifest.encoder}, ${config})`,
|
|
1299
|
+
decoder: fragment`${use("offsetDecoder", "solanaCodecsCore")}(${manifest.decoder}, ${config})`
|
|
1300
|
+
};
|
|
1177
1301
|
}
|
|
1178
1302
|
|
|
1179
1303
|
// src/fragments/discriminatorConstants.ts
|
|
@@ -1306,7 +1430,7 @@ ${ctorParams}
|
|
|
1306
1430
|
const decodeFnName = scope.nameApi.accountDecodeFunction(name);
|
|
1307
1431
|
const sizeFragment = node.size != null ? fragment`
|
|
1308
1432
|
/// The size of the [${fragmentFromString(typeName)}] account data in bytes.
|
|
1309
|
-
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(
|
|
1433
|
+
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(getNonNegativeInteger(node.size))};` : emptyFragment();
|
|
1310
1434
|
const discFragment = getDiscriminatorConstantsFragment(node, scope);
|
|
1311
1435
|
const discriminatorValidation = getDiscriminatorValidationFragment(
|
|
1312
1436
|
node,
|
|
@@ -1420,9 +1544,10 @@ function getErrorPageFragment(programNode, scope) {
|
|
|
1420
1544
|
programName,
|
|
1421
1545
|
err.name
|
|
1422
1546
|
);
|
|
1547
|
+
getNonNegativeInteger(err.code);
|
|
1423
1548
|
const hexCode = `0x${err.code.toString(16)}`;
|
|
1424
|
-
const docs = err.docs?.length ? err.docs.map(
|
|
1425
|
-
const message = err.message ?
|
|
1549
|
+
const docs = err.docs?.length ? err.docs.map(toDartDocComment).join("\n") + "\n" : "";
|
|
1550
|
+
const message = err.message ? `${toDartDocComment(`Message: "${err.message}"`)}
|
|
1426
1551
|
` : "";
|
|
1427
1552
|
return `${docs}${message}const int ${constName} = ${hexCode}; // ${err.code}`;
|
|
1428
1553
|
}).join("\n\n");
|
|
@@ -1432,7 +1557,7 @@ function getErrorPageFragment(programNode, scope) {
|
|
|
1432
1557
|
err.name
|
|
1433
1558
|
);
|
|
1434
1559
|
const message = err.message ?? err.name;
|
|
1435
|
-
return ` ${constName}:
|
|
1560
|
+
return ` ${constName}: ${toDartStringLiteral(message)},`;
|
|
1436
1561
|
}).join("\n");
|
|
1437
1562
|
const errorMessageFnName = scope.nameApi.errorMessageFunction(programName);
|
|
1438
1563
|
const isErrorFnName = `is${pascalCase(programName)}Error`;
|
|
@@ -1458,8 +1583,8 @@ bool ${fragmentFromString(isErrorFnName)}(int code) {
|
|
|
1458
1583
|
return _${fragmentFromString(camelCase(programName))}ErrorMessages.containsKey(code);
|
|
1459
1584
|
}`;
|
|
1460
1585
|
}
|
|
1461
|
-
function
|
|
1462
|
-
return
|
|
1586
|
+
function toDartDocComment(value) {
|
|
1587
|
+
return value.split(/\r\n?|\n/).map((line) => `/// ${line}`).join("\n");
|
|
1463
1588
|
}
|
|
1464
1589
|
|
|
1465
1590
|
// src/fragments/indexPage.ts
|
|
@@ -1585,10 +1710,28 @@ ${dataCtorInitializers}` : ""};`;
|
|
|
1585
1710
|
const dataEncoderName = `get${typeName}InstructionDataEncoder`;
|
|
1586
1711
|
const dataDecoderName = `get${typeName}InstructionDataDecoder`;
|
|
1587
1712
|
const dataCodecName = `get${typeName}InstructionDataCodec`;
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1713
|
+
const usedNames = /* @__PURE__ */ new Set([
|
|
1714
|
+
...args.map((arg) => camelCase(arg.name)),
|
|
1715
|
+
...accounts.map((account) => camelCase(account.name))
|
|
1716
|
+
]);
|
|
1717
|
+
const instrProgramParam = reserveName(
|
|
1718
|
+
usedNames.has("programAddress") ? "instructionProgramAddress" : "programAddress",
|
|
1719
|
+
usedNames
|
|
1720
|
+
);
|
|
1721
|
+
const instructionDataLocal = reserveName("instructionData", usedNames);
|
|
1722
|
+
const signerParams = /* @__PURE__ */ new Map();
|
|
1723
|
+
for (const account of accounts) {
|
|
1724
|
+
if (account.isSigner === "either") {
|
|
1725
|
+
signerParams.set(
|
|
1726
|
+
account,
|
|
1727
|
+
reserveName(`${camelCase(account.name)}IsSigner`, usedNames)
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
const signerParamDeclarations = [...signerParams.values()].map((parameter) => ` bool ${parameter} = true,`).join("\n");
|
|
1732
|
+
const signerParamDocs = [...signerParams.entries()].map(
|
|
1733
|
+
([account, parameter]) => `/// Set [${parameter}] to false when [${camelCase(account.name)}] does not sign (for example, a multisig authority).`
|
|
1734
|
+
).join("\n");
|
|
1592
1735
|
const accountParams = accounts.map((acc) => {
|
|
1593
1736
|
const fieldName = camelCase(acc.name);
|
|
1594
1737
|
const isRequired = !(acc.isOptional ?? false);
|
|
@@ -1608,7 +1751,7 @@ ${dataCtorInitializers}` : ""};`;
|
|
|
1608
1751
|
}).join("\n");
|
|
1609
1752
|
const accountMetas = accounts.map((acc) => {
|
|
1610
1753
|
const fieldName = camelCase(acc.name);
|
|
1611
|
-
const role = getAccountRole(acc);
|
|
1754
|
+
const role = getAccountRole(acc, signerParams.get(acc));
|
|
1612
1755
|
const isOptional = acc.isOptional ?? false;
|
|
1613
1756
|
if (isOptional) {
|
|
1614
1757
|
if (optionalAccountStrategy === "omitted") {
|
|
@@ -1691,13 +1834,15 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
|
|
|
1691
1834
|
return combineCodec(${fragmentFromString(dataEncoderName)}(), ${fragmentFromString(dataDecoderName)}());
|
|
1692
1835
|
}`);
|
|
1693
1836
|
parts.push(fragment`
|
|
1694
|
-
/// Creates a [${fragmentFromString(typeName)}] instruction
|
|
1837
|
+
/// Creates a [${fragmentFromString(typeName)}] instruction.${fragmentFromString(signerParamDocs ? `
|
|
1838
|
+
${signerParamDocs}` : "")}
|
|
1695
1839
|
Instruction ${fragmentFromString(instrFnName)}({
|
|
1696
1840
|
required Address ${fragmentFromString(instrProgramParam)},
|
|
1697
1841
|
${fragmentFromString(accountParams)}
|
|
1698
|
-
${fragmentFromString(argParams)}
|
|
1842
|
+
${fragmentFromString(argParams)}${fragmentFromString(signerParamDeclarations ? `
|
|
1843
|
+
${signerParamDeclarations}` : "")}
|
|
1699
1844
|
}) {
|
|
1700
|
-
final
|
|
1845
|
+
final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
|
|
1701
1846
|
${fragmentFromString(dataConstruction)}
|
|
1702
1847
|
);
|
|
1703
1848
|
|
|
@@ -1706,7 +1851,7 @@ ${fragmentFromString(dataConstruction)}
|
|
|
1706
1851
|
accounts: [
|
|
1707
1852
|
${fragmentFromString(accountMetas)}
|
|
1708
1853
|
],
|
|
1709
|
-
data: ${fragmentFromString(dataEncoderName)}().encode(
|
|
1854
|
+
data: ${fragmentFromString(dataEncoderName)}().encode(${instructionDataLocal}),
|
|
1710
1855
|
);
|
|
1711
1856
|
}`);
|
|
1712
1857
|
parts.push(fragment`
|
|
@@ -1725,13 +1870,20 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
|
|
|
1725
1870
|
}
|
|
1726
1871
|
return result;
|
|
1727
1872
|
}
|
|
1728
|
-
function
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
return
|
|
1873
|
+
function reserveName(preferredName, usedNames) {
|
|
1874
|
+
let name = preferredName;
|
|
1875
|
+
while (usedNames.has(name)) {
|
|
1876
|
+
name = `${name}_`;
|
|
1877
|
+
}
|
|
1878
|
+
usedNames.add(name);
|
|
1879
|
+
return name;
|
|
1880
|
+
}
|
|
1881
|
+
function getAccountRole(acc, signerParameter) {
|
|
1882
|
+
const role = acc.isWritable ? "AccountRole.writable" : "AccountRole.readonly";
|
|
1883
|
+
if (signerParameter) {
|
|
1884
|
+
return `${signerParameter} ? ${role}Signer : ${role}`;
|
|
1885
|
+
}
|
|
1886
|
+
return acc.isSigner === true ? `${role}Signer` : role;
|
|
1735
1887
|
}
|
|
1736
1888
|
function isDiscriminatorArg(arg, node) {
|
|
1737
1889
|
const discriminators = node.discriminators ?? [];
|
|
@@ -1784,9 +1936,9 @@ function getPdaPageFragment(node, scope) {
|
|
|
1784
1936
|
for (const seed of seeds) {
|
|
1785
1937
|
if (seed.kind === "constantPdaSeedNode") {
|
|
1786
1938
|
if (seed.value.kind === "bytesValueNode") {
|
|
1787
|
-
seedValues.push(`
|
|
1939
|
+
seedValues.push(` ${getDartValueFragment(seed.value).content},`);
|
|
1788
1940
|
} else if (seed.value.kind === "stringValueNode") {
|
|
1789
|
-
seedValues.push(`
|
|
1941
|
+
seedValues.push(` ${toDartStringLiteral(seed.value.string)},`);
|
|
1790
1942
|
} else if (seed.value.kind === "publicKeyValueNode") {
|
|
1791
1943
|
const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
|
|
1792
1944
|
if (wellKnownSeedName) {
|
|
@@ -1796,13 +1948,14 @@ function getPdaPageFragment(node, scope) {
|
|
|
1796
1948
|
);
|
|
1797
1949
|
} else {
|
|
1798
1950
|
seedValues.push(
|
|
1799
|
-
` getAddressEncoder().encode(Address(
|
|
1951
|
+
` getAddressEncoder().encode(Address(${toDartStringLiteral(seed.value.publicKey)})),`
|
|
1800
1952
|
);
|
|
1801
1953
|
}
|
|
1802
1954
|
} else if (seed.value.kind === "numberValueNode") {
|
|
1803
1955
|
const manifest = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
|
|
1804
1956
|
seedManifests.push(manifest);
|
|
1805
|
-
|
|
1957
|
+
const value = getDartValueFragment(seed.value, manifest.type.content);
|
|
1958
|
+
seedValues.push(` ${manifest.encoder.content}.encode(${value.content}),`);
|
|
1806
1959
|
}
|
|
1807
1960
|
} else if (seed.kind === "variablePdaSeedNode") {
|
|
1808
1961
|
const manifest = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
|
|
@@ -1816,7 +1969,7 @@ function getPdaPageFragment(node, scope) {
|
|
|
1816
1969
|
if (wellKnownProgramName) {
|
|
1817
1970
|
use(wellKnownProgramName, "solanaAddresses");
|
|
1818
1971
|
}
|
|
1819
|
-
const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(
|
|
1972
|
+
const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(${toDartStringLiteral(node.programId)})` : "required Address programAddress";
|
|
1820
1973
|
const typedDataImport = hasByteSeeds ? use("Uint8List", "dartTypedData") : fragmentFromString("");
|
|
1821
1974
|
const metaImport = hasSeedsClass ? use("immutable", "meta") : fragmentFromString("");
|
|
1822
1975
|
const parts = [
|
|
@@ -1863,14 +2016,6 @@ ${fragmentFromString(seedValues.join("\n"))}
|
|
|
1863
2016
|
}
|
|
1864
2017
|
return result;
|
|
1865
2018
|
}
|
|
1866
|
-
function bytesValueToDart(hex) {
|
|
1867
|
-
const clean = hex.replace(/^0x/, "");
|
|
1868
|
-
const pairs = [];
|
|
1869
|
-
for (let i = 0; i < clean.length; i += 2) {
|
|
1870
|
-
pairs.push(`0x${clean.slice(i, i + 2)}`);
|
|
1871
|
-
}
|
|
1872
|
-
return `[${pairs.join(", ")}]`;
|
|
1873
|
-
}
|
|
1874
2019
|
function getProgramPageFragment(node, scope) {
|
|
1875
2020
|
const name = node.name;
|
|
1876
2021
|
const addressConstName = scope.nameApi.programAddressConstant(name);
|
|
@@ -1891,7 +2036,7 @@ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownNa
|
|
|
1891
2036
|
${use("Address", "solanaAddresses")}
|
|
1892
2037
|
|
|
1893
2038
|
/// The address of the ${fragmentFromString(pascalCase(name))} program.
|
|
1894
|
-
const ${fragmentFromString(addressConstName)} = Address(
|
|
2039
|
+
const ${fragmentFromString(addressConstName)} = Address(${fragmentFromString(toDartStringLiteral(node.publicKey))});`;
|
|
1895
2040
|
const parts = [addressDeclaration];
|
|
1896
2041
|
const accounts = node.accounts ?? [];
|
|
1897
2042
|
if (accounts.length > 0) {
|
|
@@ -2032,7 +2177,7 @@ function getValueFragment(value, dartType) {
|
|
|
2032
2177
|
case "booleanValueNode":
|
|
2033
2178
|
return fragmentFromString(String(value.boolean));
|
|
2034
2179
|
case "stringValueNode":
|
|
2035
|
-
return fragmentFromString(
|
|
2180
|
+
return fragmentFromString(toDartStringLiteral(value.string));
|
|
2036
2181
|
case "bytesValueNode": {
|
|
2037
2182
|
const clean = value.data.replace(/^0x/, "");
|
|
2038
2183
|
const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
|
|
@@ -2130,6 +2275,7 @@ function getDataEnumPageFragment(node, variants, scope) {
|
|
|
2130
2275
|
const decoderVariants = [];
|
|
2131
2276
|
const encodeCases = [];
|
|
2132
2277
|
const decodeCases = [];
|
|
2278
|
+
let pageHasListEnums = false;
|
|
2133
2279
|
const allVariantManifests = [];
|
|
2134
2280
|
for (let i = 0; i < variants.length; i++) {
|
|
2135
2281
|
const variant = variants[i];
|
|
@@ -2177,10 +2323,14 @@ function getDataEnumPageFragment(node, variants, scope) {
|
|
|
2177
2323
|
const ctorSignature = fields.length === 0 ? ` const ${variantClassName}();` : ` const ${variantClassName}({
|
|
2178
2324
|
${ctorParams}
|
|
2179
2325
|
});`;
|
|
2180
|
-
const eqChecks = fields.length === 0 ? "true" : fields.map((f) =>
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
);
|
|
2326
|
+
const eqChecks = fields.length === 0 ? "true" : fields.map((f, index) => {
|
|
2327
|
+
const fieldName = camelCase(f.name);
|
|
2328
|
+
return isListManifest(fieldManifests[index].manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
|
|
2329
|
+
}).join(" &&\n ");
|
|
2330
|
+
const hashExpression = getValueHashExpression(fields, fieldManifests);
|
|
2331
|
+
if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
|
|
2332
|
+
pageHasListEnums = true;
|
|
2333
|
+
}
|
|
2184
2334
|
const toStringFields = fields.map((f) => `${camelCase(f.name)}: $${camelCase(f.name)}`).join(", ");
|
|
2185
2335
|
variantClasses.push(`final class ${variantClassName} extends ${typeName} {
|
|
2186
2336
|
${ctorSignature}
|
|
@@ -2286,7 +2436,7 @@ sealed class ${fragmentFromString(typeName)} {
|
|
|
2286
2436
|
const ${fragmentFromString(typeName)}();
|
|
2287
2437
|
}
|
|
2288
2438
|
|
|
2289
|
-
${fragmentFromString(variantClasses.join("\n\n"))}
|
|
2439
|
+
${fragmentFromString(variantClasses.join("\n\n"))}${pageHasListEnums ? LIST_VALUE_HELPERS : ""}
|
|
2290
2440
|
|
|
2291
2441
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
2292
2442
|
return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
|
|
@@ -2329,6 +2479,7 @@ function getStructPageFragment(node, scope) {
|
|
|
2329
2479
|
const structNode = node.type;
|
|
2330
2480
|
if (structNode.kind !== "structTypeNode") return emptyFragment();
|
|
2331
2481
|
const fields = structNode.fields ?? [];
|
|
2482
|
+
let pageHasListStructs = false;
|
|
2332
2483
|
const fieldManifests = fields.map((f) => ({
|
|
2333
2484
|
field: f,
|
|
2334
2485
|
manifest: visitorsCore.visit(f.type, scope.typeManifestVisitor)
|
|
@@ -2340,10 +2491,13 @@ function getStructPageFragment(node, scope) {
|
|
|
2340
2491
|
const ctorSignature = fields.length === 0 ? ` const ${typeName}();` : ` const ${typeName}({
|
|
2341
2492
|
${ctorParams}
|
|
2342
2493
|
});`;
|
|
2343
|
-
const eqChecks = fields.length === 0 ? "true" :
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2494
|
+
const eqChecks = fields.length === 0 ? "true" : fieldManifests.map(({ field: f, manifest }, index) => {
|
|
2495
|
+
const fieldName = camelCase(f.name);
|
|
2496
|
+
return isListManifest(manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
|
|
2497
|
+
}).join(" &&\n ");
|
|
2498
|
+
if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
|
|
2499
|
+
pageHasListStructs = true;
|
|
2500
|
+
}
|
|
2347
2501
|
const toStringFields = fields.map(
|
|
2348
2502
|
(f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
|
|
2349
2503
|
).join(", ");
|
|
@@ -2393,12 +2547,14 @@ ${fragmentFromString(fieldDecls)}
|
|
|
2393
2547
|
${fragmentFromString(eqChecks)};
|
|
2394
2548
|
|
|
2395
2549
|
@override
|
|
2396
|
-
int get hashCode => ${fragmentFromString(
|
|
2550
|
+
int get hashCode => ${fragmentFromString(
|
|
2551
|
+
getValueHashExpression(fields, fieldManifests)
|
|
2552
|
+
)};
|
|
2397
2553
|
|
|
2398
2554
|
@override
|
|
2399
2555
|
String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
|
|
2400
2556
|
}
|
|
2401
|
-
|
|
2557
|
+
${pageHasListStructs ? LIST_VALUE_HELPERS : ""}
|
|
2402
2558
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
2403
2559
|
final structEncoder = getStructEncoder(<(String, Encoder<Object?>)>[
|
|
2404
2560
|
${fragmentFromString(encFields)}
|
|
@@ -2464,6 +2620,56 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
|
|
|
2464
2620
|
return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
|
|
2465
2621
|
}`;
|
|
2466
2622
|
}
|
|
2623
|
+
function isListManifest(manifest) {
|
|
2624
|
+
return manifest.type.content.startsWith("List<") || manifest.type.content === "Uint8List";
|
|
2625
|
+
}
|
|
2626
|
+
var LIST_VALUE_HELPERS = `
|
|
2627
|
+
bool _listEquals<T>(List<T>? a, List<T>? b) {
|
|
2628
|
+
if (identical(a, b)) return true;
|
|
2629
|
+
if (a == null || b == null) return a == b;
|
|
2630
|
+
if (a.length != b.length) return false;
|
|
2631
|
+
for (var i = 0; i < a.length; i++) {
|
|
2632
|
+
final left = a[i];
|
|
2633
|
+
final right = b[i];
|
|
2634
|
+
if (left is List<Object?> && right is List<Object?>) {
|
|
2635
|
+
if (!_listEquals(left, right)) return false;
|
|
2636
|
+
} else if (left != right) {
|
|
2637
|
+
return false;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
return true;
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
Object? _deepHash(Object? value) {
|
|
2644
|
+
if (value is List<Object?>) {
|
|
2645
|
+
return Object.hashAll(value.map(_deepHash));
|
|
2646
|
+
}
|
|
2647
|
+
return value;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
int _listHashCode<T>(List<T>? a) {
|
|
2651
|
+
if (a == null) return 0;
|
|
2652
|
+
return Object.hashAll(a.map(_deepHash));
|
|
2653
|
+
}
|
|
2654
|
+
`;
|
|
2655
|
+
function getValueHashExpression(fields, fieldManifests) {
|
|
2656
|
+
const fieldNames = fields.map(
|
|
2657
|
+
(f) => camelCase(f.name)
|
|
2658
|
+
);
|
|
2659
|
+
const hasListFields = fieldManifests.some(
|
|
2660
|
+
({ manifest }) => isListManifest(manifest)
|
|
2661
|
+
);
|
|
2662
|
+
if (!hasListFields) {
|
|
2663
|
+
return getHashExpression(fieldNames);
|
|
2664
|
+
}
|
|
2665
|
+
if (fieldNames.length === 1) {
|
|
2666
|
+
return `_listHashCode(${fieldNames[0]})`;
|
|
2667
|
+
}
|
|
2668
|
+
const hashArgs = fields.map(
|
|
2669
|
+
(f, index) => isListManifest(fieldManifests[index].manifest) ? `_listHashCode(${camelCase(f.name)})` : camelCase(f.name)
|
|
2670
|
+
);
|
|
2671
|
+
return `Object.hash(${hashArgs.join(", ")})`;
|
|
2672
|
+
}
|
|
2467
2673
|
function getHashExpression(fields) {
|
|
2468
2674
|
if (fields.length === 0) return "runtimeType.hashCode";
|
|
2469
2675
|
if (fields.length === 1) return `${fields[0]}.hashCode`;
|
|
@@ -2537,9 +2743,12 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2537
2743
|
visitorsCore.visit(node.program, self),
|
|
2538
2744
|
...(node.additionalPrograms ?? []).map((p) => visitorsCore.visit(p, self))
|
|
2539
2745
|
];
|
|
2540
|
-
const mergedProgramMap =
|
|
2746
|
+
const mergedProgramMap = mergeRenderMapsWithoutCollisions(programMaps);
|
|
2541
2747
|
const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
|
|
2542
|
-
return
|
|
2748
|
+
return mergeRenderMapsWithoutCollisions([
|
|
2749
|
+
mergedProgramMap,
|
|
2750
|
+
...categoryMaps
|
|
2751
|
+
]);
|
|
2543
2752
|
},
|
|
2544
2753
|
visitProgram(node, { self }) {
|
|
2545
2754
|
const programName = node.name;
|
|
@@ -2601,7 +2810,7 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2601
2810
|
);
|
|
2602
2811
|
maps.push(rootMap);
|
|
2603
2812
|
}
|
|
2604
|
-
return
|
|
2813
|
+
return mergeRenderMapsWithoutCollisions(maps);
|
|
2605
2814
|
},
|
|
2606
2815
|
visitAccount(node, { self }) {
|
|
2607
2816
|
const fileName = `${snakeCase(node.name)}.dart`;
|
|
@@ -2636,6 +2845,18 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2636
2845
|
(v) => visitorsCore.recordLinkablesOnFirstVisitVisitor(v, linkables)
|
|
2637
2846
|
);
|
|
2638
2847
|
}
|
|
2848
|
+
function mergeRenderMapsWithoutCollisions(maps) {
|
|
2849
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2850
|
+
for (const map of maps) {
|
|
2851
|
+
for (const path of map.keys()) {
|
|
2852
|
+
if (paths.has(path)) {
|
|
2853
|
+
throw new Error(`Duplicate generated path "${path}"`);
|
|
2854
|
+
}
|
|
2855
|
+
paths.add(path);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
return renderersCore.mergeRenderMaps(maps);
|
|
2859
|
+
}
|
|
2639
2860
|
function getSharedCategoryIndexMaps(renderMap) {
|
|
2640
2861
|
const categories = /* @__PURE__ */ new Map();
|
|
2641
2862
|
const categoryIndexNames = {
|
|
@@ -2652,7 +2873,6 @@ function getSharedCategoryIndexMaps(renderMap) {
|
|
|
2652
2873
|
);
|
|
2653
2874
|
if (!match) continue;
|
|
2654
2875
|
const [, category, fileName] = match;
|
|
2655
|
-
if (fileName === categoryIndexNames[category]) continue;
|
|
2656
2876
|
if (!categories.has(category)) {
|
|
2657
2877
|
categories.set(category, /* @__PURE__ */ new Set());
|
|
2658
2878
|
}
|
|
@@ -2682,9 +2902,6 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2682
2902
|
} = options;
|
|
2683
2903
|
return visitorsCore.rootNodeVisitor((root) => {
|
|
2684
2904
|
const normalizedRoot = normalizeRootNode(root);
|
|
2685
|
-
if (deleteFolderBeforeRendering && fs.existsSync(outputDir)) {
|
|
2686
|
-
renderersCore.deleteDirectory(outputDir);
|
|
2687
|
-
}
|
|
2688
2905
|
const renderMap = visitorsCore.visit(
|
|
2689
2906
|
normalizedRoot,
|
|
2690
2907
|
getRenderMapVisitor({ nameApi, dependencyMap })
|
|
@@ -2696,12 +2913,9 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2696
2913
|
typePathMap[`definedType:${match[1]}`] = renderPath;
|
|
2697
2914
|
}
|
|
2698
2915
|
}
|
|
2916
|
+
const files = [];
|
|
2699
2917
|
for (const [filePath, frag] of renderMap.entries()) {
|
|
2700
2918
|
const fullPath = path.join(outputDir, filePath);
|
|
2701
|
-
const dir = path.dirname(fullPath);
|
|
2702
|
-
if (!fs.existsSync(dir)) {
|
|
2703
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
2704
|
-
}
|
|
2705
2919
|
const fileDir = path.dirname(filePath);
|
|
2706
2920
|
const internalMap = {};
|
|
2707
2921
|
for (const [key, typePath] of Object.entries(typePathMap)) {
|
|
@@ -2716,6 +2930,13 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2716
2930
|
internalMap[key] = rel;
|
|
2717
2931
|
}
|
|
2718
2932
|
const content = resolveFragmentContent(frag, dependencyMap ?? {}, internalMap);
|
|
2933
|
+
files.push([fullPath, content]);
|
|
2934
|
+
}
|
|
2935
|
+
if (deleteFolderBeforeRendering && fs.existsSync(outputDir)) {
|
|
2936
|
+
renderersCore.deleteDirectory(outputDir);
|
|
2937
|
+
}
|
|
2938
|
+
for (const [fullPath, content] of files) {
|
|
2939
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
2719
2940
|
fs.writeFileSync(fullPath, content, "utf-8");
|
|
2720
2941
|
}
|
|
2721
2942
|
if (formatCode) {
|