Skip to content

fix(richtext): read element types from interfaces and type aliases - #747

Merged
maoberlehner merged 3 commits into
mainfrom
fix/richtext-element-types-type-aliases
Aug 12, 2026
Merged

fix(richtext): read element types from interfaces and type aliases#747
maoberlehner merged 3 commits into
mainfrom
fix/richtext-element-types-type-aliases

Conversation

@maoberlehner

@maoberlehner maoberlehner commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Running pnpm nx run-many -t generate:openapi on main today silently empties StoryblokRichTextElementByType and breaks @storyblok/angular:build:

src/lib/richtext/richtext.feature.ts:20:3 - error TS2536: Type 'T' cannot be used to index type
'StoryblokRichTextElementByType<StoryblokAngularRichTextRenderContext>'.

Three pieces of drift combine into it:

  1. @hey-api/openapi-ts was declared as ^0.92.3. It is a 0.x package whose emitted declaration style is not configurable and changes across patch releases: earlier releases emit export interface X { … }, the 0.92.4 the lockfile resolves emits export type X = { … }.
  2. packages/{cli,richtext}/src/generated/overlay/types.gen.ts is committed output produced under the older style. Since CI never regenerates (it needs the private spec cache), nothing re-derived it when the resolved version moved.
  3. packages/richtext/src/static/generate/richtext-element-types.ts matches ts.isInterfaceDeclaration only. After a regeneration it finds zero RichTextFieldValue* declarations, writes export interface StoryblokRichTextElementByType<TContext = unknown> {}, and exits 0.

Nothing catches this: the generator succeeds, and the failure surfaces in a different package as a type error.

Changes

  • The generator accepts both interface and type-alias object declarations. The two are equivalent for this parser's purposes, so matching either keeps us off the treadmill of tracking the generator's current style.
  • It throws when no declaration matches, instead of emitting an empty map, so the next generator style change fails at generation time rather than in a consumer build.
  • generateElementTypes(path) delegates to generateElementTypesFromSource(source), so the parser is unit-testable; added richtext-element-types.test.ts covering both shapes, the export/name filters, and the empty-output guard.
  • Pinned @hey-api/openapi-ts to an exact 0.92.4, matching how tools/openapi-codegen already pins its other output-affecting tools (typescript, oxlint). A floating range let a plain pnpm install silently change what the next regeneration produces; now that can only happen in a deliberate bump PR where regenerating and reviewing the diff is the expected work. tools/openapi-codegen/README.md documents the generator version as a third input to generation, alongside the two spec sources.
  • Regenerated every consumer, which also picks up the template-derived types that the repo-wide oxfmt pass could not reach (.prettierignore excludes **/src/generated/).

Verification

pnpm --filter @storyblok/openapi-codegen verify passes, and a full pnpm nx run-many -t generate:openapi --skip-nx-cache across all 7 consumers leaves the tree clean — no drift left. pnpm nx build @storyblok/angular (the original TS2536 failure) passes, as do lint and the richtext test suite.

Note for reviewers

The types.gen.ts diff is large but mechanical: interface X {type X = { plus indentation. #719 previously carried the template-derived part of this regeneration; once this lands, that PR's regeneration commit reduces to its own template change.

`@hey-api/openapi-ts` emits object schemas as `export interface X { … }` in the
version that produced the committed `types.gen.ts` and as `export type X = { … }`
in the version the lockfile now resolves. The element-types generator matched
`ts.isInterfaceDeclaration` only, so regenerating the overlay types made it find
zero `RichTextFieldValue*` declarations and write an empty
`StoryblokRichTextElementByType`, with a zero exit code. The breakage only
surfaced later and elsewhere, as `TS2536` in `@storyblok/angular`, which indexes
that interface by element name.

Accept both declaration shapes, and throw when no declarations match instead of
emitting an empty map, so a future generator change fails at generation time.

`generateElementTypes` now delegates to `generateElementTypesFromSource`, which
takes the source text, so the parser is testable without touching the filesystem.
The committed output had drifted from what the generator produces today, in two
independent ways, and nothing in CI can catch it because CI never regenerates.

- The spec-derived overlay types (`packages/{cli,richtext}/src/generated/overlay/
  types.gen.ts`) predate the `@hey-api/openapi-ts` version the lockfile resolves,
  so their object schemas were still emitted as interfaces rather than type
  aliases. No type-level change, but regenerating them was the trigger for the
  empty element types fixed in the previous commit.
- The template-derived types (`packages/*/src/generated/types/*`) predate the
  repo-wide oxfmt pass. That pass reformatted `tools/openapi-codegen/templates/`
  but could not reformat the consumers' copies, which `.prettierignore` excludes.

Regenerating brings both back in sync. `packages/richtext/src/static/
richtext-element-types.generated.ts` regenerates byte-identical to its committed
content, which confirms the generator fix restores the previous output rather
than changing the public surface.
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Cross-package changes detected

This PR is titled as a fix(richtext) commit but modifies files across 6 packages:

  • packages/capi-client/
  • packages/cli/
  • packages/live-preview/
  • packages/mapi-client/
  • packages/richtext/
  • packages/schema/

When this PR is squash-merged, all changes become a single fix(richtext) commit.
Nx release uses file changes (not the commit scope) to determine which packages to bump,
so every package listed above will get a version bump on the next release.

If the changes to other packages are cosmetic (README fixes, dependency bumps, config cleanup),
please split them into a separate PR with a chore: title. chore commits don't trigger version bumps.

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@storyblok/angular

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/angular@747

@storyblok/astro

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/astro@747

@storyblok/api-client

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/api-client@747

storyblok

npm i https://pkg.pr.new/storyblok/monoblok/storyblok@747

@storyblok/experiments

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/experiments@747

@storyblok/js

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/js@747

storyblok-js-client

npm i https://pkg.pr.new/storyblok/monoblok/storyblok-js-client@747

@storyblok/lint-config

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/lint-config@747

@storyblok/live-preview

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/live-preview@747

@storyblok/management-api-client

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/management-api-client@747

@storyblok/migrations

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/migrations@747

@storyblok/nuxt

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/nuxt@747

@storyblok/react

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/react@747

@storyblok/region-helper

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/region-helper@747

@storyblok/richtext

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/richtext@747

@storyblok/schema

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/schema@747

@storyblok/svelte

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/svelte@747

@storyblok/vue

npm i https://pkg.pr.new/storyblok/monoblok/@storyblok/vue@747

commit: cbfc873

`@hey-api/openapi-ts` is a 0.x package whose emitted declaration style is not
configurable and changes across patch releases (0.92.4 emits `export type X =
{ … }` where earlier releases emitted `export interface X { … }`). The
generated output is committed and CI never regenerates it, so a floating
`^0.92.3` let a plain `pnpm install` silently change what the next
regeneration would produce — which is how the richtext element-type map ended
up empty.

Pin it exactly, matching the existing convention in that package for
output-affecting tools (`typescript`, `oxlint`), and document the generator
version as the third input to generation alongside the two spec sources.

Also reword the dual-shape comment in the richtext generator: it read as if we
support several hey-api versions at once, which we do not and should not.
@maoberlehner
maoberlehner merged commit af06293 into main Aug 12, 2026
13 checks passed
@maoberlehner
maoberlehner deleted the fix/richtext-element-types-type-aliases branch August 12, 2026 07:40
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