Skip to content

fix(examples): render the dynamic example on @dynamic-labs v5 - #851

Merged
chybisov merged 3 commits into
mainfrom
fix/dynamic-example-v5
Aug 21, 2026
Merged

fix(examples): render the dynamic example on @dynamic-labs v5#851
chybisov merged 3 commits into
mainfrom
fix/dynamic-example-v5

Conversation

@chybisov

@chybisov chybisov commented Aug 21, 2026

Copy link
Copy Markdown
Member

Which Linear task is linked to this PR?

Follow-up to #847 — no Linear task.

Why was it implemented this way?

#847 bumped @dynamic-labs/* from 4.92.3 to 5.3.1, but examples/dynamic was
status: broken in e2e/examples.json, so the harness skipped it and the v5 major was never
render-verified. This makes the example work and returns it to the active set — 18 examples.

What was actually broken

The old brokenReason blamed vite-plugin-env-compatible and prescribed swapping in
vite-plugin-node-polyfills. The diagnosis was right: vite-plugin-env-compatible only
substitutes process.env.* at build time, while the Dynamic SDK reads a bare process global
and its own polyfills.js imports buffer/index.js. Confirmed from the built bundle:

process.nextTick   process.versions.node   process.emit   process.version

But the prescribed fix no longer works on its own. vite-plugin-node-polyfills advertises a
Vite 8 peer range (^2 || … || ^8), yet aliases its own shims by bare specifier, and its
exports map still carries legacy trailing-slash keys ("./shims/process/") that resolve to files.
Rolldown validates those strictly where Vite 7's rollup ignored them:

[builtin:vite-alias] plugin `builtin:vite-alias` threw an error
Caused by:
  Expecting folder to folder mapping.
  ".../vite-plugin-node-polyfills/package.json" should end with "/"

Things I ruled out along the way:

  • Not the plugin version. 0.28.0 (latest, May 2026) ships a byte-identical exports map and
    the same shim files, and fails the same way. Bumping does not help.
  • Not Vite. Holding Vite at the new 8.2.1 and varying only the plugin isolates the plugin.
  • Not something you can simply delete. Dropping the plugin and adding the real buffer
    package does build — the SDK's polyfills.js assigns window.global/window.Buffer itself —
    but the free process global is still undefined at runtime.
  • No upstream fix exists yet. The fixes are open and unreleased:
    #161 (build root shims
    export) and #154 (use
    oxc when Rolldown is detected), with
    #158 and
    #140 reporting this
    exact error.

The fix

nodePolyfills() is kept — it is what actually injects the shims. Its bare shim references are
resolved to real files in the two places that need it, because dependency pre-bundling runs its
own resolver:

  1. The module graph, via a resolveId hook. The regex is anchored so it matches only
    buffer / global / process, their node: forms, their subpaths (buffer/index.js), and
    the plugin's own vite-plugin-node-polyfills/shims/* — and not lookalikes such as
    some-pkg/process, processing, or my-buffer (unit-tested).
  2. The plugin's own returned config, patched before Vite merges it. This is where the
    pre-bundle alias maps and the injected banner come from, and a resolveId hook cannot reach
    them — which is why vite dev stayed broken until this was added.

Alias values point at the shim directory rather than the file: aliases substitute by prefix, so
a directory keeps both buffer and buffer/index.js resolvable. Import specifiers in the banner
still get the file.

Both are deletable as soon as upstream ships; the comment in the config says so.

MUI system props on Box

Fixing dev mode surfaced a second, real bug. WalletHeader passed display,
justifyContent, alignItems and borderBottom as direct Box props. MUI 9 no longer forwards
those, so they leaked to the DOM — React logged "does not recognize the prop" for each, and the
header styling was silently lost. Moving them into sx restores the layout and clears the
last tsc -b errors here, so examples/dynamic now type-checks clean.

Bundled in: pnpm dedupe

#850 added ws@>=7.0.0 <8.0.0 and ws@>=8.0.0 overrides but never ran pnpm dedupe, so the
lockfile still carried ws@7.5.13 alongside ws@8.21.3. The vulnerable copy stayed in the tree
via @remix-run/dev and jayson (through isomorphic-ws) — precisely what those overrides were
meant to remove, so that security fix was only half-applied, and pnpm dedupe --check failed on
main. Deduping collapses ws to a single 8.21.3; the diff is 27 lines.

vite-plugin-node-polyfills moves to devDependencies at ^0.26.0, matching the twelve other
examples that already use it, and vite-plugin-env-compatible is dropped.

Visual showcase (Screenshots or Videos)

examples/dynamic renders the widget alongside Dynamic's own connect button, in both
vite dev and vite preview. Page text from a headless load:

Dynamic + LI.FI widget Example
Connect wallet
Exchange
From   Select...
To     Select...
Send   $0.00

Console is clean — no process is not defined, no app errors.

Verification

Gate Result
examples/dynamic widget-smoke 3 / 3 — container renders, Settings opens, tokens selectable
examples/dynamic vite dev widget renders, 0 pre-bundle errors, 0 React prop warnings
examples/dynamic tsc -b passes (was 3 errors)
pnpm test:examples 18 / 18 passed on the rebased base, before the dev-mode and dedupe commits; CI's per-example E2E jobs cover the final tree
pnpm build pass
pnpm check (Biome) pass
pnpm check:types pass
pnpm check:circular-deps pass
pnpm install --frozen-lockfile pass
pnpm dedupe --check pass (failed on main before this PR)

Also updated the profile enumeration in e2e/tests/profiles/widget-smoke.spec.ts — the standard
profile is now 14, not 13.

Not addressed here

The same MUI TS2769 overload errors still fail tsc -b in privy, connectkit and reown,
from the same system-props-on-Box pattern. That is long-standing and why those examples carry
buildCmd: vite-build, which skips tsc. dynamic is fixed because its errors were in the file
I had to touch anyway; the other three are a separate sweep.

Checklist before requesting a review

  • I have performed a self-review and testing of my code.
  • This pull request is focused and addresses a single problem.
  • If this PR modifies the Widget API or adds new features that require documentation, I have updated the documentation in the public-docs repository.

@changeset-bot

changeset-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d1ec95e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

✅ E2E Dev Smoke — passing

Check Result
Dev server start (pnpm dev) ✅ started
Smoke tests ✅ passed

4 passed · 0 failed · 0 skipped · 32s

View run

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

E2E Examples — all passed

All examples passed in the latest run.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

E2E Playground results

passed  158 passed

Details

stats  158 tests across 10 suites
duration  2 minutes, 11 seconds
commit  d1ec95e

📥 Download full HTML report (open the run → Artifacts → playwright-report)

The example was `status: broken` and skipped by the e2e harness, so the
@dynamic-labs v5 bump was never render-verified. It now builds, serves and
passes the widget-smoke suite, and is back in the active set (18 examples).

The old broken note blamed vite-plugin-env-compatible and prescribed swapping
in vite-plugin-node-polyfills. The diagnosis was right — that plugin only
substitutes `process.env.*` at build time, while the Dynamic SDK reads a bare
`process` global (nextTick/versions/emit) and its own polyfills.js imports
`buffer/index.js` — but the prescribed fix no longer works on its own.

vite-plugin-node-polyfills advertises a Vite 8 peer range, yet aliases its own
shims by bare specifier, and its exports map still carries legacy
trailing-slash keys that resolve to files. Rolldown rejects those:

  Expecting folder to folder mapping.
  ".../vite-plugin-node-polyfills/package.json" should end with "/"

0.28.0 ships an identical exports map and fails the same way, so bumping the
plugin does not help. Upstream fixes are open but unreleased (#161, #154; #158
and #140 report this error). A one-hook `resolveId` plugin resolves the three
shims to their real files, which keeps rolldown out of the exports map — and
becomes deletable once upstream ships.

`vite dev` for this example is still broken: dependency pre-bundling resolves
the plugin's injected banner in a separate plugin container this hook cannot
reach. That path was equally broken before (`process is not defined` in both
dev and build), and no CI job covers an example's dev server.
@chybisov
chybisov force-pushed the fix/dynamic-example-v5 branch from 3dc0631 to c0d3728 Compare August 21, 2026 12:48
Patch the shim references in nodePolyfills()' own returned config, before Vite
merges it, instead of only intercepting the module graph. That is where the
dependency pre-bundle alias maps and the injected banner come from, and
pre-bundling resolves them in its own container — so a resolveId hook alone
left `vite dev` failing on the same "Expecting folder to folder mapping".

Alias values point at the shim directory rather than the file: aliases
substitute by prefix, so a directory keeps both `buffer` and `buffer/index.js`
resolvable. Import specifiers in the banner still get the file.

Also move WalletHeader off MUI system props. MUI 9 no longer forwards
`display`/`justifyContent`/`alignItems`/`borderBottom` on Box, so they leaked
to the DOM — React logged "does not recognize the prop" for each and the header
styling was silently lost. Using `sx` restores it and clears the last
`tsc -b` errors in this example, which now type-checks clean.

`vite dev`, `vite build` and `vite preview` all render the widget now.
#850 added `ws@>=7.0.0 <8.0.0` and `ws@>=8.0.0` overrides but never ran
`pnpm dedupe`, so the lockfile still carried ws@7.5.13 alongside ws@8.21.3.
The vulnerable copy stayed in the tree via @remix-run/dev and jayson (through
isomorphic-ws), which is exactly what those overrides were meant to remove —
the fix was only half-applied, and `pnpm dedupe --check` failed on main.

`pnpm dedupe` collapses ws to a single 8.21.3 and the check passes again.
@chybisov
chybisov merged commit 3991702 into main Aug 21, 2026
30 checks passed
@chybisov
chybisov deleted the fix/dynamic-example-v5 branch August 21, 2026 13:25
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