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

fix(ssr-compiler): add SSR for lwc:component #4000

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

abdulsattar
Copy link
Contributor

@abdulsattar abdulsattar commented Feb 19, 2024

Details

Adds support for SSRing lwc:component to the #3880 branch.

Does this pull request introduce a breaking change?

  • ✅ No, it does not introduce a breaking change.

Does this pull request introduce an observable change?

  • ✅ No, it does not introduce an observable change.

GUS work item

divmain and others added 10 commits February 9, 2024 16:36
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
@abdulsattar abdulsattar requested a review from a team as a code owner February 19, 2024 11:30
@@ -133,4 +133,13 @@ export function addGenerateMarkupExport(
program.body.push(
bGenerateMarkup(attrsAugmentation, classIdentifier, renderCall, classIdentifier)
);
program.body.push(
Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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(
Copy link
Contributor

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 {
Copy link
Contributor

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(
Copy link
Contributor

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)}`;
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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>
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@divmain divmain force-pushed the divmain/ssr-compiler branch 3 times, most recently from 5b2f725 to 3377347 Compare April 25, 2024 07:37
Base automatically changed from divmain/ssr-compiler to master April 25, 2024 07:50
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

Successfully merging this pull request may close these issues.

None yet

3 participants