static `name` method that has the method name dynamically set is overridden with a string when `keepNames` is enabled
window.foo = 'name'
class Foo {
static [window.foo]() {}
}
console.log(Foo.name)
The input code outputs ƒ [window.foo]() {}. On the other hand, the output code outputs Foo.
(Note: this is not what I encountered in a real world usage. I found it when trying to know how keepNames feature works in esbuild.)
I guess the ƒ [window.foo]() {} printed on browser devtools is only because the devtool extracts the function body (func.toString()). In other environments it will be different, like it is [Function: name] in Node.js and [Function] in Bun.
The keepNames feature is used to preserve the behavior of <function>.name as much as possible, in which case Foo.name.name is always "name".
I guess the following example may be what you really want to check out:
globalThis.foo = 'name'
class Foo {
static get [globalThis.foo]() { return 'Bar' }
}
console.log(Foo.name)
Run with node input.mjs outputs Bar, while esbuild input.mjs --keep-names | node - outputs Foo. It is because esbuild injects the get name() getter function after the declaration of the class, in which case it overrides the original get name() defined by yourself.
The following code will run correctly with or without --keep-names:
class Foo {
static get name() { return 'Bar' }
}
console.log(Foo.name)
I guess the
ƒ [window.foo]() {}printed on browser devtools is only because the devtool extracts the function body (func.toString()). In other environments it will be different, like it is[Function: name]in Node.js and[Function]in Bun.The
keepNamesfeature is used to preserve the behavior of<function>.nameas much as possible, in which caseFoo.name.nameis always"name".
Yes the output is different depending on the environment. But you can see that the value of Foo.name changes (you can see it more explicitly when you add typeof before Foo.name).
I think keepNames feature intends to support not just <function>.name but also <class>.name.
Yes, I get you. The whole thing is if a class has a static field or method named (by indirect expression) "name", then the keepNames feature may mess up this field. Functions do not have this issue.
I guess either we just live with it, or esbuild disables keepNames in all classes with computed static fields which might be a field called "name".
I wonder if the __name helper can skip setting name when it already exists like:
var __defProp = Object.defineProperty;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __hasOwnProp.call(target, "name") ? {} : __defProp(target, "name", { value, configurable: true });
It works for this case:
var __defProp = Object.defineProperty;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __hasOwnProp.call(target, "name") ? {} : __defProp(target, "name", { value, configurable: true });
window.foo = "name";
class Foo {
static {
__name(this, "Foo");
}
static [window.foo]() {
}
}
console.log(Foo.name);
@sapphi-red That won't work.
> function foo() {}
> Object.hasOwn(foo, 'name')
true
Ah, true. Maybe injecting to the first dynamic static property key can work? This would run before the name static properties / methods.
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
window.foo = "name";
class Foo {
static [(__name(this, 'Foo'), window.foo)]() {}
}
console.log(Foo.name)