Skip to content

🐛 Bug: Syntax error with @babel/register, JSX and latest Node.js 20 LTS (require ESM) only when run via Mocha #5317

@mbtts

Description

@mbtts

Bug Report Checklist

  • I have read and agree to Mocha's Code of Conduct and Contributing Guidelines
  • I have searched for related issues and issues with the faq label, but none matched my issue.
  • I have 'smoke tested' the code to be tested by running it outside the real test suite to get a better sense of whether the problem is in the code under test, my usage of Mocha, or Mocha itself.
  • I want to provide a PR to resolve this

Expected

A test to pass, using:

  1. Mocha 11.2.0
  2. @babel/register (with env and react presets)
  3. Node.js 20 LTS (v20.19.0)
    1. This is the first release with require ESM enabled by default.

Test code

import assert from 'node:assert'
import { isValidElement } from 'react'
import Component from "./component";

describe('component test', () => {
    it('exports a component', () => {
        assert.equal(isValidElement(<Component />), true);
    });
});

Source code under test

export default function Component () {
    return (
        <p>Test component</p>
    );
}

Output using Node.js v20.18.3

mocha "src/**.test.js"

  component test
    ✔ exports a component


  1 passing (1ms)

Actual

Output using Node.js v20.19.0

The test fails with a syntax error

 mocha "src/**.test.js"

 Exception during run: SyntaxError[ @/src/component.test.js ]: Unexpected token '<'
    at compileSourceTextModule (node:internal/modules/esm/utils:340:16)
    at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:146:18)
    at #translate (node:internal/modules/esm/loader:431:12)
    at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:478:27)
    at async ModuleJob._link (node:internal/modules/esm/module_job:110:19)

Minimal, Reproducible Example

The repository README highlights the relevant scripts (start and test, along with expected output).

Versions

> mocha --version
11.2.0
> node_modules/.bin/mocha --version
11.2.0

✅ Working Node.js version:

node --version
v20.18.3

❌ Broken Node.js version:

> node --version
v20.19.0

Additional Info

  • Babel register is still able to run the same code with Node.js (but without Mocha)
    • This is the primary reason I am filing as a Mocha issue first, but I appreciate Node.js have changed the behaviour with the minor update and the require ESM feature.
  • The problem is triggered by the use of JSX syntax in JS files. Because the file has a .js file extension, Node.js ESM loader is attempting to perform import analysis/processing on it before the request intercepted by pirates (Babel register). This source analysis crashes because it cannot parse the syntax.
    • If the files are renamed from .js to use a .jsx extension then the analysis is not performed, the request is handed on to babel register and the tests pass. This is the simplest/easiest workaround, especially in small projects.
  • The reason Babel register is still able to run the same code with Node.js (but without Mocha) appears to be because it never passes through the (Node.js) ESM loader and so the import analysis/processing is not performed. I believe this line from esm-utils.js in Mocha is forcing code to be loaded through the ESM loader, even in a CJS environment.
    • In other words, Babel register with exactly the same configuration works when used directly with Node.js in an exclusively CJS environment:
      node --require @babel/register src/index.js
      
      <p>Test component</p>

I mostly wanted to let you know and document it as I suspect I won’t be the only person to hit this problem.

Many thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: in triagea maintainer should (re-)triage (review) this issuetype: buga defect, confirmed by a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions