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): correctly render foreign namespace element closing tags #4992

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ async function compileFixture({
// IGNORED_SLOT_ATTRIBUTE_IN_CHILD is fine; it is used in some of these tests
message.includes('LWC1201') ||
message.includes('-h-t-m-l') ||
code === 'CIRCULAR_DEPENDENCY';
code === 'CIRCULAR_DEPENDENCY' ||
// TODO [#5010]: template-compiler -> index -> validateElement generates UNKNOWN_HTML_TAG_IN_TEMPLATE for MathML elements
message.includes('LWC1123');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is bizarre... we should fix this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, updated the comment with a TODO on that issue.

if (!shouldIgnoreWarning) {
throw new Error(message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<x-elements class="lwc-4q9u0sqsfc0-host" data-lwc-host-scope-token="lwc-4q9u0sqsfc0-host">
<template shadowrootmode="open">
<style class="lwc-4q9u0sqsfc0" type="text/css">
:host {background: blue;}
</style>
<a class="lwc-4q9u0sqsfc0" href="https://www.salesforce.com/">
</a>
<label class="lwc-4q9u0sqsfc0" for="textInput">
Some input
</label>
<input class="lwc-4q9u0sqsfc0" id="textInput" type="text">
<svg class="lwc-4q9u0sqsfc0">
<pattern class="lwc-4q9u0sqsfc0">
<image class="lwc-4q9u0sqsfc0" xlink:href="https://www.salesforce.com/"/>
</pattern>
<image class="lwc-4q9u0sqsfc0" xlink:title="title"/>
</svg>
<math class="lwc-4q9u0sqsfc0">
<mrow class="lwc-4q9u0sqsfc0">
<mi class="lwc-4q9u0sqsfc0">
a
</mi>
<mo class="lwc-4q9u0sqsfc0">
+
</mo>
<mi class="lwc-4q9u0sqsfc0">
b
</mi>
</mrow>
</math>
<svg class="lwc-4q9u0sqsfc0">
<pattern class="lwc-4q9u0sqsfc0">
<image xlink:href="https://www.salesforce.com/">
</image>
</pattern>
<image class="lwc-4q9u0sqsfc0" xlink:title="title"/>
</svg>
</template>
</x-elements>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const tagName = 'x-elements';
export { default } from 'x/elements';
export * from 'x/elements';
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template>
<!-- Empty non-void HTML namespace element -->
<a href="https://www.salesforce.com/"></a>
<!-- Non-empty non-void HTML namespace element -->
<label for="textInput">Some input</label>
<!-- Void HTML namespace element -->
<input id="textInput" type="text">
<!-- Foreign namespace elements -->
<svg>
<!-- Non-empty foreign element -->
<pattern>
<image xlink:href="https://www.salesforce.com/"></image>
</pattern>
<!-- Empty foreign element -->
<image xlink:title="title"></image>
</svg>
<math>
<!-- TODO [#5015]: Add void example -->
<mrow>
<mi>a</mi>
<mo>+</mo>
<mi>b</mi>
</mrow>
</math>
<!-- Rendered with inner-html -->
<svg>
<pattern lwc:inner-html={foreignNamespaceInnerHtml}></pattern>
<image xlink:title="title"></image>
</svg>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to add a MathML block here for completeness.

Copy link
Contributor Author

@jhefferman-sfdc jhefferman-sfdc Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

including <math> namespace elements passed for ssr-compiler but generates a warning when engine-server fixtures are ran as it appears MathML elements are not recognized as a knownTag by template-compiler -> validateElement. To enable testing MathML for both I suppressed the warning here.

Is that OK or would you rather modify template-compiler -> validateElement to recognize MathML elements, or...?

I suppose it could be argued that the same warning / tag-check should be there for @lwc/ssr-compiler too, or not needed? I'm not sure as tags used in the correct namespace shouldn't really generate a warning, so should we be reproducing this behavior in v2 regardless?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, opened an issue for this: #5010

MathML should be supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thanks, seems wrong. I updated the warning suppression in the fixture to reference the issue you opened.

</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { LightningElement, api } from 'lwc';

export default class extends LightningElement {
@api foreignNamespaceInnerHtml = '<image xlink:href="https://www.salesforce.com/"></image>';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
background: blue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export const expectedFailures = new Set([
'attribute-component-global-html/index.js',
'attribute-global-html/as-component-prop/undeclared/index.js',
'attribute-global-html/as-component-prop/without-@api/index.js',
'attribute-namespace/index.js',
'attribute-style/basic/index.js',
'attribute-style/dynamic/index.js',
'exports/component-as-default/index.js',
Expand All @@ -35,7 +34,6 @@ export const expectedFailures = new Set([
'superclass/render-in-superclass/no-template-in-subclass/index.js',
'superclass/render-in-superclass/unused-default-in-subclass/index.js',
'superclass/render-in-superclass/unused-default-in-superclass/index.js',
'svgs/index.js',
'wire/errors/throws-on-computed-key/index.js',
'wire/errors/throws-when-colliding-prop-then-method/index.js',
'wire/errors/throws-when-computed-prop-is-expression/index.js',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,6 @@ export const Element: Transformer<IrElement | IrExternalComponent | IrSlot> = fu
return result;
});

if (isVoidElement(node.name, HTML_NAMESPACE)) {
return [bYield(b.literal(`<${node.name}`)), ...yieldAttrsAndProps, bYield(b.literal(`>`))];
}

let childContent: EsStatement[];
// An element can have children or lwc:inner-html, but not both
// If it has both, the template compiler will throw an error before reaching here
Expand All @@ -246,13 +242,17 @@ export const Element: Transformer<IrElement | IrExternalComponent | IrSlot> = fu
childContent = [];
}

const isForeignSelfClosingElement =
node.namespace !== HTML_NAMESPACE && childContent.length === 0;
const isSelfClosingElement =
isVoidElement(node.name, HTML_NAMESPACE) || isForeignSelfClosingElement;

return [
bYield(b.literal(`<${node.name}`)),
// If we haven't already prefixed the scope token to an existing class, add an explicit class here
...(hasClassAttribute ? [] : [bConditionallyYieldScopeTokenClass()]),
...yieldAttrsAndProps,
bYield(b.literal(`>`)),
...childContent,
bYield(b.literal(`</${node.name}>`)),
bYield(b.literal(isForeignSelfClosingElement ? `/>` : `>`)),
...(isSelfClosingElement ? [] : [...childContent, bYield(b.literal(`</${node.name}>`))]),
].filter(Boolean);
};