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
fix(ssr-compiler): add SSR for lwc:component
#4000
base: master
Are you sure you want to change the base?
Conversation
chore: add new keywords to package.json Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> chore: remove unnecessary non-null assertion Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> chore: improve internal type signature chore: address minor PR feedback chore: address more PR feedback chore: move serverSideRenderComponent to @lwc/ssr-runtime chore: pretty chore: remove acorn from @lwc/ssr-compiler chore: remove the test command from subpackages chore: move reserved keywords over to @lwc/shared chore: revert unnecessary rollup change chore: remove non-null assertions chore: address PR feedback chore: address PR feedback chore: address PR feedback chore: address PR feedback chore: clean up types in estemplate chore: minor cleanup chore: support backslash path separators chore: remove redundant NonNullable chore: minor cleanup fix: prop values of null or undefined should be rendered as empty text node chore: add a bunch of comments
@@ -133,4 +133,13 @@ export function addGenerateMarkupExport( | |||
program.body.push( | |||
bGenerateMarkup(attrsAugmentation, classIdentifier, renderCall, classIdentifier) | |||
); | |||
program.body.push( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this because dynamic components are imported in the JS file like so:
export class extends LightningElement {
x() {
ctor = await import('x/test');
}
}
This is a default import and it'll import only the LightningElement
. But for SSR, we want the generateMarkup
function for that component as well which is exported as a named export and isn't present in the ctor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I might be missing a piece of context for how we handle dynamic imports. Why is the resolved value of import('x/test')
the default export from that component? Normally, the resolved value would be the module namespace object, which should include generateMarkup
if that's a named export.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, people actually do:
const {default: ctor} = await import('x/test');
and use the ctor
like so:
<lwc:component is={ctor}></lwc:component>
So, the engine doesn't have access to the module namespace object, but just the default export.
@@ -133,4 +133,13 @@ export function addGenerateMarkupExport( | |||
program.body.push( | |||
bGenerateMarkup(attrsAugmentation, classIdentifier, renderCall, classIdentifier) | |||
); | |||
program.body.push( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I might be missing a piece of context for how we handle dynamic imports. Why is the resolved value of import('x/test')
the default export from that component? Normally, the resolved value would be the module namespace object, which should include generateMarkup
if that's a named export.
name: string; | ||
namespace: string; | ||
} | ||
export interface TemplateTransformOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once/if ssr-compiler
is the official path forward, we might want to define this interface as a subset of the compiler's TransformOptions
, possibly by pulling it out of both into @lwc/shared
or something like that.
For now, I think defining the interface here works well and should be left as is.
cxt.hoist(componentImport, childGeneratorLocalName); | ||
const childTagName = node.name; | ||
|
||
export function ImportedComponent( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comment here to disambiguate ImportedComponent
from Component
?
|
||
export const Component: Transformer<IrComponent> = function Component(node, cxt) { | ||
// Import the custom component's generateMarkup export. | ||
const childGeneratorLocalName = `generateMarkup_${toPropertyName(node.name)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think node.name
can have hyphens in it here. If so, that would be an invalid JS identifier, so we'll need to transform it into something compliant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed it to use a uid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And added a test for a component that has two child components.
return directive.name === 'Is'; | ||
} | ||
export const LwcDynamic: Transformer<IrComponent> = function LwcDynamic(node, cxt) { | ||
const dynamicDirective = node.directives.find(isLwcIsDirective); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is more than one lwc:is
attribute, this will just return the first and ignore any that follow. Is this the same behavior as in engine-core
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parser throws LWC1058: Invalid HTML syntax: duplicate-attribute. For more information
in both cases.
@@ -1,3 +1,4 @@ | |||
<template> | |||
<x-child></x-child> | |||
<x-anotherchild></x-anotherchild> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a test here for <lwc:component>
...?
BTW this is a good example of why it'd be nice to have the compiler output as part of the snapshots... would help with visualizing the generated code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tests the import statement we add when we see a child in the template. lwc:component
are imported in the JS by the user.
Specifically, here db3322f#diff-17a5fc2e6a05c3f1e674d6942273c831cb914d8d3c3286062c429e5054465bdfR101, we increment UID to add unique import statements for each child.
e.g.
import { generateMarkup as generateMarkup_1 } from 'x/child';
import { generateMarkup as generateMarkup_2 } from 'x/anotherchild';
Definitely agree with having the compiler output in snapshots. This would have been clearer.
5b2f725
to
3377347
Compare
Details
Adds support for SSRing
lwc:component
to the #3880 branch.Does this pull request introduce a breaking change?
Does this pull request introduce an observable change?
GUS work item