Skip to content

Commit 8e32350

Browse files
committed
Sync Cargo.toml when generating code
1 parent e4711fb commit 8e32350

File tree

9 files changed

+739
-40
lines changed

9 files changed

+739
-40
lines changed

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,24 @@
4141
"test:unit": "vitest run"
4242
},
4343
"dependencies": {
44-
"@codama/errors": "^1.4.1",
45-
"@codama/nodes": "^1.4.1",
46-
"@codama/renderers-core": "^1.3.0",
47-
"@codama/visitors-core": "^1.4.1",
44+
"@codama/errors": "^1.4.2",
45+
"@codama/nodes": "^1.4.2",
46+
"@codama/renderers-core": "^1.3.1",
47+
"@codama/visitors-core": "^1.4.2",
48+
"@iarna/toml": "^2.2.5",
4849
"@solana/codecs-strings": "^5.0.0",
49-
"nunjucks": "^3.2.4"
50+
"nunjucks": "^3.2.4",
51+
"semver": "^7.7.3"
5052
},
5153
"devDependencies": {
52-
"@codama/nodes-from-anchor": "^1.3.3",
5354
"@changesets/changelog-github": "^0.5.1",
5455
"@changesets/cli": "^2.29.7",
56+
"@codama/nodes-from-anchor": "^1.3.4",
5557
"@solana/eslint-config-solana": "^5.0.0",
5658
"@solana/prettier-config-solana": "0.0.5",
5759
"@types/node": "^24",
5860
"@types/nunjucks": "^3.2.6",
61+
"@types/semver": "^7.7.1",
5962
"agadoo": "^3.0.0",
6063
"eslint": "^9.35.0",
6164
"happy-dom": "^20.0.0",

pnpm-lock.yaml

Lines changed: 21 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ImportMap.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { TypeManifest } from './getTypeManifestVisitor';
1+
import type { TypeManifest } from './getTypeManifestVisitor';
2+
3+
/** Rust keywords that should not be identified as crate names. */
4+
export const RUST_CORE_IMPORTS = new Set(['std', 'crate', 'super', 'self', 'core', 'alloc', 'clippy']);
25

36
const DEFAULT_MODULE_MAP: Record<string, string> = {
47
generated: 'crate::generated',
@@ -71,6 +74,11 @@ export class ImportMap {
7174
return newImportMap;
7275
}
7376

77+
getExternalDependencies(dependencyMap: Record<string, string>): Set<string> {
78+
const resolvedMap = this.resolveDependencyMap(dependencyMap);
79+
return new Set([...resolvedMap._imports].map(i => i.split('::')[0]).filter(i => !RUST_CORE_IMPORTS.has(i)));
80+
}
81+
7482
toString(dependencies: Record<string, string>): string {
7583
const resolvedMap = this.resolveDependencyMap(dependencies);
7684
const importStatements = [...resolvedMap.imports].map(i => {

src/getRenderMapVisitor.ts

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { getTypeManifestVisitor } from './getTypeManifestVisitor';
3030
import { ImportMap } from './ImportMap';
3131
import { renderValueNode } from './renderValueNodeVisitor';
3232
import {
33+
CargoDependencies,
34+
Fragment,
3335
getDiscriminatorConstants,
3436
getImportFromFactory,
3537
getTraitsFromNodeFactory,
@@ -42,6 +44,7 @@ export type GetRenderMapOptions = {
4244
anchorTraits?: boolean;
4345
defaultTraitOverrides?: string[];
4446
dependencyMap?: Record<string, string>;
47+
dependencyVersions?: CargoDependencies;
4548
linkOverrides?: LinkOverrides;
4649
renderParentInstructions?: boolean;
4750
traitOptions?: TraitOptions;
@@ -64,7 +67,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
6467
const anchorTraits = options.anchorTraits ?? true;
6568

6669
return pipe(
67-
staticVisitor(() => createRenderMap(), {
70+
staticVisitor(() => createRenderMap<Fragment>(), {
6871
keys: ['rootNode', 'programNode', 'instructionNode', 'accountNode', 'definedTypeNode'],
6972
}),
7073
v =>
@@ -107,11 +110,10 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
107110
.filter(isNodeFilter('constantPdaSeedNode'))
108111
.filter(seed => !isNode(seed.value, 'programIdValueNode'));
109112

110-
const { imports } = typeManifest;
111-
112-
if (hasVariableSeeds) {
113-
imports.mergeWith(seedsImports);
114-
}
113+
const imports = typeManifest.imports
114+
.mergeWith(...(hasVariableSeeds ? [seedsImports] : []))
115+
.mergeWith(discriminatorConstants.imports)
116+
.remove(`generatedAccounts::${pascalCase(node.name)}`);
115117

116118
return createRenderMap(`accounts/${snakeCase(node.name)}.rs`, {
117119
content: render('accountsPage.njk', {
@@ -120,28 +122,29 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
120122
constantSeeds,
121123
discriminatorConstants: discriminatorConstants.render,
122124
hasVariableSeeds,
123-
imports: imports
124-
.mergeWith(discriminatorConstants.imports)
125-
.remove(`generatedAccounts::${pascalCase(node.name)}`)
126-
.toString(dependencyMap),
125+
imports: imports.toString(dependencyMap),
127126
pda,
128127
program,
129128
seeds,
130129
typeManifest,
131130
}),
131+
imports,
132132
});
133133
},
134134

135135
visitDefinedType(node) {
136136
const typeManifest = visit(node, typeManifestVisitor);
137-
const imports = new ImportMap().mergeWithManifest(typeManifest);
137+
const imports = new ImportMap()
138+
.mergeWithManifest(typeManifest)
139+
.remove(`generatedTypes::${pascalCase(node.name)}`);
138140

139141
return createRenderMap(`types/${snakeCase(node.name)}.rs`, {
140142
content: render('definedTypesPage.njk', {
141143
definedType: node,
142-
imports: imports.remove(`generatedTypes::${pascalCase(node.name)}`).toString(dependencyMap),
144+
imports: imports.toString(dependencyMap),
143145
typeManifest,
144146
}),
147+
imports,
145148
});
146149
},
147150

@@ -231,23 +234,24 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
231234
const typeManifest = visit(struct, structVisitor);
232235

233236
const dataTraits = getTraitsFromNode(node);
234-
imports.mergeWith(dataTraits.imports);
237+
imports
238+
.mergeWith(dataTraits.imports)
239+
.mergeWith(discriminatorConstants.imports)
240+
.remove(`generatedInstructions::${pascalCase(node.name)}`);
235241

236242
return createRenderMap(`instructions/${snakeCase(node.name)}.rs`, {
237243
content: render('instructionsPage.njk', {
238244
dataTraits: dataTraits.render,
239245
discriminatorConstants: discriminatorConstants.render,
240246
hasArgs,
241247
hasOptional,
242-
imports: imports
243-
.mergeWith(discriminatorConstants.imports)
244-
.remove(`generatedInstructions::${pascalCase(node.name)}`)
245-
.toString(dependencyMap),
248+
imports: imports.toString(dependencyMap),
246249
instruction: node,
247250
instructionArgs,
248251
program,
249252
typeManifest,
250253
}),
254+
imports,
251255
});
252256
},
253257

@@ -269,6 +273,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
269273
imports: new ImportMap().toString(dependencyMap),
270274
program: node,
271275
}),
276+
imports: new ImportMap(),
272277
});
273278
}
274279

@@ -301,21 +306,29 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
301306
return mergeRenderMaps([
302307
createRenderMap({
303308
['accounts/mod.rs']:
304-
accountsToExport.length > 0 ? { content: render('accountsMod.njk', ctx) } : undefined,
309+
accountsToExport.length > 0
310+
? { content: render('accountsMod.njk', ctx), imports: new ImportMap() }
311+
: undefined,
305312
['errors/mod.rs']:
306-
programsToExport.length > 0 ? { content: render('errorsMod.njk', ctx) } : undefined,
313+
programsToExport.length > 0
314+
? { content: render('errorsMod.njk', ctx), imports: new ImportMap() }
315+
: undefined,
307316
['instructions/mod.rs']:
308317
instructionsToExport.length > 0
309-
? { content: render('instructionsMod.njk', ctx) }
318+
? { content: render('instructionsMod.njk', ctx), imports: new ImportMap() }
310319
: undefined,
311-
['mod.rs']: { content: render('rootMod.njk', ctx) },
320+
['mod.rs']: { content: render('rootMod.njk', ctx), imports: new ImportMap() },
312321
['programs.rs']:
313-
programsToExport.length > 0 ? { content: render('programsMod.njk', ctx) } : undefined,
322+
programsToExport.length > 0
323+
? { content: render('programsMod.njk', ctx), imports: new ImportMap() }
324+
: undefined,
314325
['shared.rs']:
315-
accountsToExport.length > 0 ? { content: render('sharedPage.njk', ctx) } : undefined,
326+
accountsToExport.length > 0
327+
? { content: render('sharedPage.njk', ctx), imports: new ImportMap() }
328+
: undefined,
316329
['types/mod.rs']:
317330
definedTypesToExport.length > 0
318-
? { content: render('definedTypesMod.njk', ctx) }
331+
? { content: render('definedTypesMod.njk', ctx), imports: new ImportMap() }
319332
: undefined,
320333
}),
321334
...getAllPrograms(node).map(p => visit(p, self)),

src/renderVisitor.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { logError, logWarn } from '@codama/errors';
2-
import { deleteDirectory, writeRenderMapVisitor } from '@codama/renderers-core';
2+
import { deleteDirectory, writeRenderMap } from '@codama/renderers-core';
33
import { rootNodeVisitor, visit } from '@codama/visitors-core';
44
import { spawnSync } from 'child_process';
55

66
import { GetRenderMapOptions, getRenderMapVisitor } from './getRenderMapVisitor';
7+
import { syncCargoToml } from './utils';
78

89
export type RenderOptions = GetRenderMapOptions & {
910
crateFolder?: string;
1011
deleteFolderBeforeRendering?: boolean;
1112
formatCode?: boolean;
13+
syncCargoToml?: boolean;
1214
toolchain?: string;
1315
};
1416

@@ -20,7 +22,11 @@ export function renderVisitor(path: string, options: RenderOptions = {}) {
2022
}
2123

2224
// Render the new files.
23-
visit(root, writeRenderMapVisitor(getRenderMapVisitor(options), path));
25+
const renderMap = visit(root, getRenderMapVisitor(options));
26+
writeRenderMap(renderMap, path);
27+
28+
// Sync Cargo.toml dependencies and versions, if requested.
29+
syncCargoToml(renderMap, options);
2430

2531
// format the code
2632
if (options.formatCode) {

0 commit comments

Comments
 (0)