Skip to content

Introduce @codama-internal/spec-generators and regenerate @codama/node-types#995

Open
lorisleiva wants to merge 1 commit into
05-08-introduce_codama_fragments_with_javascript_and_rust_subpathsfrom
05-08-introduce_codama-internal_spec-generators_and_regenerate_codama_node-types_
Open

Introduce @codama-internal/spec-generators and regenerate @codama/node-types#995
lorisleiva wants to merge 1 commit into
05-08-introduce_codama_fragments_with_javascript_and_rust_subpathsfrom
05-08-introduce_codama-internal_spec-generators_and_regenerate_codama_node-types_

Conversation

@lorisleiva
Copy link
Copy Markdown
Member

Introduces the private @codama-internal/spec-generators workspace package and uses it to fully regenerate the @codama/node-types source surface from the encoded @codama/spec description.

The new generator package houses every code generator the monorepo needs behind a single orchestrator entrypoint. Each generator is self-contained — it knows which spec major it targets, which output directory it writes to, and which compatibility knobs it needs — and exposes a no-argument generate() from its index.ts. The orchestrator at src/index.ts runs every registered generator sequentially. This first cut ships only the gen-ts-node-types generator, ported from the spec repo with imports rewritten to consume @codama/fragments/javascript; the v1 driver supplies the legacy compatibility tables (narrowableDataAttributes, genericParamOrder) needed to keep the existing public type surface stable. Adding a future generator (migrators, JSON schema, …) is a matter of dropping a folder under src/, exposing its own generate(), and wiring it into the orchestrator.

The regenerated @codama/node-types/src/ replaces the hand-maintained interfaces wholesale: the generator wipes its target generated/ directory before writing, so stale files cannot survive. Hand-written content lives at the top of src/ as siblings of generated/, never inside it. Two compatibility shims are added there — Docs = Array<string> and ProgramVersion = Version — to keep @codama/nodes and @codama/nodes-from-anchor compiling against the slimmer generated surface; both can be removed in a future major if desired.

Array fields are emitted as Array<T> rather than T[] so an inline-union element type like array(literalUnion(true, false)) doesn't need extra parentheses to preserve precedence with |. The shape changes that fall out of regeneration are documented in the accompanying changeset.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 11, 2026

🦋 Changeset detected

Latest commit: 617f50a

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

This PR includes changesets to release 14 packages
Name Type
@codama/node-types Minor
@codama/nodes-from-anchor Patch
@codama/errors Minor
@codama/nodes Minor
@codama/dynamic-client Patch
@codama/dynamic-codecs Patch
@codama/dynamic-parsers Patch
@codama/fragments Patch
codama Minor
@codama/renderers-core Patch
@codama/validators Minor
@codama/visitors-core Minor
@codama/visitors Minor
@codama/cli 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

Copy link
Copy Markdown
Member Author

lorisleiva commented May 11, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@lorisleiva lorisleiva force-pushed the 05-08-introduce_codama_fragments_with_javascript_and_rust_subpaths branch from a9c3d19 to b4b8197 Compare May 11, 2026 16:11
@lorisleiva lorisleiva force-pushed the 05-08-introduce_codama-internal_spec-generators_and_regenerate_codama_node-types_ branch from 874f20c to 436acc9 Compare May 11, 2026 16:11
@lorisleiva lorisleiva marked this pull request as ready for review May 12, 2026 08:24
@lorisleiva
Copy link
Copy Markdown
Member Author

@trevor-cortex

Copy link
Copy Markdown

@trevor-cortex trevor-cortex left a comment

Choose a reason for hiding this comment

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

Summary

This is a substantial, well-executed refactor that:

  1. Introduces @codama-internal/spec-generators — a private build-time package that drives codegen from @codama/spec.
  2. Regenerates the entire @codama/node-types/src/ surface (~60 files) under src/generated/, with hand-written static helpers (brands.ts, Docs.ts, Version.ts, ProgramVersion.ts) sitting as siblings outside generated/.
  3. Wires pnpm generate at the repo root to invoke the orchestrator, which wipes generated/ and rewrites it deterministically.

The generator architecture is sound: renderers are layout-agnostic (they emit use(...) calls keyed by symbolic strings like 'node:numberTypeNode'), and a single RenderScope resolves those keys to file locations at write time. The v1 driver supplies legacy compatibility tables (narrowableDataAttributes for NumberTypeNode<'u32'>/StringTypeNode<'utf8'> narrowing, genericParamOrder to preserve positional generic args). Both are cross-checked against the spec at startup so drift fails loudly.

The test coverage in packages/spec-generators/test/nodeTypes/ is genuinely thorough — each renderer has focused unit tests, and generate.test.ts exercises the orchestrator end-to-end against the real spec.

Key things to watch

1. Semver — is minor the right bump for @codama/node-types?

The changeset marks @codama/node-types as a minor bump, but a few of the shape changes are arguably breaking for strict-mode consumers on a 1.x package:

  • programNode.version: stringVersion (`${number}.${number}.${number}`): any consumer literal version: '0.1' (two segments) or version: someString (without a cast) will now fail typecheck. The PR already had to update nodes-from-anchor to cast through Version; downstream packages may need the same treatment.
  • accountNode.size: number | nullnumber | undefined: anyone explicitly passing or destructuring null here will break.
  • instructionAccountNode.isSigner: true | false | 'either'boolean | 'either': structurally identical at runtime, but discriminated-union pattern matching on the literal true/false arms (e.g. exhaustive switches) may stop narrowing the way it did.

None of these are deal-breakers and the changeset explains each, but given the package is at 1.6.0 (not pre-1.0), it's worth a conscious decision rather than a defaulted one. If the policy is "pre-2.0 minors can shape-break", that's fine — just want to make sure that's the call being made.

2. Node union restructuring

The new Node.ts re-expresses Node as the union of the seven Registered<Category>Node aliases plus the top-level nodes (AccountNode, RootNode, etc.). Structurally this should be assignment-compatible with the previous flat union, but it does mean tooling that introspects the union (e.g. via conditional types) now sees an extra layer of aliases. Worth flagging to anyone maintaining downstream visitors.

The new GetNodeFromKind<TKind> helper is a nice addition and the right place to encourage as the extension point.

3. Generator self-validation is well-scoped

validateGenerateOptions cross-checks narrowableDataAttributes and genericParamOrder against the live spec, and renderNode additionally asserts the override's set equals the lifted set exactly. That's the right defensive posture — it catches the case where a future spec rev drops/renames an attribute the v1 driver still expects, instead of silently emitting a stale shape. Good design.

4. Spec dependency pinning

@codama/spec is pinned to the exact 1.6.0-rc.4 rather than a range. That's intentional given how tightly coupled the generator is to the spec encoding, but it means every spec bump will require a coordinated change here. No action needed; just calling it out.

Notes for subsequent reviewers

  • Verify the regenerated surface diffs cleanly against a fresh pnpm generate run on the merge commit. The whole point of this refactor is that generated/ is reproducible from the spec; the easiest smoke test is to delete the directory and re-run the script.
  • Check that downstream packages (@codama/nodes, @codama/nodes-from-anchor, @codama/visitors-core, renderers) all still compile against the new surface. Spot-checks of accountNode, instructionNode, pdaValueNode, and instructionArgumentNode constructors show that the legacy positional generics still resolve thanks to GENERIC_PARAM_ORDER keeping new generics appended at the end. The unchanged packages/validators/test/getValidationItemsVisitor.test.ts ts-expect-error comment was updated to mention Version instead of ProgramVersion — good sign that types still flow through.
  • The tsconfig.declarations.json include widened from ["src/index.ts", "src/types"] to ["src"]. The old src/types path didn't exist, so this is a fix; the broader scope now correctly pulls in src/generated/ for .d.ts emission. Worth a sanity check on the published .d.ts shape after build.
  • packages/spec-generators/src/nodeTypes/renderNodeFile.ts uses mergeFragments(memberRefs, () => { const sorted = [...memberRefs].sort(...); ... }) — the merge callback ignores its parts argument and re-reads memberRefs from the outer scope. Functionally fine, but a touch surprising; if a future refactor changes how mergeFragments composes inputs, this site won't notice. Minor.
  • One small architectural note: packages/spec-generators/src/nodeTypes/index.ts mixes the orchestrator concern (the exported generate() and generateNodeTypes()) with the v1 driver tables (NARROWABLE_DATA_ATTRIBUTES, GENERIC_PARAM_ORDER). The README says future spec majors will get their own drivers; when v2 lands, you'll likely want to extract a v1.ts (or similar) so the orchestrator stays version-agnostic. Not blocking — easy to do later — but worth keeping in mind so the seam is clean when it matters.

Overall this looks like a careful, well-tested foundation for spec-driven codegen across the monorepo. Nice work.

@lorisleiva lorisleiva force-pushed the 05-08-introduce_codama-internal_spec-generators_and_regenerate_codama_node-types_ branch from 436acc9 to 617f50a Compare May 13, 2026 13:36
…node-types`

Introduces the private `@codama-internal/spec-generators` workspace package and uses it to fully regenerate the `@codama/node-types` source surface from the encoded `@codama/spec` description.

The new generator package houses every code generator the monorepo needs behind a single orchestrator entrypoint. Each generator is self-contained — it knows which spec major it targets, which output directory it writes to, and which compatibility knobs it needs — and exposes a no-argument `generate()` from its `index.ts`. The orchestrator at `src/index.ts` runs every registered generator sequentially. This first cut ships only the gen-ts-node-types generator, ported from the spec repo with imports rewritten to consume `@codama/fragments/javascript`; the v1 driver supplies the legacy compatibility tables (`narrowableDataAttributes`, `genericParamOrder`) needed to keep the existing public type surface stable. Adding a future generator (migrators, JSON schema, …) is a matter of dropping a folder under `src/`, exposing its own `generate()`, and wiring it into the orchestrator.

The regenerated `@codama/node-types/src/` replaces the hand-maintained interfaces wholesale: the generator wipes its target `generated/` directory before writing, so stale files cannot survive. Hand-written content lives at the top of `src/` as siblings of `generated/`, never inside it. Two compatibility shims are added there — `Docs = Array<string>` and `ProgramVersion = Version` — to keep `@codama/nodes` and `@codama/nodes-from-anchor` compiling against the slimmer generated surface; both can be removed in a future major if desired.

Array fields are emitted as `Array<T>` rather than `T[]` so an inline-union element type like `array(literalUnion(true, false))` doesn't need extra parentheses to preserve precedence with `|`. The shape changes that fall out of regeneration are documented in the accompanying changeset.
@lorisleiva lorisleiva force-pushed the 05-08-introduce_codama_fragments_with_javascript_and_rust_subpaths branch from b4b8197 to 568e19d Compare May 13, 2026 18:28
@lorisleiva lorisleiva force-pushed the 05-08-introduce_codama-internal_spec-generators_and_regenerate_codama_node-types_ branch from 617f50a to 2571810 Compare May 13, 2026 18:28
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.

2 participants