adobe / adobe/aio-lib-web

`aio app build` generates `jsxDEV` for Admin UI V2 even with `NODE_ENV=production`

Open
#247 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
7
Forks
15
Avg merge
22h 17m
Merged PRs (30d)
1

Description

## Description

When building an Adobe Commerce Admin UI SDK V2 extension using `aio app build`, the generated web bundle is compiled using React's development JSX runtime (`react/jsx-dev-runtime` / `jsxDEV`), even when `NODE_ENV=production` and `--web-optimize` are used.

This causes the deployed Admin UI extension to fail at runtime with:

```text
Uncaught TypeError: (0 , _jsxDevRuntime.jsxDEV) is not a function
```

## Environment

* Node.js: `v24.x`
* npm: `11.x`
* `@adobe/aio-cli`: `11.1.2`
* `@adobe/aio-cli-plugin-app`: `14.8.2`
* `@adobe/aio-commerce-lib-app`: `1.11.0`
* `@adobe/aio-commerce-lib-admin-ui`: `1.0.1`
* `@adobe/aio-commerce-sdk`: `1.4.2`
* React: `19.3.0`
* TypeScript: `6.0.3`
* Parcel: `2.16.4`
* `@adobe/aio-lib-web`: `7.2.0`

The extension uses:

```yaml
commerce/backend-ui/2
```

and the web source is TypeScript/TSX.

## Reproduction

The project is created using the Adobe Commerce Admin UI SDK V2 scaffolding.

The TypeScript configuration contains:

```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react",
"module": "esnext",
"moduleResolution": "bundler",
"noEmit": true
}
}
```

A normal TypeScript compilation correctly produces:

```js
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
```

However, after running:

```bash
NODE_ENV=production aio app build --force-build
```

or:

```bash
NODE_ENV=production aio app build --force-build --web-optimize
```

the generated Admin UI bundle contains:

```text
react/jsx-dev-runtime
```

and calls:

```js
jsxDEV(...)
```

The resulting application fails at runtime:

```text
Uncaught TypeError: (0 , _jsxDevRuntime.jsxDEV) is not a function
```

## Expected behavior

A production App Builder build should compile the React JSX using:

```text
react/jsx-runtime
```

and generate:

```js
jsx(...)
```

rather than:

```js
jsxDEV(...)
```

when building for production.

## Investigation

I traced the build process to `@adobe/aio-lib-web`.

The relevant Parcel options are constructed approximately as follows:

```js
const parcelBundleOptions = {
entries,
defaultConfig: require.resolve('@parcel/config-default'),
shouldDisableCache: false,
targets: {
webassets: {
includeNodeModules: true,
distDir: dest
}
},
defaultTargetOptions: {
distDir: dest,
shouldOptimize: options.shouldOptimize
},
shouldPatchConsole: false,
shouldContentHash: true,
logLevel: 'error',
...options
}
```

The important observation is that `mode` is not explicitly provided.

Parcel defaults:

```js
mode = initialOptions.mode ?? 'development';
```

Therefore, even when:

```bash
NODE_ENV=production
```

is set, Parcel's own `options.mode` remains:

```text
development
```

The Parcel JavaScript transformer then uses the development mode when configuring the React JSX transform, resulting in:

```text
react/jsx-dev-runtime
jsxDEV()
```

I also verified that React itself is functioning correctly:

```text
react/jsx-runtime.jsx -> function
react/jsx-dev-runtime.jsxDEV -> function
```

A standalone TypeScript compilation also correctly generates `react/jsx-runtime`.

Therefore, this appears to be related to the Parcel build mode rather than React or TypeScript.

## Additional testing

The following were tested:

### 1. `NODE_ENV=production`

```bash
NODE_ENV=production aio app build --force-build
```

Still produces `jsxDEV`.

### 2. `--web-optimize`

```bash
aio app build --force-build --web-optimize
```

Still produces `jsxDEV`.

### 3. `NODE_ENV=production` + `--web-optimize`

```bash
NODE_ENV=production aio app build --force-build --web-optimize
```

Still produces `jsxDEV`.

### 4. Parcel production mode

Parcel's API supports explicitly passing:

```js
mode: "production"
```

When Parcel receives `mode: "production"`, the React JSX transformation correctly uses the production JSX runtime.

## Question

Is there currently a supported way to pass:

```js
mode: "production"
```

to the Parcel instance used by `aio app build`?

For example, something equivalent to:

```bash
aio app build --mode production
```

or a supported App Builder/package configuration that causes `@adobe/aio-lib-web` to pass:

```js
{
mode: "production"
}
```

to Parcel?

If there is no supported configuration mechanism, could the build pipeline be updated so that production builds explicitly pass:

```js
mode: "production"
```

to Parcel?

For example:

```js
mode: options.mode || (options.shouldOptimize ? "production" : "development")
```

or an equivalent implementation.

## Why this matters

`NODE_ENV=production` is currently insufficient because the JSX transformation appears to depend on Parcel's `mode`, not only the runtime `NODE_ENV`.

This means an Admin UI V2 extension can successfully build and deploy but fail immediately in the browser because the production React runtime is incompatible with the generated `jsxDEV()` calls.

Any guidance on the officially supported way to force Parcel into production mode during `aio app build` would be appreciated.

Thank you!

Contributor guide

Open the contributing guide

Research direction

Start at the Parcel options construction in @adobe/aio-lib-web and reproduce the issue through the `aio app build` entry point with `NODE_ENV=production` and `--web-optimize`. Check how Parcel receives its mode, then verify that a production build uses `react/jsx-runtime` and `jsx(...)` rather than `react/jsx-dev-runtime` and `jsxDEV(...)`.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nodejs, react, typescript
Domain
build-system, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.