Skip to content

Commit

Permalink
fix(ssr): correctly render foreign namespace element closing tags (#4992
Browse files Browse the repository at this point in the history
)

* fix: correctly render foreign element closing tags

* fix: properly handle scoped class for void and foreign elements

* fix: address review comments

* fix: remove math example for now

* fix: ignore UNKNOWN_HTML_TAG_IN_TEMPLATE warning to allow for MathML test

* fix: update tag warning suppression to reference issue

* fix: simpler MathML example
  • Loading branch information
jhefferman-sfdc authored Dec 10, 2024
1 parent b8362b5 commit ee2edae
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 10 deletions.
4 changes: 3 additions & 1 deletion packages/@lwc/engine-server/src/__tests__/fixtures.spec.ts
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');
if (!shouldIgnoreWarning) {
throw new Error(message);
}
Expand Down
Empty file.
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>
</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 @@ -32,7 +31,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);
};

0 comments on commit ee2edae

Please sign in to comment.