Skip to content

parser: false "Duplicated export" when await appears in variable initializer under .js auto-detect #22158

@dvrd

Description

@dvrd

parser: false "Duplicated export" when await appears in variable initializer under .js auto-detect

export var x = await + 1; produces a spurious Duplicated export 'x' error when parsed with a .js filename (sourceType auto-detection). The same source parses cleanly with .mjs or explicit sourceType: 'module'.

Reproduction

Playground link

Steps:

mkdir /tmp/oxc-repro && cd /tmp/oxc-repro
npm init -y
npm install oxc-parser
node -e "
  const { parseSync } = require('oxc-parser');
  const src = 'export var x = await + 1;';

  // ✗ .js auto-detect: false 'Duplicated export'
  const r1 = parseSync('input.js', src);
  console.log('.js auto-detect:', r1.errors.length ? 'ERROR: ' + r1.errors[0].message : 'OK');

  // ✓ .mjs: works fine
  const r2 = parseSync('input.mjs', src);
  console.log('.mjs:', r2.errors.length ? 'ERROR: ' + r2.errors[0].message : 'OK');

  // ✓ .js with explicit sourceType: works fine
  const r3 = parseSync('input.js', src, { sourceType: 'module' });
  console.log('.js + sourceType=module:', r3.errors.length ? 'ERROR: ' + r3.errors[0].message : 'OK');

  // ✓ .js with prior module syntax: works fine
  const r4 = parseSync('input.js', 'export {}; ' + src);
  console.log('.js + prior export:', r4.errors.length ? 'ERROR: ' + r4.errors[0].message : 'OK');
"

Expected output:

.js auto-detect: OK
.mjs: OK
.js + sourceType=module: OK
.js + prior export: OK

Actual output (oxc-parser 0.129.0):

.js auto-detect: ERROR: Duplicated export 'x'
.mjs: OK
.js + sourceType=module: OK
.js + prior export: OK

V8 cross-check (confirming it's valid JS):

echo 'export var x = await + 1;' > /tmp/test.js
node --check /tmp/test.js && echo "V8: OK" || echo "V8: ERROR"
# → V8: OK

echo 'export var x = await + 1;' > /tmp/test.mjs
node --check /tmp/test.mjs && echo "V8: OK" || echo "V8: ERROR"
# → V8: OK

Analysis

The bug fires when all three conditions hold:

  1. .js filename (auto-detect mode, not .mjs or explicit sourceType)
  2. export is the first module syntax in the file (no prior import/export)
  3. await appears in the variable initializer

The AST itself is correct — the body contains a single ExportNamedDeclaration with one declarator initialized to an AwaitExpression — but the duplicate-export check fires against what appears to be a stale pre-upgrade binding. Both labels in the error codeframe point at the same x identifier.

Other await patterns also trigger the bug:

export var x = await(1);     // ✗ Duplicated export 'x'
export var x = await + 0;    // ✗ Duplicated export 'x'

Non-await initializers do not:

export var x = 1 + 1;        // ✓
export var x = void 0;       // ✓
export var x = yield + 1;    // ✓

Spec reference

ECMA-262 §16.2.3: export VariableStatement binds the declared names as exports. In module code, await is a keyword (§16.2), so await + 1 is parsed as await (+1) — a top-level AwaitExpression of the unary expression +1. The result is a single VariableDeclaration with one binding x. There is no duplicate.

Parser matrix

Parser Version export var x = await + 1;
V8 (node --check, .js) v25.9.0 ✅ accepts
V8 (node --check, .mjs) v25.9.0 ✅ accepts
Acorn latest ✅ accepts
Babel (sourceType: unambiguous) 7.29.3 ✅ accepts
TypeScript 6.0.3 ✅ accepts
SWC 1.15.33 ✅ accepts
esbuild 0.28.0 ✅ accepts
OXC (.mjs) 0.129.0 ✅ accepts
OXC (sourceType: module) 0.129.0 ✅ accepts
OXC (.js auto-detect) 0.129.0 ❌ Duplicated export

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions