codama-renderers-dart 0.4.3 → 0.5.1
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/LICENSE +21 -0
- package/dist/index.browser.cjs +634 -159
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +635 -161
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +634 -159
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +635 -161
- package/dist/index.node.js.map +1 -1
- package/dist/index.react-native.js +635 -161
- package/dist/index.react-native.js.map +1 -1
- package/dist/types/fragments/accountPage.d.ts.map +1 -1
- package/dist/types/fragments/instructionPage.d.ts.map +1 -1
- package/dist/types/fragments/programPage.d.ts +1 -1
- package/dist/types/fragments/programPage.d.ts.map +1 -1
- package/dist/types/fragments/typePage.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/utils/discriminators.d.ts +10 -0
- package/dist/types/utils/discriminators.d.ts.map +1 -0
- package/dist/types/utils/exactDecoder.d.ts +17 -0
- package/dist/types/utils/exactDecoder.d.ts.map +1 -0
- package/dist/types/utils/formatCode.d.ts +1 -1
- package/dist/types/utils/formatCode.d.ts.map +1 -1
- package/dist/types/utils/importMap.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +4 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/nameTransformers.d.ts +1 -1
- package/dist/types/utils/nameTransformers.d.ts.map +1 -1
- package/dist/types/utils/normalizeRootNode.d.ts +11 -0
- package/dist/types/utils/normalizeRootNode.d.ts.map +1 -0
- package/dist/types/utils/valueNodes.d.ts +16 -0
- package/dist/types/utils/valueNodes.d.ts.map +1 -0
- 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 +31 -23
- package/readme.md +30 -3
- package/src/fragments/accountPage.ts +90 -17
- package/src/fragments/instructionPage.ts +109 -72
- package/src/fragments/programPage.ts +165 -6
- package/src/fragments/typePage.ts +36 -15
- package/src/index.ts +1 -0
- package/src/utils/discriminators.ts +121 -0
- package/src/utils/exactDecoder.ts +82 -0
- package/src/utils/formatCode.ts +7 -11
- package/src/utils/importMap.ts +16 -3
- package/src/utils/index.ts +4 -0
- package/src/utils/nameTransformers.ts +3 -2
- package/src/utils/normalizeRootNode.ts +76 -0
- package/src/utils/valueNodes.ts +87 -0
- package/src/utils/wellKnownAddresses.ts +4 -4
- package/src/visitors/getRenderMapVisitor.ts +1 -1
- package/src/visitors/getTypeManifestVisitor.ts +48 -29
- package/src/visitors/renderVisitor.ts +11 -4
package/dist/index.browser.js
CHANGED
|
@@ -3,18 +3,76 @@ 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
5
|
import { execSync } from 'child_process';
|
|
6
|
-
import { resolveNestedTypeNode } from '@codama/nodes';
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
execSync(`dart format "${dir}"`, {
|
|
12
|
+
encoding: "utf-8",
|
|
13
|
+
timeout: 6e4,
|
|
14
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/utils/normalizeRootNode.ts
|
|
19
|
+
function normalizeRootNode(root) {
|
|
20
|
+
return normalizeNode(root);
|
|
21
|
+
}
|
|
22
|
+
function normalizeNode(value) {
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map(normalizeNode);
|
|
25
|
+
}
|
|
26
|
+
if (!isRecord(value)) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
const normalized = Object.fromEntries(
|
|
30
|
+
Object.entries(value).map(([key, child]) => [key, normalizeNode(child)])
|
|
31
|
+
);
|
|
32
|
+
switch (normalized.kind) {
|
|
33
|
+
case "rootNode":
|
|
34
|
+
setEmptyCollection(normalized, "additionalPrograms");
|
|
35
|
+
break;
|
|
36
|
+
case "programNode":
|
|
37
|
+
setEmptyCollection(normalized, "accounts");
|
|
38
|
+
setEmptyCollection(normalized, "constants");
|
|
39
|
+
setEmptyCollection(normalized, "definedTypes");
|
|
40
|
+
setEmptyCollection(normalized, "errors");
|
|
41
|
+
setEmptyCollection(normalized, "events");
|
|
42
|
+
setEmptyCollection(normalized, "instructions");
|
|
43
|
+
setEmptyCollection(normalized, "pdas");
|
|
44
|
+
break;
|
|
45
|
+
case "instructionNode":
|
|
46
|
+
setEmptyCollection(normalized, "accounts");
|
|
47
|
+
setEmptyCollection(normalized, "arguments");
|
|
48
|
+
break;
|
|
49
|
+
case "pdaNode":
|
|
50
|
+
setEmptyCollection(normalized, "seeds");
|
|
51
|
+
break;
|
|
52
|
+
case "structTypeNode":
|
|
53
|
+
setEmptyCollection(normalized, "fields");
|
|
54
|
+
break;
|
|
55
|
+
case "enumTypeNode":
|
|
56
|
+
setEmptyCollection(normalized, "variants");
|
|
57
|
+
break;
|
|
58
|
+
case "tupleTypeNode":
|
|
59
|
+
setEmptyCollection(normalized, "items");
|
|
60
|
+
break;
|
|
61
|
+
case "hiddenPrefixTypeNode":
|
|
62
|
+
setEmptyCollection(normalized, "prefix");
|
|
63
|
+
break;
|
|
64
|
+
case "hiddenSuffixTypeNode":
|
|
65
|
+
setEmptyCollection(normalized, "suffix");
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return normalized;
|
|
69
|
+
}
|
|
70
|
+
function isRecord(value) {
|
|
71
|
+
return typeof value === "object" && value !== null;
|
|
72
|
+
}
|
|
73
|
+
function setEmptyCollection(record, key) {
|
|
74
|
+
if (record[key] === void 0) {
|
|
75
|
+
record[key] = [];
|
|
18
76
|
}
|
|
19
77
|
}
|
|
20
78
|
|
|
@@ -46,7 +104,7 @@ function createDartNameApi() {
|
|
|
46
104
|
errorConstant: (programName, errorName) => `${camelCase(programName)}Error${pascalCase(errorName)}`,
|
|
47
105
|
errorMessageFunction: (name) => `get${pascalCase(name)}ErrorMessage`,
|
|
48
106
|
enumVariant: (name) => camelCase(name),
|
|
49
|
-
sealedClassVariant: (
|
|
107
|
+
sealedClassVariant: (parentName, variantName) => `${pascalCase(parentName)}${pascalCase(variantName)}`,
|
|
50
108
|
isTypeFunction: (name) => `is${pascalCase(name)}`,
|
|
51
109
|
fileName: (name) => snakeCase(name),
|
|
52
110
|
discriminatorConstant: (name) => `${camelCase(name)}Discriminator`
|
|
@@ -120,11 +178,16 @@ var DartImportMap = class {
|
|
|
120
178
|
const uris = /* @__PURE__ */ new Set();
|
|
121
179
|
for (const module of this._imports) {
|
|
122
180
|
if (module in internalMap) {
|
|
123
|
-
|
|
181
|
+
const uri = internalMap[module];
|
|
182
|
+
if (uri) {
|
|
183
|
+
uris.add(uri);
|
|
184
|
+
}
|
|
124
185
|
} else if (module in DART_EXTERNAL_PACKAGE_MAP) {
|
|
125
186
|
uris.add(DART_EXTERNAL_PACKAGE_MAP[module]);
|
|
126
|
-
} else {
|
|
187
|
+
} else if (isRawDartImportUri(module)) {
|
|
127
188
|
uris.add(module);
|
|
189
|
+
} else {
|
|
190
|
+
throw new Error(`Unresolved Dart import module "${module}"`);
|
|
128
191
|
}
|
|
129
192
|
}
|
|
130
193
|
return [...uris].sort((a, b) => {
|
|
@@ -153,6 +216,9 @@ var DartImportMap = class {
|
|
|
153
216
|
return lines.join("\n");
|
|
154
217
|
}
|
|
155
218
|
};
|
|
219
|
+
function isRawDartImportUri(module) {
|
|
220
|
+
return module.startsWith("dart:") || module.startsWith("package:") || module.endsWith(".dart") && !module.includes(":");
|
|
221
|
+
}
|
|
156
222
|
|
|
157
223
|
// src/utils/fragment.ts
|
|
158
224
|
function fragment(strings, ...values) {
|
|
@@ -196,17 +262,6 @@ function use(typeName, module) {
|
|
|
196
262
|
return { content: typeName, imports };
|
|
197
263
|
}
|
|
198
264
|
|
|
199
|
-
// src/utils/typeManifest.ts
|
|
200
|
-
function emptyTypeManifest() {
|
|
201
|
-
return {
|
|
202
|
-
type: emptyFragment(),
|
|
203
|
-
encoder: emptyFragment(),
|
|
204
|
-
decoder: emptyFragment(),
|
|
205
|
-
value: emptyFragment(),
|
|
206
|
-
isEnum: false
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
265
|
// src/utils/wellKnownAddresses.ts
|
|
211
266
|
var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
|
|
212
267
|
// Agave / Solana native programs
|
|
@@ -244,10 +299,10 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
|
|
|
244
299
|
["Sysvar1111111111111111111111111111111111111", "sysvarOwnerAddress"],
|
|
245
300
|
// SPL programs
|
|
246
301
|
["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "tokenProgramAddress"],
|
|
247
|
-
["
|
|
248
|
-
["
|
|
249
|
-
["
|
|
250
|
-
["
|
|
302
|
+
["TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "token2022ProgramAddress"],
|
|
303
|
+
["ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "associatedTokenProgramAddress"],
|
|
304
|
+
["MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", "memoProgramAddress"],
|
|
305
|
+
["Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", "memoLegacyProgramAddress"],
|
|
251
306
|
// Metaplex programs
|
|
252
307
|
["metaqbxxU9qX8rZXkmUJ7haCrXXz2W1PSUq1Rn3E1od", "tokenMetadataProgramAddress"],
|
|
253
308
|
["BGUMAp9V1mQ2KV8t34L5u3gZsXYZaYDBdKkc1xYk1qA1", "mplBubblegumProgramAddress"],
|
|
@@ -261,6 +316,207 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
|
|
|
261
316
|
["Es9vMFrzaCERm6YKXx6bqKr1Rmd9b5v3J8rM5e4rfF4", "usdtAddress"]
|
|
262
317
|
]);
|
|
263
318
|
|
|
319
|
+
// src/utils/valueNodes.ts
|
|
320
|
+
function getDartValueFragment(value, dartType) {
|
|
321
|
+
switch (value.kind) {
|
|
322
|
+
case "numberValueNode":
|
|
323
|
+
return fragmentFromString(
|
|
324
|
+
dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
|
|
325
|
+
);
|
|
326
|
+
case "booleanValueNode":
|
|
327
|
+
return fragmentFromString(String(value.boolean));
|
|
328
|
+
case "stringValueNode":
|
|
329
|
+
return fragmentFromString(toDartStringLiteral(value.string));
|
|
330
|
+
case "noneValueNode":
|
|
331
|
+
return fragmentFromString("null");
|
|
332
|
+
case "bytesValueNode": {
|
|
333
|
+
if (value.encoding !== "base16") {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`Unsupported deterministic bytes encoding: ${value.encoding}`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
const clean = value.data.replace(/^0x/, "");
|
|
339
|
+
if (clean.length % 2 !== 0 || !/^[0-9a-f]*$/i.test(clean)) {
|
|
340
|
+
throw new Error(`Invalid hexadecimal bytes value: ${value.data}`);
|
|
341
|
+
}
|
|
342
|
+
const bytes = clean.match(/.{2}/g)?.map((byte) => `0x${byte}`) ?? [];
|
|
343
|
+
return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
|
|
344
|
+
}
|
|
345
|
+
case "publicKeyValueNode": {
|
|
346
|
+
const wellKnownName = WELL_KNOWN_ADDRESSES.get(value.publicKey);
|
|
347
|
+
return wellKnownName ? fragment`${use(wellKnownName, "solanaAddresses")}` : fragment`${use("Address", "solanaAddresses")}(${fragmentFromString(toDartStringLiteral(value.publicKey))})`;
|
|
348
|
+
}
|
|
349
|
+
default:
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Unsupported deterministic Dart value node: ${value.kind}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function isConstDartValueNode(value) {
|
|
356
|
+
switch (value.kind) {
|
|
357
|
+
case "numberValueNode":
|
|
358
|
+
case "booleanValueNode":
|
|
359
|
+
case "stringValueNode":
|
|
360
|
+
case "noneValueNode":
|
|
361
|
+
case "publicKeyValueNode":
|
|
362
|
+
return true;
|
|
363
|
+
default:
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function toDartStringLiteral(value) {
|
|
368
|
+
return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/utils/discriminators.ts
|
|
372
|
+
function getDiscriminatorValidationFragment(node, scope) {
|
|
373
|
+
const discriminators = node.discriminators ?? [];
|
|
374
|
+
if (discriminators.length === 0) return emptyFragment();
|
|
375
|
+
const fields = getDiscriminatorFields(node);
|
|
376
|
+
const fragments = discriminators.map((discriminator) => {
|
|
377
|
+
switch (discriminator.kind) {
|
|
378
|
+
case "constantDiscriminatorNode":
|
|
379
|
+
return getConstantValidationFragment(
|
|
380
|
+
discriminator.constant,
|
|
381
|
+
discriminator.offset,
|
|
382
|
+
scope
|
|
383
|
+
);
|
|
384
|
+
case "fieldDiscriminatorNode": {
|
|
385
|
+
const field = fields.find(
|
|
386
|
+
(candidate) => candidate.name === discriminator.name
|
|
387
|
+
);
|
|
388
|
+
if (field?.defaultValue == null) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`Field discriminator "${discriminator.name}" on ${node.kind} "${node.name}" must reference a field with a default value.`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return getConstantValidationFragment(
|
|
394
|
+
{
|
|
395
|
+
type: field.type,
|
|
396
|
+
value: field.defaultValue
|
|
397
|
+
},
|
|
398
|
+
discriminator.offset,
|
|
399
|
+
scope
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
case "sizeDiscriminatorNode": {
|
|
403
|
+
if (!Number.isSafeInteger(discriminator.size) || discriminator.size < 0) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Discriminator size must be a non-negative safe integer, got ${discriminator.size}.`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
return fragment`if (bytes.length - offset != ${fragmentFromString(String(discriminator.size))}) {
|
|
409
|
+
throw ${use("SolanaError", "solanaErrors")}(
|
|
410
|
+
${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
|
|
411
|
+
{
|
|
412
|
+
'codecDescription': '${fragmentFromString(node.name)} discriminator',
|
|
413
|
+
'expected': ${fragmentFromString(String(discriminator.size))},
|
|
414
|
+
'bytesLength': bytes.length - offset,
|
|
415
|
+
},
|
|
416
|
+
);
|
|
417
|
+
}`;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
return mergeFragments(fragments, (contents) => contents.join("\n"));
|
|
422
|
+
}
|
|
423
|
+
function getConstantValidationFragment(constant, discriminatorOffset, scope) {
|
|
424
|
+
if (!Number.isSafeInteger(discriminatorOffset) || discriminatorOffset < 0) {
|
|
425
|
+
throw new Error(
|
|
426
|
+
`Discriminator offset must be a non-negative safe integer, got ${discriminatorOffset}.`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const manifest = visit(constant.type, scope.typeManifestVisitor);
|
|
430
|
+
const value = getDartValueFragment(
|
|
431
|
+
constant.value,
|
|
432
|
+
manifest.type.content
|
|
433
|
+
);
|
|
434
|
+
return fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(
|
|
435
|
+
${manifest.encoder}.encode(${value}),
|
|
436
|
+
).read(bytes, offset + ${fragmentFromString(String(discriminatorOffset))});`;
|
|
437
|
+
}
|
|
438
|
+
function getDiscriminatorFields(node) {
|
|
439
|
+
if (node.kind === "accountNode") {
|
|
440
|
+
return resolveNestedTypeNode(node.data).fields ?? [];
|
|
441
|
+
}
|
|
442
|
+
return (node.arguments ?? []).map(
|
|
443
|
+
(argument) => ({
|
|
444
|
+
...argument,
|
|
445
|
+
kind: "structFieldTypeNode"
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/utils/exactDecoder.ts
|
|
451
|
+
function getTopLevelDecoderFragment({
|
|
452
|
+
typeName,
|
|
453
|
+
description,
|
|
454
|
+
discriminatorValidation,
|
|
455
|
+
fromMapFields,
|
|
456
|
+
requireExactConsumption
|
|
457
|
+
}) {
|
|
458
|
+
const validation = discriminatorValidation.content ? ` ${discriminatorValidation.content.replaceAll("\n", "\n ")}
|
|
459
|
+
` : "";
|
|
460
|
+
const consumptionCheck = requireExactConsumption ? ` if (newOffset != bytes.length) {
|
|
461
|
+
throwInvalidByteLength(newOffset - offset, bytes.length - offset);
|
|
462
|
+
}
|
|
463
|
+
` : "";
|
|
464
|
+
const fixedSizeCheck = requireExactConsumption ? "bytesLength != structDecoder.fixedSize" : "bytesLength < structDecoder.fixedSize";
|
|
465
|
+
const result = fragment` Never throwInvalidByteLength(int expected, int bytesLength) {
|
|
466
|
+
throw ${use("SolanaError", "solanaErrors")}(
|
|
467
|
+
${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
|
|
468
|
+
{
|
|
469
|
+
'codecDescription': '${fragmentFromString(description)}',
|
|
470
|
+
'expected': expected,
|
|
471
|
+
'bytesLength': bytesLength,
|
|
472
|
+
},
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
(${fragmentFromString(typeName)}, int) readTopLevel(Uint8List bytes, int offset) {
|
|
477
|
+
${fragmentFromString(validation)} final (map, newOffset) = structDecoder.read(bytes, offset);
|
|
478
|
+
${fragmentFromString(consumptionCheck)}
|
|
479
|
+
return (
|
|
480
|
+
${fragmentFromString(typeName)}(
|
|
481
|
+
${fragmentFromString(fromMapFields)}
|
|
482
|
+
),
|
|
483
|
+
newOffset,
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return switch (structDecoder) {
|
|
488
|
+
${use("FixedSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
|
|
489
|
+
${use("FixedSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
|
|
490
|
+
fixedSize: structDecoder.fixedSize,
|
|
491
|
+
read: (bytes, offset) {
|
|
492
|
+
final bytesLength = bytes.length - offset;
|
|
493
|
+
if (${fragmentFromString(fixedSizeCheck)}) {
|
|
494
|
+
throwInvalidByteLength(structDecoder.fixedSize, bytesLength);
|
|
495
|
+
}
|
|
496
|
+
return readTopLevel(bytes, offset);
|
|
497
|
+
},
|
|
498
|
+
),
|
|
499
|
+
${use("VariableSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
|
|
500
|
+
${use("VariableSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
|
|
501
|
+
read: readTopLevel,
|
|
502
|
+
maxSize: structDecoder.maxSize,
|
|
503
|
+
),
|
|
504
|
+
};`;
|
|
505
|
+
result.imports.mergeWith(discriminatorValidation.imports);
|
|
506
|
+
return result;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/utils/typeManifest.ts
|
|
510
|
+
function emptyTypeManifest() {
|
|
511
|
+
return {
|
|
512
|
+
type: emptyFragment(),
|
|
513
|
+
encoder: emptyFragment(),
|
|
514
|
+
decoder: emptyFragment(),
|
|
515
|
+
value: emptyFragment(),
|
|
516
|
+
isEnum: false
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
264
520
|
// src/visitors/getTypeManifestVisitor.ts
|
|
265
521
|
function getTypeManifestVisitor(input) {
|
|
266
522
|
const { nameApi, linkables, nonScalarEnums = [] } = input;
|
|
@@ -402,17 +658,23 @@ function getTypeManifestVisitor(input) {
|
|
|
402
658
|
},
|
|
403
659
|
visitArrayType(node, { self }) {
|
|
404
660
|
const itemManifest = visit(node.item, self);
|
|
405
|
-
const
|
|
661
|
+
const encoderSizeExpr = getArraySizeExpression(node, "Encoder");
|
|
662
|
+
const decoderSizeExpr = getArraySizeExpression(node, "Decoder");
|
|
663
|
+
const itemType = fragmentFromString(itemManifest.type.content);
|
|
664
|
+
const itemEncoder = fragment`${use(
|
|
665
|
+
"transformEncoder",
|
|
666
|
+
"solanaCodecsCore"
|
|
667
|
+
)}(${itemManifest.encoder}, (${itemType} value) => value)`;
|
|
406
668
|
return {
|
|
407
669
|
type: fragment`List<${itemManifest.type}>`,
|
|
408
670
|
encoder: fragment`${use(
|
|
409
671
|
"getArrayEncoder",
|
|
410
672
|
"solanaCodecsDataStructures"
|
|
411
|
-
)}(${
|
|
673
|
+
)}<${itemType}>(${itemEncoder}${encoderSizeExpr})`,
|
|
412
674
|
decoder: fragment`${use(
|
|
413
675
|
"getArrayDecoder",
|
|
414
676
|
"solanaCodecsDataStructures"
|
|
415
|
-
)}(${itemManifest.decoder}${
|
|
677
|
+
)}(${itemManifest.decoder}${decoderSizeExpr})`,
|
|
416
678
|
value: emptyTypeManifest().value,
|
|
417
679
|
isEnum: false
|
|
418
680
|
};
|
|
@@ -420,40 +682,42 @@ function getTypeManifestVisitor(input) {
|
|
|
420
682
|
visitMapType(node, { self }) {
|
|
421
683
|
const keyManifest = visit(node.key, self);
|
|
422
684
|
const valueManifest = visit(node.value, self);
|
|
423
|
-
const
|
|
685
|
+
const encoderSizeExpr = getMapSizeExpression(node, "Encoder");
|
|
686
|
+
const decoderSizeExpr = getMapSizeExpression(node, "Decoder");
|
|
424
687
|
return {
|
|
425
688
|
type: fragment`Map<${keyManifest.type}, ${valueManifest.type}>`,
|
|
426
689
|
encoder: fragment`${use(
|
|
427
690
|
"getMapEncoder",
|
|
428
691
|
"solanaCodecsDataStructures"
|
|
429
|
-
)}(${keyManifest.encoder}, ${valueManifest.encoder}${
|
|
692
|
+
)}(${keyManifest.encoder}, ${valueManifest.encoder}${encoderSizeExpr})`,
|
|
430
693
|
decoder: fragment`${use(
|
|
431
694
|
"getMapDecoder",
|
|
432
695
|
"solanaCodecsDataStructures"
|
|
433
|
-
)}(${keyManifest.decoder}, ${valueManifest.decoder}${
|
|
696
|
+
)}(${keyManifest.decoder}, ${valueManifest.decoder}${decoderSizeExpr})`,
|
|
434
697
|
value: emptyTypeManifest().value,
|
|
435
698
|
isEnum: false
|
|
436
699
|
};
|
|
437
700
|
},
|
|
438
701
|
visitSetType(node, { self }) {
|
|
439
702
|
const itemManifest = visit(node.item, self);
|
|
440
|
-
const
|
|
703
|
+
const encoderSizeExpr = getSetSizeExpression(node, "Encoder");
|
|
704
|
+
const decoderSizeExpr = getSetSizeExpression(node, "Decoder");
|
|
441
705
|
return {
|
|
442
706
|
type: fragment`Set<${itemManifest.type}>`,
|
|
443
707
|
encoder: fragment`${use(
|
|
444
708
|
"getSetEncoder",
|
|
445
709
|
"solanaCodecsDataStructures"
|
|
446
|
-
)}(${itemManifest.encoder}${
|
|
710
|
+
)}(${itemManifest.encoder}${encoderSizeExpr})`,
|
|
447
711
|
decoder: fragment`${use(
|
|
448
712
|
"getSetDecoder",
|
|
449
713
|
"solanaCodecsDataStructures"
|
|
450
|
-
)}(${itemManifest.decoder}${
|
|
714
|
+
)}(${itemManifest.decoder}${decoderSizeExpr})`,
|
|
451
715
|
value: emptyTypeManifest().value,
|
|
452
716
|
isEnum: false
|
|
453
717
|
};
|
|
454
718
|
},
|
|
455
719
|
visitTupleType(node, { self }) {
|
|
456
|
-
const itemManifests = node.items.map((item) => visit(item, self));
|
|
720
|
+
const itemManifests = (node.items ?? []).map((item) => visit(item, self));
|
|
457
721
|
const typeList = itemManifests.map((m) => m.type.content).join(", ");
|
|
458
722
|
const encoderList = itemManifests.map((m) => m.encoder);
|
|
459
723
|
const decoderList = itemManifests.map((m) => m.decoder);
|
|
@@ -486,8 +750,9 @@ function getTypeManifestVisitor(input) {
|
|
|
486
750
|
};
|
|
487
751
|
},
|
|
488
752
|
visitStructType(node, { self }) {
|
|
489
|
-
const
|
|
490
|
-
const
|
|
753
|
+
const nodeFields = node.fields ?? [];
|
|
754
|
+
const fields = nodeFields.map((f) => visit(f, self));
|
|
755
|
+
const fieldNames = nodeFields.map((f) => f.name);
|
|
491
756
|
const typeStr = "Map<String, Object?>";
|
|
492
757
|
const encoderFields = fields.map((f, i) => `('${fieldNames[i]}', ${f.encoder.content})`).join(", ");
|
|
493
758
|
const encoderFrag = fragment`${use(
|
|
@@ -517,7 +782,7 @@ function getTypeManifestVisitor(input) {
|
|
|
517
782
|
return visit(node.type, self);
|
|
518
783
|
},
|
|
519
784
|
visitEnumType(node, { self }) {
|
|
520
|
-
const isScalar = node.variants.every(
|
|
785
|
+
const isScalar = (node.variants ?? []).every(
|
|
521
786
|
(v) => v.kind === "enumEmptyVariantTypeNode"
|
|
522
787
|
);
|
|
523
788
|
if (isScalar) {
|
|
@@ -609,7 +874,7 @@ function getTypeManifestVisitor(input) {
|
|
|
609
874
|
encoder: fragment`${use(
|
|
610
875
|
"fixEncoderSize",
|
|
611
876
|
"solanaCodecsCore"
|
|
612
|
-
)}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))})`,
|
|
877
|
+
)}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))}, allowTruncation: false)`,
|
|
613
878
|
decoder: fragment`${use(
|
|
614
879
|
"fixDecoderSize",
|
|
615
880
|
"solanaCodecsCore"
|
|
@@ -623,9 +888,10 @@ function getTypeManifestVisitor(input) {
|
|
|
623
888
|
const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
|
|
624
889
|
let prefixManifest;
|
|
625
890
|
if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
|
|
891
|
+
const codecName = getNumberCodecName(node.prefix.format);
|
|
626
892
|
prefixManifest = {
|
|
627
|
-
encoder: fragment
|
|
628
|
-
decoder: fragment
|
|
893
|
+
encoder: fragment`transformEncoder(${use(`get${codecName}Encoder`, "solanaCodecsNumbers")}(), (size) => BigInt.from(size))`,
|
|
894
|
+
decoder: fragment`transformDecoder(${use(`get${codecName}Decoder`, "solanaCodecsNumbers")}(), (size, _, __) => size.toInt())`
|
|
629
895
|
};
|
|
630
896
|
} else {
|
|
631
897
|
prefixManifest = visit(node.prefix, self);
|
|
@@ -646,7 +912,7 @@ function getTypeManifestVisitor(input) {
|
|
|
646
912
|
},
|
|
647
913
|
visitHiddenPrefixType(node, { self }) {
|
|
648
914
|
const innerManifest = visit(node.type, self);
|
|
649
|
-
const prefixes = node.prefix.map((p) => getHiddenAffixManifest(p, self));
|
|
915
|
+
const prefixes = (node.prefix ?? []).map((p) => getHiddenAffixManifest(p, self));
|
|
650
916
|
const prefixEncoders = prefixes.map((p) => p.encoder.content).join(", ");
|
|
651
917
|
const prefixDecoders = prefixes.map((p) => p.decoder.content).join(", ");
|
|
652
918
|
const encoderFrag = fragment`${use(
|
|
@@ -673,7 +939,7 @@ function getTypeManifestVisitor(input) {
|
|
|
673
939
|
},
|
|
674
940
|
visitHiddenSuffixType(node, { self }) {
|
|
675
941
|
const innerManifest = visit(node.type, self);
|
|
676
|
-
const suffixes = node.suffix.map((s) => getHiddenAffixManifest(s, self));
|
|
942
|
+
const suffixes = (node.suffix ?? []).map((s) => getHiddenAffixManifest(s, self));
|
|
677
943
|
const suffixEncoders = suffixes.map((s) => s.encoder.content).join(", ");
|
|
678
944
|
const suffixDecoders = suffixes.map((s) => s.decoder.content).join(", ");
|
|
679
945
|
const encoderFrag = fragment`${use(
|
|
@@ -812,7 +1078,7 @@ function getNumberCodecExpression(format, codecType) {
|
|
|
812
1078
|
const name = getNumberCodecName(format);
|
|
813
1079
|
return `get${name}${codecType}()`;
|
|
814
1080
|
}
|
|
815
|
-
function getArraySizeExpression(node) {
|
|
1081
|
+
function getArraySizeExpression(node, codecType) {
|
|
816
1082
|
if (!("count" in node) || !node.count) return "";
|
|
817
1083
|
const count = node.count;
|
|
818
1084
|
switch (count.kind) {
|
|
@@ -825,13 +1091,13 @@ function getArraySizeExpression(node) {
|
|
|
825
1091
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
826
1092
|
return "";
|
|
827
1093
|
}
|
|
828
|
-
return `, size: PrefixedArraySize(
|
|
1094
|
+
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
|
|
829
1095
|
}
|
|
830
1096
|
default:
|
|
831
1097
|
return "";
|
|
832
1098
|
}
|
|
833
1099
|
}
|
|
834
|
-
function getMapSizeExpression(node) {
|
|
1100
|
+
function getMapSizeExpression(node, codecType) {
|
|
835
1101
|
if (!("count" in node) || !node.count) return "";
|
|
836
1102
|
const count = node.count;
|
|
837
1103
|
switch (count.kind) {
|
|
@@ -844,13 +1110,13 @@ function getMapSizeExpression(node) {
|
|
|
844
1110
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
845
1111
|
return "";
|
|
846
1112
|
}
|
|
847
|
-
return `, size: PrefixedArraySize(
|
|
1113
|
+
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
|
|
848
1114
|
}
|
|
849
1115
|
default:
|
|
850
1116
|
return "";
|
|
851
1117
|
}
|
|
852
1118
|
}
|
|
853
|
-
function getSetSizeExpression(node) {
|
|
1119
|
+
function getSetSizeExpression(node, codecType) {
|
|
854
1120
|
if (!("count" in node) || !node.count) return "";
|
|
855
1121
|
const count = node.count;
|
|
856
1122
|
switch (count.kind) {
|
|
@@ -863,7 +1129,7 @@ function getSetSizeExpression(node) {
|
|
|
863
1129
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
864
1130
|
return "";
|
|
865
1131
|
}
|
|
866
|
-
return `, size: PrefixedArraySize(
|
|
1132
|
+
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
|
|
867
1133
|
}
|
|
868
1134
|
default:
|
|
869
1135
|
return "";
|
|
@@ -960,19 +1226,52 @@ function hexToBytes(hex) {
|
|
|
960
1226
|
function getAccountPageFragment(node, scope) {
|
|
961
1227
|
const name = node.name;
|
|
962
1228
|
const typeName = scope.nameApi.dataType(name);
|
|
963
|
-
const
|
|
964
|
-
const fields = dataNode.fields;
|
|
1229
|
+
const fields = resolveNestedTypeNode(node.data).fields ?? [];
|
|
965
1230
|
const fieldManifests = fields.map((f) => ({
|
|
966
1231
|
field: f,
|
|
967
1232
|
manifest: visit(f.type, scope.typeManifestVisitor)
|
|
968
1233
|
}));
|
|
1234
|
+
const omittedFields = fields.filter(isOmittedDefaultField);
|
|
1235
|
+
const constructorFields = fields.filter(
|
|
1236
|
+
(field) => !isOmittedDefaultField(field)
|
|
1237
|
+
);
|
|
1238
|
+
const forcedFields = /* @__PURE__ */ new Set([
|
|
1239
|
+
...omittedFields,
|
|
1240
|
+
...fields.filter((field) => isFieldDiscriminator(field, node))
|
|
1241
|
+
]);
|
|
1242
|
+
const fieldDefaults = new Map(
|
|
1243
|
+
fieldManifests.flatMap(({ field, manifest }) => {
|
|
1244
|
+
if (!forcedFields.has(field) || field.defaultValue == null) return [];
|
|
1245
|
+
return [
|
|
1246
|
+
[
|
|
1247
|
+
field,
|
|
1248
|
+
getDartValueFragment(field.defaultValue, manifest.type.content)
|
|
1249
|
+
]
|
|
1250
|
+
];
|
|
1251
|
+
})
|
|
1252
|
+
);
|
|
1253
|
+
for (const field of forcedFields) {
|
|
1254
|
+
if (!fieldDefaults.has(field)) {
|
|
1255
|
+
throw new Error(
|
|
1256
|
+
`Forced field "${field.name}" on account "${node.name}" must have a deterministic default value.`
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
969
1260
|
const fieldDecls = fieldManifests.map(({ field: f, manifest }) => {
|
|
970
1261
|
return ` final ${manifest.type.content} ${camelCase(f.name)};`;
|
|
971
1262
|
}).join("\n");
|
|
972
|
-
const ctorParams =
|
|
973
|
-
const
|
|
1263
|
+
const ctorParams = constructorFields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
|
|
1264
|
+
const ctorInitializers = omittedFields.map((field) => {
|
|
1265
|
+
const fieldName = camelCase(field.name);
|
|
1266
|
+
return `${fieldName} = ${fieldDefaults.get(field).content}`;
|
|
1267
|
+
}).join(",\n ");
|
|
1268
|
+
const ctorKeyword = omittedFields.every(
|
|
1269
|
+
(field) => isConstDartValueNode(field.defaultValue)
|
|
1270
|
+
) ? "const " : "";
|
|
1271
|
+
const ctorSignature = constructorFields.length === 0 ? `${ctorKeyword}${typeName}()${ctorInitializers ? ` : ${ctorInitializers}` : ""};` : `${ctorKeyword}${typeName}({
|
|
974
1272
|
${ctorParams}
|
|
975
|
-
})
|
|
1273
|
+
})${ctorInitializers ? ` :
|
|
1274
|
+
${ctorInitializers}` : ""};`;
|
|
976
1275
|
const eqChecks = fields.length === 0 ? "true" : fields.map(
|
|
977
1276
|
(f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
|
|
978
1277
|
).join(" &&\n ");
|
|
@@ -987,10 +1286,11 @@ ${ctorParams}
|
|
|
987
1286
|
const decFields = fieldManifests.map(({ field: f, manifest }) => {
|
|
988
1287
|
return ` ('${f.name}', ${manifest.decoder.content}),`;
|
|
989
1288
|
}).join("\n");
|
|
990
|
-
const toMapFields = fields.map(
|
|
991
|
-
(f)
|
|
992
|
-
|
|
993
|
-
|
|
1289
|
+
const toMapFields = fields.map((f) => {
|
|
1290
|
+
const value = forcedFields.has(f) ? fieldDefaults.get(f).content : `value.${camelCase(f.name)}`;
|
|
1291
|
+
return ` '${f.name}': ${value},`;
|
|
1292
|
+
}).join("\n");
|
|
1293
|
+
const fromMapFields = fieldManifests.filter(({ field }) => !isOmittedDefaultField(field)).map(({ field: f, manifest }) => {
|
|
994
1294
|
const typeStr = manifest.type.content;
|
|
995
1295
|
const isNullable = typeStr.endsWith("?");
|
|
996
1296
|
const accessor = isNullable ? `map['${f.name}']` : `map['${f.name}']!`;
|
|
@@ -1004,6 +1304,19 @@ ${ctorParams}
|
|
|
1004
1304
|
/// The size of the [${fragmentFromString(typeName)}] account data in bytes.
|
|
1005
1305
|
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(String(node.size))};` : emptyFragment();
|
|
1006
1306
|
const discFragment = getDiscriminatorConstantsFragment(node, scope);
|
|
1307
|
+
const discriminatorValidation = getDiscriminatorValidationFragment(
|
|
1308
|
+
node,
|
|
1309
|
+
scope
|
|
1310
|
+
);
|
|
1311
|
+
const topLevelDecoder = getTopLevelDecoderFragment({
|
|
1312
|
+
typeName,
|
|
1313
|
+
description: `${name} account decoder`,
|
|
1314
|
+
discriminatorValidation,
|
|
1315
|
+
fromMapFields,
|
|
1316
|
+
requireExactConsumption: (node.discriminators ?? []).some(
|
|
1317
|
+
(discriminator) => discriminator.kind === "sizeDiscriminatorNode"
|
|
1318
|
+
)
|
|
1319
|
+
});
|
|
1007
1320
|
const parts = [
|
|
1008
1321
|
fragment`// Auto-generated. Do not edit.
|
|
1009
1322
|
// ignore_for_file: type=lint
|
|
@@ -1063,12 +1376,7 @@ Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
|
|
|
1063
1376
|
${fragmentFromString(decFields)}
|
|
1064
1377
|
]);
|
|
1065
1378
|
|
|
1066
|
-
|
|
1067
|
-
structDecoder,
|
|
1068
|
-
(Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}(
|
|
1069
|
-
${fragmentFromString(fromMapFields)}
|
|
1070
|
-
),
|
|
1071
|
-
);
|
|
1379
|
+
${topLevelDecoder}
|
|
1072
1380
|
}
|
|
1073
1381
|
|
|
1074
1382
|
Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragmentFromString(codecName)}() {
|
|
@@ -1084,8 +1392,19 @@ Account<${fragmentFromString(typeName)}> ${fragmentFromString(decodeFnName)}(Enc
|
|
|
1084
1392
|
result.imports.mergeWith(manifest.decoder.imports);
|
|
1085
1393
|
result.imports.mergeWith(manifest.type.imports);
|
|
1086
1394
|
}
|
|
1395
|
+
for (const defaultValue of fieldDefaults.values()) {
|
|
1396
|
+
result.imports.mergeWith(defaultValue.imports);
|
|
1397
|
+
}
|
|
1087
1398
|
return result;
|
|
1088
1399
|
}
|
|
1400
|
+
function isOmittedDefaultField(field) {
|
|
1401
|
+
return field.defaultValue != null && field.defaultValueStrategy === "omitted";
|
|
1402
|
+
}
|
|
1403
|
+
function isFieldDiscriminator(field, node) {
|
|
1404
|
+
return (node.discriminators ?? []).some(
|
|
1405
|
+
(discriminator) => discriminator.kind === "fieldDiscriminatorNode" && discriminator.name === field.name
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1089
1408
|
|
|
1090
1409
|
// src/fragments/errorPage.ts
|
|
1091
1410
|
function getErrorPageFragment(programNode, scope) {
|
|
@@ -1146,7 +1465,7 @@ function getIndexPageFragment(fileNames, relativePath = "") {
|
|
|
1146
1465
|
"// Auto-generated. Do not edit.\n// ignore_for_file: type=lint\n"
|
|
1147
1466
|
);
|
|
1148
1467
|
}
|
|
1149
|
-
const exports
|
|
1468
|
+
const exports = fileNames.sort().map((f) => {
|
|
1150
1469
|
const path = relativePath ? `${relativePath}/${f}` : f;
|
|
1151
1470
|
return `export '${path}';`;
|
|
1152
1471
|
}).join("\n");
|
|
@@ -1154,17 +1473,17 @@ function getIndexPageFragment(fileNames, relativePath = "") {
|
|
|
1154
1473
|
`// Auto-generated. Do not edit.
|
|
1155
1474
|
// ignore_for_file: type=lint
|
|
1156
1475
|
|
|
1157
|
-
${exports
|
|
1476
|
+
${exports}
|
|
1158
1477
|
`
|
|
1159
1478
|
);
|
|
1160
1479
|
}
|
|
1161
1480
|
function getRootIndexPageFragment(categories) {
|
|
1162
|
-
const exports
|
|
1481
|
+
const exports = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
|
|
1163
1482
|
return fragmentFromString(
|
|
1164
1483
|
`// Auto-generated. Do not edit.
|
|
1165
1484
|
// ignore_for_file: type=lint
|
|
1166
1485
|
|
|
1167
|
-
${exports
|
|
1486
|
+
${exports}
|
|
1168
1487
|
`
|
|
1169
1488
|
);
|
|
1170
1489
|
}
|
|
@@ -1174,30 +1493,62 @@ function getInstructionPageFragment(node, scope) {
|
|
|
1174
1493
|
const instrFnName = scope.nameApi.instructionFunction(name);
|
|
1175
1494
|
const parseFnName = scope.nameApi.instructionParseFunction(name);
|
|
1176
1495
|
const accounts = node.accounts ?? [];
|
|
1177
|
-
const args = (node.arguments ?? []).filter(
|
|
1178
|
-
(arg) => !isDiscriminatorArg(arg, node)
|
|
1179
|
-
);
|
|
1180
1496
|
const allArgs = node.arguments ?? [];
|
|
1497
|
+
const optionalAccountStrategy = node.optionalAccountStrategy ?? "programId";
|
|
1498
|
+
if (optionalAccountStrategy !== "programId" && optionalAccountStrategy !== "omitted") {
|
|
1499
|
+
throw new Error(
|
|
1500
|
+
`Unsupported optional account strategy on instruction "${node.name}": ${optionalAccountStrategy}`
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
const args = allArgs.filter(
|
|
1504
|
+
(arg) => !isDiscriminatorArg(arg, node) && !isOmittedDefaultArgument(arg)
|
|
1505
|
+
);
|
|
1181
1506
|
const dataClassName = `${typeName}InstructionData`;
|
|
1182
|
-
const allArgManifests =
|
|
1507
|
+
const allArgManifests = (node.arguments ?? []).map((arg) => ({
|
|
1183
1508
|
arg,
|
|
1184
1509
|
manifest: visit(arg.type, scope.typeManifestVisitor)
|
|
1185
1510
|
}));
|
|
1511
|
+
const argDefaultValues = new Map(
|
|
1512
|
+
allArgManifests.flatMap(({ arg, manifest }) => {
|
|
1513
|
+
if (arg.defaultValue == null) return [];
|
|
1514
|
+
const needsRenderedDefault = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) || arg.defaultValue.kind !== "accountBumpValueNode";
|
|
1515
|
+
if (!needsRenderedDefault) return [];
|
|
1516
|
+
return [
|
|
1517
|
+
[
|
|
1518
|
+
arg,
|
|
1519
|
+
getDartValueFragment(arg.defaultValue, manifest.type.content)
|
|
1520
|
+
]
|
|
1521
|
+
];
|
|
1522
|
+
})
|
|
1523
|
+
);
|
|
1524
|
+
for (const arg of allArgs.filter(
|
|
1525
|
+
(candidate) => isDiscriminatorArg(candidate, node)
|
|
1526
|
+
)) {
|
|
1527
|
+
if (!argDefaultValues.has(arg)) {
|
|
1528
|
+
throw new Error(
|
|
1529
|
+
`Field discriminator "${arg.name}" on instruction "${node.name}" must reference a field with a default value.`
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1186
1533
|
const dataFieldDecls = allArgManifests.map(({ arg, manifest }) => {
|
|
1187
1534
|
const fieldName = camelCase(arg.name);
|
|
1188
1535
|
return ` final ${manifest.type.content} ${fieldName};`;
|
|
1189
1536
|
}).join("\n");
|
|
1190
1537
|
const dataCtorParams = allArgManifests.map(({ arg, manifest }) => {
|
|
1191
1538
|
const fieldName = camelCase(arg.name);
|
|
1539
|
+
if (isOmittedDefaultArgument(arg)) return "";
|
|
1192
1540
|
if (isDiscriminatorArg(arg, node)) {
|
|
1193
|
-
return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${
|
|
1541
|
+
return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${argDefaultValues.get(arg).content},` : ` ${manifest.type.content}? ${fieldName},`;
|
|
1194
1542
|
}
|
|
1195
1543
|
return ` required this.${fieldName},`;
|
|
1196
|
-
}).join("\n");
|
|
1544
|
+
}).filter(Boolean).join("\n");
|
|
1197
1545
|
const dataCtorInitializers = allArgManifests.map(({ arg }) => {
|
|
1198
1546
|
const fieldName = camelCase(arg.name);
|
|
1547
|
+
if (isOmittedDefaultArgument(arg)) {
|
|
1548
|
+
return ` ${fieldName} = ${argDefaultValues.get(arg).content}`;
|
|
1549
|
+
}
|
|
1199
1550
|
if (isDiscriminatorArg(arg, node) && !isConstDefaultValue(arg.defaultValue)) {
|
|
1200
|
-
return ` ${fieldName} = ${fieldName} ?? ${
|
|
1551
|
+
return ` ${fieldName} = ${fieldName} ?? ${argDefaultValues.get(arg).content}`;
|
|
1201
1552
|
}
|
|
1202
1553
|
return null;
|
|
1203
1554
|
}).filter((value) => value !== null).join(",\n");
|
|
@@ -1207,16 +1558,26 @@ function getInstructionPageFragment(node, scope) {
|
|
|
1207
1558
|
const decFields = allArgManifests.map(({ arg, manifest }) => {
|
|
1208
1559
|
return ` ('${arg.name}', ${manifest.decoder.content}),`;
|
|
1209
1560
|
}).join("\n");
|
|
1210
|
-
const toMapFields = allArgs.map(
|
|
1211
|
-
(arg)
|
|
1212
|
-
|
|
1213
|
-
|
|
1561
|
+
const toMapFields = allArgs.map((arg) => {
|
|
1562
|
+
const value = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) ? argDefaultValues.get(arg).content : `value.${camelCase(arg.name)}`;
|
|
1563
|
+
return ` '${arg.name}': ${value},`;
|
|
1564
|
+
}).join("\n");
|
|
1565
|
+
const fromMapFields = allArgManifests.filter(({ arg }) => !isOmittedDefaultArgument(arg)).map(({ arg, manifest }) => {
|
|
1214
1566
|
const typeStr = manifest.type.content;
|
|
1215
1567
|
const isNullable = typeStr.endsWith("?");
|
|
1216
1568
|
const accessor = isNullable ? `map['${arg.name}']` : `map['${arg.name}']!`;
|
|
1217
1569
|
return ` ${camelCase(arg.name)}: ${accessor} as ${typeStr},`;
|
|
1218
1570
|
}).join("\n");
|
|
1219
|
-
const dataCtorKeyword =
|
|
1571
|
+
const dataCtorKeyword = allArgs.every(
|
|
1572
|
+
(arg) => !isOmittedDefaultArgument(arg) || isConstDartValueNode(arg.defaultValue)
|
|
1573
|
+
) && allArgs.every(
|
|
1574
|
+
(arg) => !isDiscriminatorArg(arg, node) || isConstDefaultValue(arg.defaultValue)
|
|
1575
|
+
) ? "const " : "";
|
|
1576
|
+
const dataCtorSignature = dataCtorParams ? `${dataCtorKeyword}${dataClassName}({
|
|
1577
|
+
${dataCtorParams}
|
|
1578
|
+
})${dataCtorInitializers ? ` :
|
|
1579
|
+
${dataCtorInitializers}` : ""};` : `${dataCtorKeyword}${dataClassName}()${dataCtorInitializers ? ` :
|
|
1580
|
+
${dataCtorInitializers}` : ""};`;
|
|
1220
1581
|
const dataEncoderName = `get${typeName}InstructionDataEncoder`;
|
|
1221
1582
|
const dataDecoderName = `get${typeName}InstructionDataDecoder`;
|
|
1222
1583
|
const dataCodecName = `get${typeName}InstructionDataCodec`;
|
|
@@ -1234,7 +1595,7 @@ function getInstructionPageFragment(node, scope) {
|
|
|
1234
1595
|
const manifest = argManifestMap.get(arg);
|
|
1235
1596
|
const fieldName = camelCase(arg.name);
|
|
1236
1597
|
const typeStr = manifest.type.content;
|
|
1237
|
-
const hasDefault = arg.defaultValue != null;
|
|
1598
|
+
const hasDefault = arg.defaultValue != null && arg.defaultValue.kind !== "accountBumpValueNode";
|
|
1238
1599
|
if (hasDefault) {
|
|
1239
1600
|
const nullableType = typeStr.endsWith("?") ? typeStr : `${typeStr}?`;
|
|
1240
1601
|
return ` ${nullableType} ${fieldName},`;
|
|
@@ -1246,18 +1607,33 @@ function getInstructionPageFragment(node, scope) {
|
|
|
1246
1607
|
const role = getAccountRole(acc);
|
|
1247
1608
|
const isOptional = acc.isOptional ?? false;
|
|
1248
1609
|
if (isOptional) {
|
|
1249
|
-
|
|
1610
|
+
if (optionalAccountStrategy === "omitted") {
|
|
1611
|
+
return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
|
|
1612
|
+
}
|
|
1613
|
+
return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}) else AccountMeta(address: ${instrProgramParam}, role: AccountRole.readonly),`;
|
|
1250
1614
|
}
|
|
1251
1615
|
return ` AccountMeta(address: ${fieldName}, role: ${role}),`;
|
|
1252
1616
|
}).join("\n");
|
|
1253
1617
|
const dataConstruction = allArgs.map((arg) => {
|
|
1254
1618
|
const fieldName = camelCase(arg.name);
|
|
1255
|
-
if (isDiscriminatorArg(arg, node)) {
|
|
1619
|
+
if (isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node)) {
|
|
1256
1620
|
return "";
|
|
1257
1621
|
}
|
|
1258
|
-
|
|
1622
|
+
const skipDefault = arg.defaultValue?.kind === "accountBumpValueNode";
|
|
1623
|
+
return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${argDefaultValues.get(arg).content}` : ""},`;
|
|
1259
1624
|
}).filter(Boolean).join("\n");
|
|
1260
1625
|
const discFragment = getDiscriminatorConstantsFragment(node, scope);
|
|
1626
|
+
const discriminatorValidation = getDiscriminatorValidationFragment(
|
|
1627
|
+
node,
|
|
1628
|
+
scope
|
|
1629
|
+
);
|
|
1630
|
+
const topLevelDecoder = getTopLevelDecoderFragment({
|
|
1631
|
+
typeName: dataClassName,
|
|
1632
|
+
description: `${name} instruction decoder`,
|
|
1633
|
+
discriminatorValidation,
|
|
1634
|
+
fromMapFields,
|
|
1635
|
+
requireExactConsumption: true
|
|
1636
|
+
});
|
|
1261
1637
|
const parts = [
|
|
1262
1638
|
fragment`// Auto-generated. Do not edit.
|
|
1263
1639
|
// ignore_for_file: type=lint
|
|
@@ -1281,10 +1657,7 @@ ${use("AccountRole", "solanaInstructions")}`
|
|
|
1281
1657
|
parts.push(fragment`
|
|
1282
1658
|
@immutable
|
|
1283
1659
|
class ${fragmentFromString(dataClassName)} {
|
|
1284
|
-
${fragmentFromString(
|
|
1285
|
-
${fragmentFromString(dataCtorParams)}
|
|
1286
|
-
})${fragmentFromString(dataCtorInitializers ? ` :
|
|
1287
|
-
${dataCtorInitializers}` : "")};
|
|
1660
|
+
${fragmentFromString(dataCtorSignature)}
|
|
1288
1661
|
|
|
1289
1662
|
${fragmentFromString(dataFieldDecls)}
|
|
1290
1663
|
}`);
|
|
@@ -1307,12 +1680,7 @@ Decoder<${fragmentFromString(dataClassName)}> ${fragmentFromString(dataDecoderNa
|
|
|
1307
1680
|
${fragmentFromString(decFields)}
|
|
1308
1681
|
]);
|
|
1309
1682
|
|
|
1310
|
-
|
|
1311
|
-
structDecoder,
|
|
1312
|
-
(Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(dataClassName)}(
|
|
1313
|
-
${fragmentFromString(fromMapFields)}
|
|
1314
|
-
),
|
|
1315
|
-
);
|
|
1683
|
+
${topLevelDecoder}
|
|
1316
1684
|
}
|
|
1317
1685
|
|
|
1318
1686
|
Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}> ${fragmentFromString(dataCodecName)}() {
|
|
@@ -1348,6 +1716,9 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
|
|
|
1348
1716
|
result.imports.mergeWith(manifest.decoder.imports);
|
|
1349
1717
|
result.imports.mergeWith(manifest.type.imports);
|
|
1350
1718
|
}
|
|
1719
|
+
for (const defaultValue of argDefaultValues.values()) {
|
|
1720
|
+
result.imports.mergeWith(defaultValue.imports);
|
|
1721
|
+
}
|
|
1351
1722
|
return result;
|
|
1352
1723
|
}
|
|
1353
1724
|
function getAccountRole(acc) {
|
|
@@ -1364,19 +1735,6 @@ function isDiscriminatorArg(arg, node) {
|
|
|
1364
1735
|
(d) => d.kind === "fieldDiscriminatorNode" && d.name === arg.name
|
|
1365
1736
|
);
|
|
1366
1737
|
}
|
|
1367
|
-
function getDiscriminatorDefault(arg, _node) {
|
|
1368
|
-
if (arg.defaultValue) {
|
|
1369
|
-
if (arg.defaultValue.kind === "numberValueNode") {
|
|
1370
|
-
return String(arg.defaultValue.number);
|
|
1371
|
-
}
|
|
1372
|
-
if (arg.defaultValue.kind === "bytesValueNode") {
|
|
1373
|
-
const data = arg.defaultValue.data;
|
|
1374
|
-
const bytes = data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
|
|
1375
|
-
return `Uint8List.fromList([${bytes.join(", ")}])`;
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
return "0";
|
|
1379
|
-
}
|
|
1380
1738
|
function isConstDefaultValue(defaultValue) {
|
|
1381
1739
|
if (!defaultValue) return false;
|
|
1382
1740
|
switch (defaultValue.kind) {
|
|
@@ -1389,33 +1747,8 @@ function isConstDefaultValue(defaultValue) {
|
|
|
1389
1747
|
return false;
|
|
1390
1748
|
}
|
|
1391
1749
|
}
|
|
1392
|
-
function
|
|
1393
|
-
|
|
1394
|
-
const dv = arg.defaultValue;
|
|
1395
|
-
switch (dv.kind) {
|
|
1396
|
-
case "numberValueNode":
|
|
1397
|
-
return String(dv.number);
|
|
1398
|
-
case "booleanValueNode":
|
|
1399
|
-
return String(dv.boolean);
|
|
1400
|
-
case "stringValueNode":
|
|
1401
|
-
return `'${dv.string}'`;
|
|
1402
|
-
case "publicKeyValueNode": {
|
|
1403
|
-
const wellKnownName = WELL_KNOWN_ADDRESSES.get(dv.publicKey);
|
|
1404
|
-
if (wellKnownName) {
|
|
1405
|
-
use(wellKnownName, "solanaAddresses");
|
|
1406
|
-
return wellKnownName;
|
|
1407
|
-
}
|
|
1408
|
-
return `Address('${dv.publicKey}')`;
|
|
1409
|
-
}
|
|
1410
|
-
case "noneValueNode":
|
|
1411
|
-
return "null";
|
|
1412
|
-
case "bytesValueNode": {
|
|
1413
|
-
const bytes = dv.data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
|
|
1414
|
-
return `Uint8List.fromList([${bytes.join(", ")}])`;
|
|
1415
|
-
}
|
|
1416
|
-
default:
|
|
1417
|
-
return "null";
|
|
1418
|
-
}
|
|
1750
|
+
function isOmittedDefaultArgument(arg) {
|
|
1751
|
+
return arg.defaultValue != null && arg.defaultValueStrategy === "omitted";
|
|
1419
1752
|
}
|
|
1420
1753
|
function getPdaPageFragment(node, scope) {
|
|
1421
1754
|
const name = node.name;
|
|
@@ -1534,8 +1867,6 @@ function bytesValueToDart(hex) {
|
|
|
1534
1867
|
}
|
|
1535
1868
|
return `[${pairs.join(", ")}]`;
|
|
1536
1869
|
}
|
|
1537
|
-
|
|
1538
|
-
// src/fragments/programPage.ts
|
|
1539
1870
|
function getProgramPageFragment(node, scope) {
|
|
1540
1871
|
const name = node.name;
|
|
1541
1872
|
const addressConstName = scope.nameApi.programAddressConstant(name);
|
|
@@ -1567,41 +1898,171 @@ enum ${fragmentFromString(pascalCase(name))}Account {
|
|
|
1567
1898
|
${fragmentFromString(accountVariants)}
|
|
1568
1899
|
}`);
|
|
1569
1900
|
}
|
|
1570
|
-
const instructions = node
|
|
1901
|
+
const instructions = getAllInstructionsWithSubs(node);
|
|
1571
1902
|
if (instructions.length > 0) {
|
|
1903
|
+
const programName = pascalCase(name);
|
|
1904
|
+
const instructionEnum = `${programName}Instruction`;
|
|
1572
1905
|
const instrVariants = instructions.map((instr) => ` ${camelCase(instr.name)},`).join("\n");
|
|
1573
1906
|
parts.push(fragment`
|
|
1574
|
-
/// Known instructions for the ${fragmentFromString(
|
|
1575
|
-
enum ${fragmentFromString(
|
|
1907
|
+
/// Known instructions for the ${fragmentFromString(programName)} program.
|
|
1908
|
+
enum ${fragmentFromString(instructionEnum)} {
|
|
1576
1909
|
${fragmentFromString(instrVariants)}
|
|
1577
1910
|
}`);
|
|
1911
|
+
const identifiableInstructions = instructions.map((instruction) => ({
|
|
1912
|
+
instruction,
|
|
1913
|
+
condition: getInstructionDiscriminatorCondition(instruction, scope)
|
|
1914
|
+
})).filter(({ condition }) => condition !== null);
|
|
1915
|
+
if (identifiableInstructions.length > 0) {
|
|
1916
|
+
const identifyBranches = identifiableInstructions.map(
|
|
1917
|
+
({ instruction, condition }) => fragment` if (${condition}) {
|
|
1918
|
+
return ${fragmentFromString(instructionEnum)}.${fragmentFromString(camelCase(instruction.name))};
|
|
1919
|
+
}`
|
|
1920
|
+
);
|
|
1921
|
+
const parsedBase = `Parsed${programName}Instruction`;
|
|
1922
|
+
const parsedClasses = instructions.map((instruction) => {
|
|
1923
|
+
const instructionName = pascalCase(instruction.name);
|
|
1924
|
+
const variant = camelCase(instruction.name);
|
|
1925
|
+
const parsedClass = `Parsed${instructionName}`;
|
|
1926
|
+
const dataClass = `${instructionName}InstructionData`;
|
|
1927
|
+
return fragment`/// A parsed ${fragmentFromString(instructionName)} instruction.
|
|
1928
|
+
final class ${fragmentFromString(parsedClass)} extends ${fragmentFromString(parsedBase)} {
|
|
1929
|
+
const ${fragmentFromString(parsedClass)}({required this.data})
|
|
1930
|
+
: super(${fragmentFromString(instructionEnum)}.${fragmentFromString(variant)});
|
|
1931
|
+
|
|
1932
|
+
final ${use(dataClass, "../instructions/instructions.dart")} data;
|
|
1933
|
+
}`;
|
|
1934
|
+
});
|
|
1935
|
+
const parseBranches = instructions.map((instruction) => {
|
|
1936
|
+
const instructionName = pascalCase(instruction.name);
|
|
1937
|
+
const variant = camelCase(instruction.name);
|
|
1938
|
+
const parsedClass = `Parsed${instructionName}`;
|
|
1939
|
+
const parseFunction = scope.nameApi.instructionParseFunction(
|
|
1940
|
+
instruction.name
|
|
1941
|
+
);
|
|
1942
|
+
return fragment` ${fragmentFromString(instructionEnum)}.${fragmentFromString(variant)} => ${fragmentFromString(parsedClass)}(
|
|
1943
|
+
data: ${use(parseFunction, "../instructions/instructions.dart")}(instruction),
|
|
1944
|
+
),`;
|
|
1945
|
+
});
|
|
1946
|
+
parts.push(fragment`
|
|
1947
|
+
/// Identifies the type of a ${fragmentFromString(programName)} instruction.
|
|
1948
|
+
${fragmentFromString(instructionEnum)} identify${fragmentFromString(programName)}Instruction(
|
|
1949
|
+
${use("Uint8List", "dartTypedData")} data,
|
|
1950
|
+
) {
|
|
1951
|
+
${mergeFragments(identifyBranches, (cs) => cs.join("\n"))}
|
|
1952
|
+
|
|
1953
|
+
throw ${use("SolanaError", "solanaErrors")}(
|
|
1954
|
+
${use("SolanaErrorCode", "solanaErrors")}.programClientsFailedToIdentifyInstruction,
|
|
1955
|
+
{
|
|
1956
|
+
'instructionData': data,
|
|
1957
|
+
'programName': '${fragmentFromString(name)}',
|
|
1958
|
+
},
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
/// A parsed instruction from the ${fragmentFromString(programName)} program.
|
|
1963
|
+
sealed class ${fragmentFromString(parsedBase)} {
|
|
1964
|
+
const ${fragmentFromString(parsedBase)}(this.instructionType);
|
|
1965
|
+
|
|
1966
|
+
final ${fragmentFromString(instructionEnum)} instructionType;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
${mergeFragments(parsedClasses, (cs) => cs.join("\n\n"))}
|
|
1970
|
+
|
|
1971
|
+
/// Parses a ${fragmentFromString(programName)} instruction.
|
|
1972
|
+
${fragmentFromString(parsedBase)} parse${fragmentFromString(programName)}Instruction(
|
|
1973
|
+
${use("Instruction", "solanaInstructions")} instruction,
|
|
1974
|
+
) {
|
|
1975
|
+
return switch (identify${fragmentFromString(programName)}Instruction(
|
|
1976
|
+
instruction.data ?? Uint8List(0),
|
|
1977
|
+
)) {
|
|
1978
|
+
${mergeFragments(parseBranches, (cs) => cs.join("\n"))}
|
|
1979
|
+
};
|
|
1980
|
+
}`);
|
|
1981
|
+
}
|
|
1578
1982
|
}
|
|
1579
1983
|
return mergeFragments(parts, (cs) => cs.join("\n"));
|
|
1580
1984
|
}
|
|
1985
|
+
function getInstructionDiscriminatorCondition(instruction, scope) {
|
|
1986
|
+
const discriminators = instruction.discriminators ?? [];
|
|
1987
|
+
if (discriminators.length === 0) return null;
|
|
1988
|
+
const conditions = discriminators.map((discriminator) => {
|
|
1989
|
+
switch (discriminator.kind) {
|
|
1990
|
+
case "sizeDiscriminatorNode":
|
|
1991
|
+
return fragment`data.length == ${discriminator.size}`;
|
|
1992
|
+
case "constantDiscriminatorNode": {
|
|
1993
|
+
const manifest = visit(
|
|
1994
|
+
discriminator.constant.type,
|
|
1995
|
+
scope.typeManifestVisitor
|
|
1996
|
+
);
|
|
1997
|
+
const value = getValueFragment(
|
|
1998
|
+
discriminator.constant.value,
|
|
1999
|
+
manifest.type.content
|
|
2000
|
+
);
|
|
2001
|
+
if (value === null) return null;
|
|
2002
|
+
return fragment`${use("containsBytes", "solanaCodecsCore")}(data, ${manifest.encoder}.encode(${value}), ${discriminator.offset})`;
|
|
2003
|
+
}
|
|
2004
|
+
case "fieldDiscriminatorNode": {
|
|
2005
|
+
const argument = (instruction.arguments ?? []).find(
|
|
2006
|
+
(candidate) => candidate.name === discriminator.name
|
|
2007
|
+
);
|
|
2008
|
+
if (argument?.defaultValue == null) return null;
|
|
2009
|
+
const manifest = visit(argument.type, scope.typeManifestVisitor);
|
|
2010
|
+
const value = getValueFragment(
|
|
2011
|
+
argument.defaultValue,
|
|
2012
|
+
manifest.type.content
|
|
2013
|
+
);
|
|
2014
|
+
if (value === null) return null;
|
|
2015
|
+
return fragment`${use("containsBytes", "solanaCodecsCore")}(data, ${manifest.encoder}.encode(${value}), ${discriminator.offset})`;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
if (conditions.some((condition) => condition === null)) return null;
|
|
2020
|
+
return mergeFragments(conditions, (cs) => cs.join(" && "));
|
|
2021
|
+
}
|
|
2022
|
+
function getValueFragment(value, dartType) {
|
|
2023
|
+
switch (value.kind) {
|
|
2024
|
+
case "numberValueNode":
|
|
2025
|
+
return fragmentFromString(
|
|
2026
|
+
dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
|
|
2027
|
+
);
|
|
2028
|
+
case "booleanValueNode":
|
|
2029
|
+
return fragmentFromString(String(value.boolean));
|
|
2030
|
+
case "stringValueNode":
|
|
2031
|
+
return fragmentFromString(`'${value.string.replaceAll("'", "\\'")}'`);
|
|
2032
|
+
case "bytesValueNode": {
|
|
2033
|
+
const clean = value.data.replace(/^0x/, "");
|
|
2034
|
+
const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
|
|
2035
|
+
return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
|
|
2036
|
+
}
|
|
2037
|
+
default:
|
|
2038
|
+
return null;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
1581
2041
|
function getTypePageFragment(node, scope) {
|
|
1582
2042
|
const name = node.name;
|
|
1583
2043
|
scope.nameApi.dataType(name);
|
|
1584
2044
|
const typeNode = node.type;
|
|
1585
2045
|
if (typeNode.kind === "enumTypeNode") {
|
|
1586
|
-
const
|
|
2046
|
+
const variants = typeNode.variants ?? [];
|
|
2047
|
+
const isScalar = variants.every(
|
|
1587
2048
|
(v) => v.kind === "enumEmptyVariantTypeNode"
|
|
1588
2049
|
);
|
|
1589
2050
|
if (isScalar) {
|
|
1590
|
-
return getScalarEnumPageFragment(node, scope);
|
|
2051
|
+
return getScalarEnumPageFragment(node, variants, scope);
|
|
1591
2052
|
}
|
|
1592
|
-
return getDataEnumPageFragment(node, scope);
|
|
2053
|
+
return getDataEnumPageFragment(node, variants, scope);
|
|
1593
2054
|
}
|
|
1594
2055
|
if (typeNode.kind === "structTypeNode") {
|
|
1595
2056
|
return getStructPageFragment(node, scope);
|
|
1596
2057
|
}
|
|
1597
2058
|
return getTypeAliasPageFragment(node, scope);
|
|
1598
2059
|
}
|
|
1599
|
-
function getScalarEnumPageFragment(node, scope) {
|
|
2060
|
+
function getScalarEnumPageFragment(node, enumVariants, scope) {
|
|
1600
2061
|
const name = node.name;
|
|
1601
2062
|
const typeName = scope.nameApi.dataType(name);
|
|
1602
2063
|
const enumNode = node.type;
|
|
1603
2064
|
if (enumNode.kind !== "enumTypeNode") return emptyFragment();
|
|
1604
|
-
const variants =
|
|
2065
|
+
const variants = enumVariants.map((v) => {
|
|
1605
2066
|
const variantName = scope.nameApi.enumVariant(v.name);
|
|
1606
2067
|
return variantName;
|
|
1607
2068
|
});
|
|
@@ -1612,6 +2073,14 @@ function getScalarEnumPageFragment(node, scope) {
|
|
|
1612
2073
|
const resolvedSize = resolveNestedTypeNode(enumNode.size);
|
|
1613
2074
|
const sizeType = resolvedSize.format ?? "u8";
|
|
1614
2075
|
const sizeCodecName = getNumberCodecName2(sizeType);
|
|
2076
|
+
const isU64 = sizeType === "u64";
|
|
2077
|
+
const encodedVariantIndex = isU64 ? "BigInt.from(value.index)" : "value.index";
|
|
2078
|
+
const decodedVariant = isU64 ? `(BigInt value, Uint8List bytes, int offset) {
|
|
2079
|
+
if (value.isNegative || value >= BigInt.from(${typeName}.values.length)) {
|
|
2080
|
+
throw RangeError('Invalid ${typeName} discriminator: ' + value.toString());
|
|
2081
|
+
}
|
|
2082
|
+
return ${typeName}.values[value.toInt()];
|
|
2083
|
+
}` : `(int value, Uint8List bytes, int offset) => ${typeName}.values[value]`;
|
|
1615
2084
|
return fragment`// Auto-generated. Do not edit.
|
|
1616
2085
|
// ignore_for_file: type=lint
|
|
1617
2086
|
|
|
@@ -1632,14 +2101,14 @@ ${fragmentFromString(variantLines)}
|
|
|
1632
2101
|
Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
|
|
1633
2102
|
return transformEncoder(
|
|
1634
2103
|
get${fragmentFromString(sizeCodecName)}Encoder(),
|
|
1635
|
-
(${fragmentFromString(typeName)} value) =>
|
|
2104
|
+
(${fragmentFromString(typeName)} value) => ${fragmentFromString(encodedVariantIndex)},
|
|
1636
2105
|
);
|
|
1637
2106
|
}
|
|
1638
2107
|
|
|
1639
2108
|
Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
|
|
1640
2109
|
return transformDecoder(
|
|
1641
2110
|
get${fragmentFromString(sizeCodecName)}Decoder(),
|
|
1642
|
-
|
|
2111
|
+
${fragmentFromString(decodedVariant)},
|
|
1643
2112
|
);
|
|
1644
2113
|
}
|
|
1645
2114
|
|
|
@@ -1647,7 +2116,7 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
|
|
|
1647
2116
|
return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
|
|
1648
2117
|
}`;
|
|
1649
2118
|
}
|
|
1650
|
-
function getDataEnumPageFragment(node, scope) {
|
|
2119
|
+
function getDataEnumPageFragment(node, variants, scope) {
|
|
1651
2120
|
const name = node.name;
|
|
1652
2121
|
const typeName = scope.nameApi.dataType(name);
|
|
1653
2122
|
const enumNode = node.type;
|
|
@@ -1658,8 +2127,9 @@ function getDataEnumPageFragment(node, scope) {
|
|
|
1658
2127
|
const encodeCases = [];
|
|
1659
2128
|
const decodeCases = [];
|
|
1660
2129
|
const allVariantManifests = [];
|
|
1661
|
-
for (let i = 0; i <
|
|
1662
|
-
const variant =
|
|
2130
|
+
for (let i = 0; i < variants.length; i++) {
|
|
2131
|
+
const variant = variants[i];
|
|
2132
|
+
const variantName = pascalCase(variant.name);
|
|
1663
2133
|
const variantClassName = scope.nameApi.sealedClassVariant(
|
|
1664
2134
|
name,
|
|
1665
2135
|
variant.name
|
|
@@ -1676,7 +2146,7 @@ function getDataEnumPageFragment(node, scope) {
|
|
|
1676
2146
|
int get hashCode => runtimeType.hashCode;
|
|
1677
2147
|
|
|
1678
2148
|
@override
|
|
1679
|
-
String toString() => '${typeName}.${
|
|
2149
|
+
String toString() => '${typeName}.${variantName}()';
|
|
1680
2150
|
}`);
|
|
1681
2151
|
encoderVariants.push(
|
|
1682
2152
|
`(${i}, getStructEncoder(<(String, Encoder<Object?>)>[]))`
|
|
@@ -1688,7 +2158,7 @@ function getDataEnumPageFragment(node, scope) {
|
|
|
1688
2158
|
decodeCases.push(`case ${i}: return const ${variantClassName}();`);
|
|
1689
2159
|
} else if (variant.kind === "enumStructVariantTypeNode") {
|
|
1690
2160
|
const resolvedStruct = resolveNestedTypeNode(variant.struct);
|
|
1691
|
-
const fields = resolvedStruct.fields;
|
|
2161
|
+
const fields = resolvedStruct.fields ?? [];
|
|
1692
2162
|
const fieldManifests = fields.map((f) => ({
|
|
1693
2163
|
field: f,
|
|
1694
2164
|
manifest: visit(f.type, scope.typeManifestVisitor)
|
|
@@ -1723,7 +2193,7 @@ ${fieldDecls}
|
|
|
1723
2193
|
int get hashCode => ${hashExpression};
|
|
1724
2194
|
|
|
1725
2195
|
@override
|
|
1726
|
-
String toString() => '${typeName}.${
|
|
2196
|
+
String toString() => '${typeName}.${variantName}(${toStringFields})';
|
|
1727
2197
|
}`);
|
|
1728
2198
|
const encFields = fieldManifests.map(({ field: f, manifest }) => {
|
|
1729
2199
|
return `('${f.name}', ${manifest.encoder.content})`;
|
|
@@ -1754,7 +2224,7 @@ ${fieldDecls}
|
|
|
1754
2224
|
decodeCases.push(`case ${i}: return ${variantClassName}(${fromMapFields.replace(/\n/g, " ").replace(/,$/, "")});`);
|
|
1755
2225
|
} else if (variant.kind === "enumTupleVariantTypeNode") {
|
|
1756
2226
|
const resolvedTuple = resolveNestedTypeNode(variant.tuple);
|
|
1757
|
-
const items = resolvedTuple.items;
|
|
2227
|
+
const items = resolvedTuple.items ?? [];
|
|
1758
2228
|
if (items.length === 1) {
|
|
1759
2229
|
const manifest = visit(items[0], scope.typeManifestVisitor);
|
|
1760
2230
|
allVariantManifests.push(manifest);
|
|
@@ -1772,7 +2242,7 @@ ${fieldDecls}
|
|
|
1772
2242
|
int get hashCode => value.hashCode;
|
|
1773
2243
|
|
|
1774
2244
|
@override
|
|
1775
|
-
String toString() => '${typeName}.${
|
|
2245
|
+
String toString() => '${typeName}.${variantName}($value)';
|
|
1776
2246
|
}`);
|
|
1777
2247
|
encoderVariants.push(
|
|
1778
2248
|
`(${i}, transformEncoder<${manifest.type.content}, Map<String, Object?>>(${manifest.encoder.content}, (Map<String, Object?> map) => map['value']! as ${manifest.type.content}))`
|
|
@@ -1854,7 +2324,7 @@ function getStructPageFragment(node, scope) {
|
|
|
1854
2324
|
const typeName = scope.nameApi.dataType(name);
|
|
1855
2325
|
const structNode = node.type;
|
|
1856
2326
|
if (structNode.kind !== "structTypeNode") return emptyFragment();
|
|
1857
|
-
const fields = structNode.fields;
|
|
2327
|
+
const fields = structNode.fields ?? [];
|
|
1858
2328
|
const fieldManifests = fields.map((f) => ({
|
|
1859
2329
|
field: f,
|
|
1860
2330
|
manifest: visit(f.type, scope.typeManifestVisitor)
|
|
@@ -2061,7 +2531,7 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2061
2531
|
visitRoot(node, { self }) {
|
|
2062
2532
|
const programMaps = [
|
|
2063
2533
|
visit(node.program, self),
|
|
2064
|
-
...node.additionalPrograms.map((p) => visit(p, self))
|
|
2534
|
+
...(node.additionalPrograms ?? []).map((p) => visit(p, self))
|
|
2065
2535
|
];
|
|
2066
2536
|
const mergedProgramMap = mergeRenderMaps(programMaps);
|
|
2067
2537
|
const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
|
|
@@ -2207,16 +2677,17 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2207
2677
|
dependencyMap
|
|
2208
2678
|
} = options;
|
|
2209
2679
|
return rootNodeVisitor((root) => {
|
|
2680
|
+
const normalizedRoot = normalizeRootNode(root);
|
|
2210
2681
|
if (deleteFolderBeforeRendering && existsSync(outputDir)) {
|
|
2211
2682
|
deleteDirectory(outputDir);
|
|
2212
2683
|
}
|
|
2213
2684
|
const renderMap = visit(
|
|
2214
|
-
|
|
2685
|
+
normalizedRoot,
|
|
2215
2686
|
getRenderMapVisitor({ nameApi, dependencyMap })
|
|
2216
2687
|
);
|
|
2217
2688
|
const typePathMap = {};
|
|
2218
2689
|
for (const renderPath of renderMap.keys()) {
|
|
2219
|
-
const match = renderPath.match(/^(?:.*\/)?types\/([a-
|
|
2690
|
+
const match = renderPath.match(/^(?:.*\/)?types\/([a-z0-9_]+)\.dart$/);
|
|
2220
2691
|
if (match) {
|
|
2221
2692
|
typePathMap[`definedType:${match[1]}`] = renderPath;
|
|
2222
2693
|
}
|
|
@@ -2230,7 +2701,10 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2230
2701
|
const fileDir = dirname(filePath);
|
|
2231
2702
|
const internalMap = {};
|
|
2232
2703
|
for (const [key, typePath] of Object.entries(typePathMap)) {
|
|
2233
|
-
if (typePath === filePath)
|
|
2704
|
+
if (typePath === filePath) {
|
|
2705
|
+
internalMap[key] = "";
|
|
2706
|
+
continue;
|
|
2707
|
+
}
|
|
2234
2708
|
let rel = posix.relative(fileDir, typePath);
|
|
2235
2709
|
if (!rel.startsWith(".")) {
|
|
2236
2710
|
rel = `./${rel}`;
|
|
@@ -2290,6 +2764,6 @@ function cleanFragmentContent(content) {
|
|
|
2290
2764
|
// src/index.ts
|
|
2291
2765
|
var src_default = renderVisitor;
|
|
2292
2766
|
|
|
2293
|
-
export { DART_EXTERNAL_PACKAGE_MAP, DartImportMap, createDartNameApi, src_default as default, getRenderMapVisitor, getTypeManifestVisitor, renderVisitor };
|
|
2767
|
+
export { DART_EXTERNAL_PACKAGE_MAP, DartImportMap, createDartNameApi, src_default as default, getRenderMapVisitor, getTypeManifestVisitor, normalizeRootNode, renderVisitor };
|
|
2294
2768
|
//# sourceMappingURL=index.browser.js.map
|
|
2295
2769
|
//# sourceMappingURL=index.browser.js.map
|