JSX + TypeScript with ember-cli-babel

I’m attempting to add support for JSX or TSX files to an Ember 3.28 app. Based on ember-react-components, I tried configuring my ember-cli-build.js as follows:

    babel: {
      spec: true,
      plugins: [
        require.resolve("@babel/plugin-syntax-jsx"),
        require.resolve("@babel/plugin-transform-react-jsx"),
      ],
    },
    "ember-cli-babel": {
      enableTypeScriptTransform: true,
      extensions: ["js", "ts", "jsx"],
    },

However, once I add a .jsx file, I get a build error from TypeScript:

test.jsx: Type parameter list cannot be empty. (2:9)

  1 | export function TestJsx() {
> 2 |   return <>Hello world!</>
    |          ^
  3 | }
  4 |SyntaxError: /Users/matthew/repos/cc/v4/frontend/cc-frontend/utils/test.jsx: Type parameter list cannot be empty. (2:9)

  1 | export function TestJsx() {
> 2 |   return <>Hello world!</>
    |          ^
  3 | }
  4 |
    at constructor (/Users/matthew/repos/cc/v4/frontend/node_modules/@babel/parser/lib/index.js:359:19)
    at TypeScriptParserMixin.raise ...

I believe that ember-cli-typescript is parsing the JSX before @babel/plugin-transform-react-jsx can, causing it to complain at the (valid) JSX that is invalid TypeScript.

I get the same error if I use a .tsx file and configure TypeScript to process it ("jsx": "react-jsx" in tsconfig.json and “tsx” in ember-cli-babel.extensions). I tried adding .jsx files to my tsconfig.json’s exclude list but it does not help.

Any ideas for how to get this working? I guess I could opt out of ember-cli-babel’s auto-generated config, but I’ve had trouble getting that to work with TypeScript.

If I do opt out of ember-cli-babel’s auto-generated config, I run into this build error (before adding anything jsx related):

Build Error (StripBadReexports)

[BABEL] unknown file: Preset /* your preset */ requires a filename to be set when babel is called directly,

babel.transformSync(code, { filename: 'file.ts', presets: [/* your preset */] });

See https://babeljs.io/docs/en/options#filename for more information.

The stack trace points to this line in ember-composable-helpers (v4.4.1). I can understand what the error is saying, but I’m confused why it only fails after I opt out.

Here is my babel.config.js (based on GitHub - emberjs/ember-cli-babel: Ember CLI plugin for Babel):

//babel.config.js

const { buildEmberPlugins } = require("ember-cli-babel")

module.exports = function (api) {
  api.cache(true)

  return {
    presets: [
      [
        require.resolve("@babel/preset-env"),
        {
          targets: require("./config/targets"),
          spec: true,
        },
      ],
      [require.resolve("@babel/preset-typescript"), { allowDeclareFields: true }],
    ],
    plugins: [
      // this is where all the ember required plugins would reside
      ...buildEmberPlugins(__dirname, {
        /*customOptions if you want to pass in */
      }),
    ],
  }
}

Okay, I got this working with a fairly standard ember-cli-babel/TypeScript setup. The key change is to explicitly list @babel/plugin-transform-typescript in babel.plugins so that you can add the isTsx: true option, overriding the options set by enableTypeScriptTransform: true.

Otherwise, you just need to install and specify plugins equivalent to babel-preset-react.

Here is the relevant part of my ember-cli-build.js:

    babel: {
      plugins: [
        [require.resolve("@babel/plugin-syntax-jsx")],
        [require.resolve("@babel/plugin-transform-react-jsx"), { runtime: "automatic" }],
        [require.resolve("@babel/plugin-transform-react-display-name")],
        [
          require.resolve("@babel/plugin-transform-typescript"),
          { allowDeclareFields: true, isTSX: true },
        ],
      ],
    },
    "ember-cli-babel": {
      enableTypeScriptTransform: true,
      extensions: ["js", "ts", "jsx", "tsx"],
    },