Skip to content

fix(blocks-react): let the build system own declaration freshness - #669

Merged
mobeenabdullah merged 3 commits into
mainfrom
fix/blocks-react-test-build-edge
Aug 11, 2026
Merged

fix(blocks-react): let the build system own declaration freshness#669
mobeenabdullah merged 3 commits into
mainfrom
fix/blocks-react-test-build-edge

Conversation

@mobeenabdullah

@mobeenabdullah mobeenabdullah commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

PRs across every lane have been going red on Lint / Typecheck / Test / Build with

Cannot find module '.../packages/nextly/dist/chunk-QJUKWWCU.mjs'
  imported from '.../packages/nextly/dist/chunk-JSW2R3ME.mjs'

in a package the change never touched, and a different package each run.

Cause

packages/blocks-react/vitest.global-setup.ts ran, during vitest collection:

pnpm exec turbo run build --filter=@nextlyhq/blocks-react

That carries the ^build edge, so it is not one package. --dry=json:

@nextlyhq/adapter-drizzle#build   @nextlyhq/adapter-mysql#build
@nextlyhq/adapter-postgres#build  @nextlyhq/adapter-sqlite#build
@nextlyhq/blocks-engine#build     @nextlyhq/blocks-react#build
@nextlyhq/telemetry#build         nextly#build            (+2 config packages)

nextly is in there because it is a devDependency of blocks-react, and turbo traverses devDependencies. Every one of those bundles with tsup clean: true, so each deletes its dist/ before emitting — while the CI Test step is running seventeen packages' suites concurrently against those same directories.

The failing job's timeline is exactly that:

12:55:50.83  blocks-engine:test          starts
12:55:53.70  plugin-page-builder:test    FAILS (missing nextly/dist/chunk-QJUKWWCU.mjs)
12:55:54.04  telemetry:build, blocks-engine:build, adapter-drizzle:build,
             adapter-mysql:build, adapter-sqlite:build     ← the nested run

It accounts for every property of the failure: a different package each run (whichever was mid-import), never reproducible locally (one suite at a time), and unaffected by any turbo flag on the outer command — the invocation is nested inside vitest, so --only on the Test step does not reach it (measured: that run had 0 replays and failed anyway).

The change

The nested build is removed. The file still refuses to start when the declarations are absent — it just says so instead of building them.

Freshness already belongs to the build system: packages/blocks-react/turbo.json makes test depend on this package's own build and names dist/** among its inputs, so an edit to anything the declarations are emitted from rebuilds and re-runs. Every documented path — pnpm test, turbo run test, CI — arrives through that edge with the artifact current. Building again in global setup was belt-and-braces against a guarantee that already held.

The entries are read from package.json rather than listed, so the check is exactly what the suites resolve and cannot drift as entries are added.

The accepted cost: a bare vitest run on a never-built tree now fails instead of self-healing.

Error: @nextlyhq/blocks-react has no built declarations for
  . (./dist/index.d.ts), ./next (./dist/next.d.ts), ./blocks (./dist/blocks/index.d.ts).
Run `pnpm build --filter @nextlyhq/blocks-react`, or use `pnpm test` /
`turbo run test`, which build first.

One legible failure, once, in place of a nondeterministic one in somebody else's package.

The trade-off this accepts, stated rather than left to be found

existsSync catches an absent dist, never a stale one. A direct vitest run against a tree whose dist predates the source will assert against the stale artifact and can pass wrongly.

That is acceptable because it is unreachable through any documented path: pnpm test, turbo run test and CI all arrive through test → dependsOn: ["build"] with dist/** in inputs, so the artifact is current by construction. It is named here so the next reader does not rediscover it as a defect.

Verification

Removing the build must not blind the suites to a stale dist. Built the package, dropped export { pruneHiddenNodes } from "./visibility"; from src/index.ts, ran without rebuilding:

× exports exactly these values
× re-exports every public value its source modules define
Test Files  1 failed | 1 passed (2)

Which suite caught it is the informative part. entry-surface.test.ts imports the module, so it sees source through Vite's transform and detects the drift. type-surface.test.ts reads the built .d.ts and passed — correctly, since the stale artifact still contained the export. The two fail on different things by design, and the one that can fail here does.

A second mutation — dropping export type { PageRendererProps } from "./page-renderer"; — passed both stale (4/4) and after a rebuild (4/4), which is correct and worth explaining so nobody reads it as the suite going blind. type-surface.test.ts asks one question: does this package re-export every engine type its declarations are written in terms of? engineTypesIn derives that set by matching from "@nextlyhq/blocks-engine" imports, named and namespace forms. PageRendererProps is declared locally in page-renderer.tsx, so it is outside that set by construction and removing it changes nothing the suite asserts, fresh or stale.

The two suites split cleanly: entry-surface covers what this package declares, type-surface covers what it borrows from the engine and owes onward. The value mutation above exercises the first; nothing exercises the second through staleness, which is the trade-off named in the previous section rather than a gap.

Also verified: the missing-dist path produces the message above; turbo run test --filter=@nextlyhq/blocks-react green (15 files, 9 tasks); check-types and lint clean.

Provenance

This finishes the reasoning of the PR that introduced the setup rather than reversing it. That PR concluded in review that build freshness belongs to the build system and removed a per-suite rebuild for exactly that reason — then left the same responsibility in global setup one layer up. Its author reviewed this diagnosis, chose removal over narrowing the filter (--only would cut ten packages to one, but that one is blocks-react, whose dist the downstream suites import, so the race would get rarer and harder to diagnose rather than closed), and asked for the stale-dist control above.

No changeset — test-harness only, nothing published changes.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@mobeenabdullah, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a4be035-5127-482d-9751-60bf1c5cc2a4

📥 Commits

Reviewing files that changed from the base of the PR and between febe69a and d8357b7.

📒 Files selected for processing (3)
  • packages/blocks-react/src/type-surface.test.ts
  • packages/blocks-react/turbo.json
  • packages/blocks-react/vitest.global-setup.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@nextlyhq/adapter-drizzle

npm i https://pkg.pr.new/@nextlyhq/adapter-drizzle@d8357b7

@nextlyhq/adapter-mysql

npm i https://pkg.pr.new/@nextlyhq/adapter-mysql@d8357b7

@nextlyhq/adapter-postgres

npm i https://pkg.pr.new/@nextlyhq/adapter-postgres@d8357b7

@nextlyhq/adapter-sqlite

npm i https://pkg.pr.new/@nextlyhq/adapter-sqlite@d8357b7

@nextlyhq/admin

npm i https://pkg.pr.new/@nextlyhq/admin@d8357b7

@nextlyhq/admin-css

npm i https://pkg.pr.new/@nextlyhq/admin-css@d8357b7

@nextlyhq/blocks-engine

npm i https://pkg.pr.new/@nextlyhq/blocks-engine@d8357b7

@nextlyhq/blocks-react

npm i https://pkg.pr.new/@nextlyhq/blocks-react@d8357b7

@nextlyhq/builder

npm i https://pkg.pr.new/@nextlyhq/builder@d8357b7

create-nextly-app

npm i https://pkg.pr.new/create-nextly-app@d8357b7

nextly

npm i https://pkg.pr.new/nextly@d8357b7

@nextlyhq/plugin-form-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-form-builder@d8357b7

@nextlyhq/plugin-page-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-page-builder@d8357b7

@nextlyhq/plugin-sdk

npm i https://pkg.pr.new/@nextlyhq/plugin-sdk@d8357b7

@nextlyhq/plugin-seo

npm i https://pkg.pr.new/@nextlyhq/plugin-seo@d8357b7

@nextlyhq/storage-s3

npm i https://pkg.pr.new/@nextlyhq/storage-s3@d8357b7

@nextlyhq/storage-uploadthing

npm i https://pkg.pr.new/@nextlyhq/storage-uploadthing@d8357b7

@nextlyhq/storage-vercel-blob

npm i https://pkg.pr.new/@nextlyhq/storage-vercel-blob@d8357b7

@nextlyhq/ui

npm i https://pkg.pr.new/@nextlyhq/ui@d8357b7

commit: d8357b7

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c153c4f0dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/blocks-react/vitest.global-setup.ts Outdated
Comment thread packages/blocks-react/vitest.global-setup.ts Outdated
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4ffe4f201

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/blocks-react/vitest.global-setup.ts
Comment thread packages/blocks-react/vitest.global-setup.ts
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: d8357b7d93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

Independent corroboration from other lanes — four branches, four packages, one shape.

The body argues this from one failing job. Two other sessions have since reported the same signature on branches with no connection to each other or to blocks-react:

branch / PR failing package error
#661 (renderer) @nextlyhq/plugin-sdk Cannot find module .../adapter-drizzle/dist/version-check.mjs
#664 (CI guard) @nextlyhq/plugin-page-builder Cannot find module .../nextly/dist/chunk-5YJCDDRX.mjs
#633 (admin email) @nextlyhq/plugin-seo Cannot find module .../nextly/dist/chunk-RO47PARJ.mjs
#645 (email delivery) @nextlyhq/plugin-form-builder Cannot find package 'nextly/field-catalog'
docs/promote-review-patterns @nextlyhq/plugin-form-builder Cannot find package 'nextly/field-catalog'

The docs-only branch is the strongest single data point. It changes no source, no config and no lockfile, so nothing on it can break module resolution inside a plugin package — yet it produces the same failure. That rules out the change under test as the cause on every one of these.

Every failing package is a dependent resolving into a directory the nested build rebuilds, and which one loses varies run to run. That is the signature of a race, and it matches the mechanism this PR removes: turbo run build --filter=@nextlyhq/blocks-react carries the ^build edge, so it rebuilds ten packages — including nextly and adapter-drizzle — each with tsup clean: true deleting dist/ before emitting, while sibling suites are already importing from them.

Also worth recording: no counter-example has been found. Both sessions were asked specifically for a run showing this signature where no blocks-react suite ran, since that would falsify the diagnosis. Neither has one.

@mobeenabdullah
mobeenabdullah merged commit c975705 into main Aug 11, 2026
19 checks passed
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.

1 participant