codesandbox / codesandbox/codesandbox-client
Browser sandbox ESM→CJS converter rewrites static class field names that match an imported binding → SyntaxError: Unexpected number
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 13.6k
- Forks
- 2.4k
- Avg merge
- 6d 19h
- Merged PRs (30d)
- 2
Description
🐛 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):
import { x } from './a.js'
export class C {
static x = x
}
Output produced by the converter (read from manager.transpiledModules[...].source.compiledCode):
"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:
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
-
Create a sandbox from the React (create-react-app) browser template.
-
Add a local package in the sandbox's own
node_modulesfolder:node_modules/buggy-pkg/package.json{ "name": "buggy-pkg", "version": "1.0.0", "type": "module", "main": "./index.js", "module": "./index.js" }node_modules/buggy-pkg/a.jsexport const x = { hello: 'world' }node_modules/buggy-pkg/index.jsimport { x } from './a.js' export class C { static x = x } -
Replace
src/index.jswith:import { C } from 'buggy-pkg' document.getElementById('root').textContent = 'C.x is: ' + JSON.stringify(C.x) -
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 |
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/app/src/sandbox/eval/transpilers/babel/ast/convert-esmodule.ts at the second-pass rename logic and inspect how convertEsModule handles class-field keys. Reproduce the case through manager.transpiledModules[...].source.compiledCode using the provided browser sandbox example. Done means static x = x remains valid and the module evaluates with C.x containing the imported value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- babel, typescript
- Domain
- build-system, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100