diff --git a/src/js_printer/lib.rs b/src/js_printer/lib.rs index 028828a4722e..dd467cbdda08 100644 --- a/src/js_printer/lib.rs +++ b/src/js_printer/lib.rs @@ -1533,6 +1533,12 @@ pub mod __gated_printer { v.left_level = Level::Call; } } + ExprData::EImportIdentifier(_) => { + // When minifying, a missing import prints as "void 0" + if self.options.minify_syntax { + v.left_level = Level::Call; + } + } _ => {} } } diff --git a/test/bundler/esbuild/importstar.test.ts b/test/bundler/esbuild/importstar.test.ts index 8743b1df9ac4..11d977593ae8 100644 --- a/test/bundler/esbuild/importstar.test.ts +++ b/test/bundler/esbuild/importstar.test.ts @@ -1,4 +1,4 @@ -import { describe } from "bun:test"; +import { describe, expect } from "bun:test"; import { itBundled } from "../expectBundled"; // Tests ported from: @@ -862,6 +862,32 @@ describe("bundler", () => { "/entry.js": [`Import "foo" will always be undefined because there is no matching export in "foo.js"`], }, }); + itBundled("importstar/NamespaceImportMissingES6ExponentiationLHS", { + files: { + "/entry.js": /* js */ ` + import * as ns from './foo' + // A missing import prints as "void 0" under minify_syntax, which is not + // a valid left operand of ** unless parenthesized. + console.log(ns.nope ** 2, 2 ** ns.nope, ns.x ** 2) + `, + "/foo.js": `export const x = 3`, + }, + minifySyntax: true, + run: { + stdout: "NaN NaN 9", + }, + bundleWarnings: { + "/entry.js": [`Import "nope" will always be undefined because there is no matching export in "foo.js"`], + }, + onAfterBundle(api) { + const out = api.readFile("/out.js"); + expect(out).toContain("(void 0) ** 2"); + expect(out).toContain("2 ** void 0"); + // The existing export and the right operand stay unwrapped. + expect(out).not.toContain("(2 ** void 0)"); + expect(out).not.toMatch(/\(\w+\) \*\* 2/); + }, + }); itBundled("importstar/ExportOtherCommonJS", { files: { "/entry.js": `export {bar} from './foo'`,