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

When the input code has nested function declaration, the output code errors with Identifier 'foo' has already been declared #2809

Closed
sapphi-red opened this issue Jan 10, 2023 · 2 comments

Comments

@sapphi-red
Copy link
Contributor

When this code is input,

const n = ''

for (let i of [0,1]) {
  function f () {}
}

esbuild outputs (--minify-identifiers --format=esm)

const f = "";
for (let o of [0, 1]) {
  let n = function() {
  };
  var f = n;
}

This code redeclares f and throws SyntaxError: Identifier 'f' has already been declared.

repl

It doesn't happen when (1) --format=esm is not set or (2) export {} is appended or (3) 'use strict' is prepended.
This seems to happen from at least v0.9.0.

Original issue: vitejs/vite#11642

@hyrious
Copy link

hyrious commented Jan 11, 2023

Interesting example, it seems that var f = function(){} does not act the same as function f(){} in nested blocks. The latter tries to pollute the outer block but silently fail if there's name conflicting in non-strict mode:

// code below are tested in node.js non-strict mode,
// it may output different in other runtimes

const foo = 1; // same result as `let foo = 1`
if (true) {
  function foo() {}
}
console.log(foo); // 1

if (true) {
  function bar() {}
}
console.log(bar); // [Function: bar]

var buzz = 1
if (true) {
  function buzz() {}
}
console.log(buzz); // [Function: buzz]

And the doc says declaring functions in non-strict mode behaves differently across runtimes. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#block-level_function_declaration

So the problem is --minify-identifiers does not take outer block names into account in non-strict context. esbuild does var f = n simply for keeping non-strict behaviors as much as possible, since vars can pollute out of current block, but it will throw error when the pollution fails.

What if we keep the function declaration but only re-assign it to another let variable?

// original output
let n = function() {
};
var f = n;

// new output
function f() {};
let n = f;

@sapphi-red
Copy link
Contributor Author

What if we keep the function declaration but only re-assign it to another let variable?

// original output
let n = function() {
};
var f = n;

// new output
function f() {};
let n = f;

I think this would work. But I guess the function name was removed because leaving that will make the scope calculation difficult.

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

2 participants