Skip to content

client: true causes TDZ error in Vite 8 dev server #100

@0xNoWater

Description

@0xNoWater

Description

When using client: true with Vite 8 (Rolldown-based), the plugin's transform causes a Cannot access '__cjsInterop1__' before initialization error in the browser dev server.

Reproduction

// vite.config.ts
cjsInterop({
  client: true,
  dependencies: ['@ant-design/icons/lib/components/Icon'],
})

Load any page that imports the listed dependency — the browser console shows:

Uncaught ReferenceError: Cannot access '__cjsInterop1__' before initialization
    at icon.tsx:1:46

Root cause

The plugin prepends its preamble to line 1 of the transformed module:

// Inserted at top:
const { default: _Icon = __cjsInterop1__ } = __cjsInterop1__?.default?.__esModule ...

// Original import, replaced in-place (further down the file):
import __cjsInterop1__ from '@ant-design/icons/lib/components/Icon';

In Vite 7 (esbuild-based dev server), import bindings are hoisted and initialized before any module code runs, so the preamble at line 1 can safely reference __cjsInterop1__. In Vite 8's dev server, imports appear to be transformed into const assignments, which have TDZ — so the preamble hits the TDZ before the declaration is reached.

Proposed fix

Insert the preamble after the last import declaration in the file rather than prepending it to the top. That way it executes after all import bindings are initialized, regardless of whether the runtime hoists them or not.

Versions

  • vite-plugin-cjs-interop: 3.1.0
  • vite: 8.0.3

Workaround

We're currently unwrapping manually at each affected import site:

// utils/cjs-interop.ts
export function resolveCjsDefault<T>(module: T): T {
  if (module !== null && typeof module === 'object' && 'default' in module) {
    // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
    return (module as unknown as { default: T }).default;
  }
  return module;
}

// usage
import _Icon from '@ant-design/icons/lib/components/Icon';
const Icon: React.ComponentType<IconComponentProps> = resolveCjsDefault(_Icon);

This works but it would be much nicer to have client: true handle it via config.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions