Skip to content

feat(cli): add additionalIdls config option - #1018

Merged
lorisleiva merged 5 commits into
codama-idl:mainfrom
plutohan:plutohan/issue-983-additional-idls
Jul 20, 2026
Merged

feat(cli): add additionalIdls config option#1018
lorisleiva merged 5 commits into
codama-idl:mainfrom
plutohan:plutohan/issue-983-additional-idls

Conversation

@plutohan

@plutohan plutohan commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Closes #983.

Implements the additionalIdls approach from your comment @lorisleiva: the config can list additional Anchor IDLs, and their programs get merged into the root node's additionalPrograms, so one codama.json generates clients for every program in an Anchor workspace.

Includes a README entry, a test, and a changeset.

@changeset-bot

changeset-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c6c170d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@codama/cli Minor
codama Patch
@codama/dynamic-address-resolution Patch
@codama/dynamic-client Patch
@codama/dynamic-instructions Patch
@codama/errors Patch
@codama/node-types Patch
@codama/nodes Patch
@codama/validators Patch
@codama/visitors-core Patch
@codama/visitors Patch
@codama/dynamic-codecs Patch
@codama/dynamic-parsers Patch
@codama/fragments Patch
@codama/nodes-from-anchor Patch
@codama/renderers-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/cli/src/parsedConfig.ts Outdated
additionalPrograms.push(...getAllPrograms(additionalRootNode));
}

return rootNode(mainRootNode.program, additionalPrograms);

@mikhd mikhd Jul 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the version could be lost here. (worth checking other fields as well)

getRootNodeFromIdl returns a RootNode one of two ways:

  • If the IDL already is a Codama root node, it returns it as-is, preserving the whatever specified version.
  • Otherwise it's an Anchor IDL, and rootNodeFromAnchor(idl) builds a root node via the rootNode().

const parsed = await getParsedConfig({});

expect(parsed.rootNode.program.name).toBe('main');
expect(parsed.rootNode.additionalPrograms.map(p => p.name)).toEqual(['a', 'b']);

@mikhd mikhd Jul 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would double check that version and other fields are preserved after additionalPrograms merge.
Thank you!

@plutohan

plutohan commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Good catch, the merge rebuilt the root node and reset version and standard. It now spreads the main root node instead, with test assertions for both.

@lorisleiva lorisleiva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I like the idea! I've just made a comment on the execution of this idea to make it more async.

Comment thread packages/cli/src/parsedConfig.ts Outdated
Comment on lines +49 to +50
const mainRootNode = await getRootNodeFromIdl(idlContent);
const rootNode = await mergeAdditionalIdls(mainRootNode, config.additionalIdls ?? [], configPath);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather we implemented this as:

  1. Promise.all await all conversions from IDL string to RootNode such that you end up with an array of RootNodes where the first one is the main one. That way all async operations are parallel.
  2. Then have a mergeRootNodes helper function that destructs [head, ...tail] and adds all programs inside tail to the additional programs of head.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — conversions now run in parallel via Promise.all and a mergeRootNodes helper merges [head, ...tail]. Thanks!

Comment thread packages/cli/src/parsedConfig.ts Outdated
Comment on lines +48 to +49
const idlContent = await importModuleItem({ identifier: 'IDL', from: idlPath });
const rootNode = await getRootNodeFromIdl(idlContent);
const rootNode = await mergeAdditionalIdls(idlContent, config.additionalIdls ?? [], configPath);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, much better. I still think it would be nice if all RootNodes were recovered in the same way. Here the main RootNode has its own importModuleItem whereas the others at in the parallel execution. Do you know what I mean?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — the main IDL now goes through the same loadIdl helper as the others, so all root nodes are recovered identically inside the same Promise.all.

@lorisleiva lorisleiva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I've just made a few final nit comments but I'm happy with the implementation. 🍺

Comment thread packages/cli/README.md Outdated
Comment thread packages/cli/test/parsedConfig.test.ts Outdated
Comment thread packages/cli/test/parsedConfig.test.ts Outdated
const visitors = (config.before ?? []).map((v, i) => parseVisitorConfig(v, configPath, i, null));

return { configPath, idlContent, idlPath, rootNode, scripts, before: visitors };
return { configPath, idlContent: mainIdl.content, idlPath, rootNode, scripts, before: visitors };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out-of-scope for this PR: I don't actually think idlContent is being used. Perhaps we should consider removing it altogether or making it match the config by adding a additionalIdlContents?

@plutohan

Copy link
Copy Markdown
Contributor Author

Addressed the nits: dropped Anchor from the additionalIdls description (kept the Anchor workspace example) and swapped the test node casts for the programNode/rootNode factories so future node changes fail the typecheck.

idlContent is indeed unused now. Rather than widen this PR, I'll open a follow-up to either remove it or add additionalIdlContents to match the config. Let me know which you'd prefer.

@lorisleiva lorisleiva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thank you!

@lorisleiva
lorisleiva merged commit d3e7f01 into codama-idl:main Jul 20, 2026
4 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[cli] Add additionalIdls to codama.json config file

4 participants