evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
38.22k stars 1.16k forks source link

Using import statements in commonjs files compiles, but produces wrong code #3962

Open blutorange opened 3 weeks ago

blutorange commented 3 weeks ago

While working on primefaces/primefaces#12717, I noticed that the bundled script file didn't work work due to a name clash of two variables. A simple example that illustrates this is below.

After some time, I realized that the main issue is the module type: the package.json sets the default to type: "commonjs". The entry point bundle.mjs is a module file. It imports a widget.js file, which is treated as commonjs due to the package.json setting. However, the widget.js file now also uses import / export syntax. Trying to run this via node bundle.mjs simply raises an error, as commonjs files can't use import.

esbuild seems to treat widget.js as a commonjs file, but does not raise an error and produces output that ultimately does not work.

I'm not sure regarding the expected behavior, but perhaps esbuild should also raise an error when a trying to use import statements in a commonjs file? Otherwise, it's hard to even notice that something is wrong and it takes some time to track down the issue. It's possible to notice the issue when running the code via node, but for frontend projects, the code may never get run via node, just bundled via a bundle.


// lib.js
export class Chart {
  constructor() { console.log("This is b.js")  }
}

This is the main file. ChartJsChart points to the class imported from lib.js.

// widget.js
import { Chart as ChartJsChart } from "./lib.js";

PrimeFaces.widget.Chart = class Chart {
    _render() {
        this.chart = new ChartJsChart();
    }
}
// bundle.mjs
import "./widget.js";

with the build script

// build.js
import * as esbuild from "esbuild";

await esbuild.build({
    entryPoints: ["bundle.mjs"],
    outfile: "dist/bundle.js",
    bundle: true,
})

and the package.json

{
  "name": "repro",
  "version": "1.0.0",
  "type": "commonjs",
  "main": "index.js",
  "dependencies": {
    "esbuild": "^0.24.0"
  }
}

When running node build.js, the output contains the following. Note that the Chart variable in the widget.js section now points the the Chart class from widget.js, not the one from lib.js anymore.

  // lib.js
  var Chart;
  var init_lib = __esm({
    "lib.js"() {
      Chart = class {
        constructor() {
          console.log("This is b.js");
        }
      };
    }
  });

  // widget.js
  var require_widget = __commonJS({
    "widget.js"() {
      init_lib();
      PrimeFaces.widget.Chart = class Chart {
        _render() {
          this.chart = new Chart();
        }
      };
    }
  });