Skip to content

bug: namespace and combined React imports crash at runtime in loader #4

Description

@hartym

Summary

TLDR — Components using import * as React from "react" or combined imports (import React, { useState } from "react") crash at runtime because transformEsmToRuntime produces invalid JavaScript. The transform function is also duplicated across two files, risking drift. Fixing the regex and extracting a shared module resolves all three issues.

Context

wilco bundles components with esbuild using --external:react, leaving import statements in the output. At runtime, transformEsmToRuntime rewrites these into lookups on window.__MODULES__["react"]. The module registry itself is correct: useState, useEffect, etc. are available as properties.

The bug is in the regex transform, which produces invalid JS for two import patterns:

graph TD
    A[Component source] -->|esbuild --external:react| B[ESM bundle with imports]
    B -->|transformEsmToRuntime| C{Import pattern?}
    C -->|"import { useState }"| D["const { useState } = window.__MODULES__[...] ✅"]
    C -->|"import * as React"| E["const * as React = window.__MODULES__[...] ❌ invalid JS"]
    C -->|"import React, { useState }"| F["Left untransformed ❌"]
Loading

Affected files (duplicated logic):

  • src/wilcojs/react/src/loader/standalone.ts (Django standalone loader)
  • src/wilcojs/react/src/loader/useComponent.ts (Vite React app loader)

Bug 1: namespace imports produce invalid JS

// Input (esbuild output for components using React.useState style):
import * as React from "react";

// Current transform (INVALID JS):
const * as React = window.__MODULES__["react"];

// Fixed:
const React = window.__MODULES__["react"];

The regex captures * as React but the as replacement ((\w+)\s+as\s+(\w+) → $1: $2) doesn't match because * is not \w+. The raw capture is emitted as-is, producing a syntax error.

Bug 2: combined default+named imports are ignored

// Input:
import React, { useState } from "react";

// Current: left untransformed (regex doesn't match this pattern)

// Fixed — two statements:
const React = window.__MODULES__["react"];
const { useState } = window.__MODULES__["react"];

The regex alternation (\{[^}]+\}|\*\s+as\s+\w+|\w+) only handles one clause. Combined imports have two (React and { useState }), so the pattern fails to match entirely. Note: esbuild normalizes imports and rarely produces this pattern for externals, but it's a correctness fix.

Possible implementation

  1. Extract transformEsmToRuntime to a shared module (src/wilcojs/react/src/loader/transform.ts), imported by both standalone.ts and useComponent.ts
  2. Fix the regex to handle all three import patterns:
    • Named: import { x } from "mod" → const { x } = window.__MODULES__["mod"]
    • Namespace: import * as X from "mod" → const X = window.__MODULES__["mod"]
    • Combined: import X, { y } from "mod" → const X = window.__MODULES__["mod"]; const { y } = window.__MODULES__["mod"]
  3. Update tests in useComponent.test.ts to fix the namespace import expectation (currently expects invalid JS) and add combined import test cases
  4. Rebuild the standalone loader (pnpm build:loader) so loader.js includes the fix

Output and testing scenarios

Expected output:

  • All import patterns transform to valid JS that correctly resolves from window.__MODULES__
  • Existing named import behavior unchanged

Testing scenarios:

  1. Happy path: Component with import { useState } from "react" renders and hooks work
  2. Namespace import: Component with import * as React from "react" and React.useState() works
  3. Combined import: import React, { useState } from "react" transforms to two valid statements
  4. Aliased imports: import { jsx as _jsx } from "react/jsx-runtime" still works
  5. Multiple imports: Multiple import lines from different modules all transform correctly
  6. Source maps: Source map comments preserved after transformation

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Spec: Review neededSpecification needs human review and validationbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions