Skip to content

fix(vite-plugin): hydrate islands exported by name#711

Draft
romain-pm wants to merge 1 commit into
mainfrom
fix/island-named-exports
Draft

fix(vite-plugin): hydrate islands exported by name#711
romain-pm wants to merge 1 commit into
mainfrom
fix/island-named-exports

Conversation

@romain-pm

Copy link
Copy Markdown

Closes #706 — part of EPIC #698 (developer experience).

The bug

// Form.client.tsx
export function Form() {  }        // named, not default

// default.server.tsx
<Island component={Form} />

Builds clean. Server-renders clean. In the browser the island requests /modules/<module>/undefined.js, the import fails, and the component never hydrates — for a form, the submit silently degrades to a native GET. The only clue is a console error.

insert-filename attaches the hydration metadata (__filename) to ExportDefaultDeclaration nodes only, so Component.__filename is undefined for a named export and <Island> interpolates it straight into the URL.

Change

Three coordinated pieces, all in this monorepo, released together:

  • vite-plugin tags every export of a *.client.{jsx,tsx} file — export function, export const, export class, and the existing default — with __filename and the new __exportName. Declarations can't be wrapped in an expression, so named exports are tagged by a statement appended after them. Descriptors are configurable so a component exported twice (export const Foo = …; export default Foo;) is tagged twice instead of throwing.
  • Island emits data-export (omitted for default exports, so nothing changes in existing markup) and now throws when a component carries no metadata at all, naming it and explaining which export forms are supported.
  • the client loader reads data-export, falling back to default — so islands built with an older plugin keep working.

export { … } lists and re-exports remain unsupported: they cannot be tagged reliably, and they now land in the fail-fast path instead of producing undefined.js.

Verification

Fixtures (yarn workspace @jahia/vite-plugin test, 2 tests): a new named.client.tsx exporting two components by name is mounted alongside the existing default-export island; the test asserts the exact (__filename, __exportName) triples in the server bundle and that the client bundle really exposes both names. Snapshots regenerated.

E2E: the test module gains a named-export island, and the hydration spec gains a case asserting it hydrates and stays interactive, with a cy.intercept that fails the test if anything ever requests undefined.js.

Live on Jahia 8.2:

  • named-export island renders with data-export="SampleNamedExportReact", hydrates, and increments on click (5 → 6);
  • the two pre-existing default-export islands still hydrate (3/3 hydrated, no data-export on them);
  • passing a component that was never declared in a client file now fails the render with <Island> received a component without hydration metadata: NotAClientComponent… instead of reaching the browser.

Note for the reviewer

Modules must be rebuilt with the matching plugin version to use named exports; already-deployed modules are unaffected (no data-export → loader falls back to default).

`insert-filename` only attached the hydration metadata (`__filename`) to the
default export of a `*.client.{jsx,tsx}` file. A component exported by name
built and server-rendered fine, but the browser then requested
`<module>/undefined.js` and the island never hydrated — silently, with an
interactive form degrading to a native submit.

Every export of a client file is now tagged, with the export name alongside
the filename, so `<Island>` can tell the loader which binding to pick up
(`data-export`, omitted for default exports so older bundles keep working).

A component that carries no metadata at all — declared outside a client file,
or reached through an `export { … }` list — now throws at render with a
message naming it, instead of letting `undefined.js` reach the browser.

Verified on a Jahia 8.2 instance: a named-export island hydrates and stays
interactive, the two default-export islands of the test module still hydrate,
and a metadata-less component fails the render with the new message.

Closes #706
@github-actions

Copy link
Copy Markdown

🦜 Chachalog

javascript-modules minor
  • <Island> now accepts client components exported by name, not only default exports: the Vite plugin tags every export of a *.client.{jsx,tsx} file, and the browser loader picks the right one. A component that carries no hydration metadata (declared outside a client file, or re-exported through an export { … } list) now fails at render with an explicit message, instead of silently making the browser request undefined.js. (Named exports in .client.tsx silently break island hydration (undefined.js) #706)

Create a new entry online or run npx chachalog@0.5.2 prompt to create a new entry locally.

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.

Named exports in .client.tsx silently break island hydration (undefined.js)

1 participant