Browser sandbox ESM→CJS converter rewrites static class field names that match an imported binding → SyntaxError: Unexpected number

Author: joyenjoyerCreated Sep 4, 2026Updated Sep 4, 2026

bug report

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for an issue that matches the one I want to file, without success.

Description of the problem

The fast ESM→CommonJS path used for node_modules in browser sandboxes (convertEsModule in packages/app/src/sandbox/eval/transpilers/babel/ast/convert-esmodule.ts) treats the key of a class field as a reference to an imported binding when both share a name. The key is rewritten to (0, $csb__mod.name), which is not a valid property name, so evaluating the module throws SyntaxError: Unexpected number.

Input (a dependency in node_modules):

javascript
import { x } from './a.js'

export class C {
  static x = x
}

Output produced by the converter (read from manager.transpiledModules[...].source.compiledCode):

javascript
"use strict";
exports.C = void 0;
Object.defineProperty(exports, "C", { enumerable: true, configurable: true, get: function $csbGet() { return C; } });
Object.defineProperty(exports, "__esModule", { value: true });
var $csb___a_js = require("./a.js");
class C {
  static (0, $csb___a_js.x) = (0, $csb___a_js.x)
}

Note static (0, $csb___a_js.x) = on the left-hand side. Renaming either the field or the import so they differ makes the module load.

Likely cause: the second pass in convert-esmodule.ts renames every escope reference whose name is in varsToRename and has resolved === null:

typescript
const scopeManager = escope.analyze(program, { ecmaVersion: 6 });
…
if (hasOwn(varsToRename, ref.identifier.name) && ref.resolved === null && !ref.writeExpr) {
  ref.identifier.name = `(0, ${varsToRename[ref.identifier.name].join('.')})`;
}

escope runs with ecmaVersion: 6, which predates class fields (PropertyDefinition, ES2022). It does not know that the identifier in a non-computed PropertyDefinition.key is a property name, reports it as an unresolved reference, and the rename is applied to it.

Suggested fix: skip identifiers that are the non-computed key of a PropertyDefinition or MethodDefinition during the rename pass, or move the scope analysis to a class-field-aware analyzer such as eslint-scope with ecmaVersion: 2022.

The pattern static propTypes = propTypes / static allowedProps = allowedProps is very common in React component libraries. Babel ≥ 7.23 with default preset-env targets and TypeScript with target: ES2022 emit native class fields, so more and more published packages have this shape. The same code runs in Node, Vite, webpack, esbuild, Rollup and every current browser.

How has this issue affected you? What are you trying to accomplish?

We maintain a React component library whose documentation site has an "Edit in CodeSandbox" button that opens each example in a browser sandbox. After a recent build change our published packages emit native class fields, and many class components contain static allowedProps = allowedProps. Since then no browser sandbox can import any of our components, so the button is broken for every example, and users who paste an example into a fresh React sandbox hit the same error.

VM Sandboxes are unaffected because they run a real bundler, but they require a signed-in user, so they are not a substitute for anonymous documentation visitors.

To Reproduce

  1. Create a sandbox from the React (create-react-app) browser template.

  2. Add a local package in the sandbox's own node_modules folder:

    node_modules/buggy-pkg/package.json

    json
    { "name": "buggy-pkg", "version": "1.0.0", "type": "module", "main": "./index.js", "module": "./index.js" }

    node_modules/buggy-pkg/a.js

    javascript
    export const x = { hello: 'world' }

    node_modules/buggy-pkg/index.js

    javascript
    import { x } from './a.js'
    
    export class C {
      static x = x
    }
  3. Replace src/index.js with:

    javascript
    import { C } from 'buggy-pkg'
    document.getElementById('root').textContent = 'C.x is: ' + JSON.stringify(C.x)
  4. Open the preview.

Expected: the page shows C.x is: {"hello":"world"}.

Actual: error overlay

SyntaxError
Unexpected number
    at $csb$eval (https://….csb.app/node_modules/buggy-pkg/index.js:…)

Renaming the field to static y = x makes it work. Any other browser template that uses the Babel transpiler (parcel, preact-cli, vue-cli, solid, …) behaves the same.

Link to sandbox: link (optional)

Preview: https://fk6lkn.csb.app

Your Environment

Software Name/Version
Сodesandbox Browser sandbox, create-react-app template, runtime bundle sandbox.d867b1f77.js, observed 2026-09-04
Browser Google Chrome (stable)
Operating System macOS 26.6.2

Source: codesandbox/codesandbox-client