Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect bundle behavior when IIFE #1837

Closed
loynoir opened this issue Dec 7, 2021 · 3 comments
Closed

Incorrect bundle behavior when IIFE #1837

loynoir opened this issue Dec 7, 2021 · 3 comments

Comments

@loynoir
Copy link

loynoir commented Dec 7, 2021

Reproduce

  1. https://github.com/loynoir/reproduce-esbuild-1837.mjs
pnpm run build
  1. check for browser console
    esm.html and iife.html

Source

import * as t from "@babel/types";
console.log(
  t.binaryExpression("+", t.stringLiteral("x"), t.stringLiteral("y"))
);

Expected

ESM context output

{type: "BinaryExpression", operator: "+", left: {…}, right: {…}}

Unexpected

iife context output

Uncaught TypeError: Property left of BinaryExpression expected node to be of a type ["Expression"] but instead got "StringLiteral"
@loynoir
Copy link
Author

loynoir commented Dec 7, 2021

@babel/types/lib/builders/builder.js contains a this

@evanw
Copy link
Owner

evanw commented Dec 8, 2021

As far as I can tell, the problem is caused by strict mode vs. sloppy mode, which is caused by the .mjs vs. .js file extension. Here's an example:

function notStrict() { return this }
function strict() { 'use strict'; return this }
console.log(notStrict.apply('foo'), strict.apply('foo'))

Sloppy mode requires the value for this to be an object while strict mode doesn't change the value of this, and this code breaks when not run in strict mode. The problem remains if you change --format=iife to --format=esm, so it's not really caused by the IIFE output mode. It's caused by the use of the .js file extension instead of the .mjs file extension.

You can get this code to work even with the .js file extension by adding "use strict"; to the top of the file, which can be done in esbuild with something like --banner:js="use strict;" (quoting this argument in a cross-shell way can be difficult, so you may want to use the JS API for this instead).

It may not be straightforward to automatically solve this in esbuild's case because strict mode infects all scopes inside it by design, and not all code that esbuild bundles can be run in strict mode. And esbuild's scope-flattening bundling approach combines scopes from different files with potentially different strict mode settings. I suppose I could potentially insert "use strict"; inside every top-level function inside every strict mode module (either implicitly or explicitly so) but I suspect that would be a very unpopular decision due to the code bloat that would cause. I wonder how other bundlers handle this.

@lightmare
Copy link

This is due to a statement incorrectly inserted before strict mode directive:

  // node_modules/.pnpm/@[email protected]/node_modules/@babel/types/lib/utils/shallowEqual.js
  var require_shallowEqual = __commonJS({
    "node_modules/.pnpm/@[email protected]/node_modules/@babel/types/lib/utils/shallowEqual.js"(exports) {
      init_shims();
      "use strict"; // directives must appear before any other statements in the block

@evanw evanw closed this as completed in 4af88a9 Dec 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants