lwc
lwc copied to clipboard
Component compiles incorrectly for `export { ... as default }` ESM syntax
Description
Consider this component:
class Component extends LightningElement {}
export { Component as default }
Per ES module syntax, it is equivalent to this component:
export default class Component extends LightningElement {}
However, the second one correctly renders, whereas the first does not. (Repro: 08c93cd41 .) The component appears to not be treated as a LightningElement per the @lwc/compiler – it is simply treated as a plain JavaScript (non-LWC) module.
Steps to Reproduce
08c93cd41
Expected Results
export default class Component extends LightningElement {}
is equivalent to
class Component extends LightningElement {}
export { Component as default }
Actual Results
Nothing is rendered, and the component is not registered at runtime as a proper LightningElement.
Version
This issue exists in master today, as well as LWC v5.0.9 (Spring '24) and v3.0.4 (Winter '24). It even repros in LWC v2.11.8 (Summer '22). So this is a longstanding issue.
Possible Solution
Use the export default class ... syntax instead.
This issue has been linked to a new work item: W-15135580
I think to solve this, a bare-minimum solution would be:
- Detect usage of
export { Foo as default } - Check the same JS module to see if there is a
class Foo extends LightningElement {}
(The solution would likely be in @lwc/babel-plugin-component.)
I think other, exotic patterns are not strictly necessary to support:
// const Foo
const Foo = class extends LightningElement {}
export { Foo as default }
// Foo imported from elsewhere
import { Foo } from './elsewhere.js'
export { Foo as default }
// really zany
const Foo = (() => { return true && class extends LightningElement {} })()
export { Foo as default }
We can probably also support some other simple patterns:
class Foo extends LightningElement {}
export default Foo
Maybe even:
const Foo = class extends LightningElement {}
export default Foo
What are the behavioral differences (in ESM land, outside the LWC context) between export default const Foo = ... vs export { default as Foo }? In particular, when we compile could we just change export { Foo as default } to export default class Foo {} or would that cause side effects?
@wjhsf We can't always safely change the ordering, because it matters in certain cases. For example:
export default Foo
class Foo {}
^ This is a syntax error (Cannot access 'Foo' before initialization). (Some minifiers like Terser and SWC will "helpfully" fix it for you though. 🙃)
In particular, when we compile could we just change
export { Foo as default }toexport default class Foo {}or would that cause side effects?
Maybe, but I wonder if it's better to just leave things alone, since otherwise we could cause other unforeseen problems.
Hi @nolanlawson i would like to contribute and help fix more bugs please let me know where i can be helpful.