Skip to content

Commit

Permalink
fix(ssr): only default-exported classes are LWC components @W-1731235…
Browse files Browse the repository at this point in the history
…8@ (#4942)

* fix(ssr): only default-exported classes are LWC components @W-17312358@

fixes #4398

* test(ssr): add fixture for `export default Component`

* test(ssr): add test for component-like-but-not scenarios

* test(ssr): use non-empty template to prove it works

* test(ssr): test `export { Component as default }`

* test(ssr): add more tests for exports

* chore: ignore failing test
  • Loading branch information
wjhsf authored Nov 26, 2024
1 parent 252545e commit b04442f
Show file tree
Hide file tree
Showing 30 changed files with 117 additions and 10 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<x-cmp>
<template shadowrootmode="open">
</template>
</x-cmp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tagName = 'x-cmp';
export { default } from 'x/cmp';
export * from 'x/cmp';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
This template isn't actually used because `export {Component as default}` isn't recognized as an LWC component.
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { LightningElement } from 'lwc';

class Component extends LightningElement {}
export {Component as default}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<x-cmp>
<template shadowrootmode="open">
As the children proclaim, you only live once!
</template>
</x-cmp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tagName = 'x-cmp';
export { default } from 'x/cmp';
export * from 'x/cmp';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
As the children proclaim, you only live once!
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { LightningElement } from 'lwc';

export default class Component extends LightningElement {}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<x-cmp>
<template shadowrootmode="open">
As the children proclaim, you only live once!
</template>
</x-cmp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tagName = 'x-cmp';
export { default } from 'x/cmp';
export * from 'x/cmp';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
As the children proclaim, you only live once!
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { LightningElement } from 'lwc';

export default class extends LightningElement {}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<x-cmp>
<template shadowrootmode="open">
As the children proclaim, you only live once!
</template>
</x-cmp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tagName = 'x-cmp';
export { default } from 'x/cmp';
export * from 'x/cmp';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
As the children proclaim, you only live once!
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { LightningElement } from 'lwc';

class Component extends LightningElement {}
export default Component;
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<x-component data-lwc-host-mutated="data-yolo" data-yolo>
<template shadowrootmode="open">
yay ook ook ook
</template>
</x-component>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tagName = 'x-component';
export { default } from 'x/component';
export * from 'x/component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
{something} {data}
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { LightningElement } from 'lwc'
import { FancyMixin } from 'x/mixinSuperclass'
import { NotDefault } from 'x/notLwcClass'
import Superless from 'x/superless'

export default class extends FancyMixin(LightningElement) {
something = new NotDefault().prop
data = new Superless().method()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const FancyMixin = Class => {
return class extends Class {
connectedCallback() {
this.setAttribute('data-yolo', '')
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class NotLightningElement {}
export class NotDefault extends NotLightningElement {
prop = 'yay'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Base {}

class NotExported extends Base {
static value = 'ook ook ook'
}

export default class {
method() {
return NotExported.value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const expectedFailures = new Set([
'dynamic-components/mixed/index.js',
'dynamic-slots/index.js',
'empty-text-with-comments-non-static-optimized/index.js',
'exports/component-as-default/index.js',
'if-conditional-slot-content/index.js',
'known-boolean-attributes/default-def-html-attributes/static-on-component/index.js',
'render-dynamic-value/index.js',
Expand Down
27 changes: 17 additions & 10 deletions packages/@lwc/ssr-compiler/src/compile-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,23 @@ const visitors: Visitors = {
},
ClassDeclaration(path, state) {
const { node } = path;
if (!node?.superClass) {
return;
}
// Assume everything with a superclass is an LWC component
state.isLWC = true;
if (node.id) {
state.lwcClassName = node.id.name;
} else {
node.id = b.identifier('DefaultComponentName');
state.lwcClassName = 'DefaultComponentName';
if (
node?.superClass &&
// export default class extends LightningElement {}
(is.exportDefaultDeclaration(path.parentPath) ||
// class Cmp extends LightningElement {}; export default Cmp
path.scope
?.getBinding(node.id.name)
?.references.some((ref) => is.exportDefaultDeclaration(ref.parent)))
) {
// If it's a default-exported class with a superclass, then it's an LWC component!
state.isLWC = true;
if (node.id) {
state.lwcClassName = node.id.name;
} else {
node.id = b.identifier('DefaultComponentName');
state.lwcClassName = 'DefaultComponentName';
}
}
},
PropertyDefinition(path, state) {
Expand Down

0 comments on commit b04442f

Please sign in to comment.