diff --git a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts index 96c55c048b..7194523c11 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts @@ -29,9 +29,6 @@ export const expectedFailures = new Set([ 'slot-not-at-top-level/with-adjacent-text-nodes/lwcIf/light/index.js', 'slot-not-at-top-level/with-adjacent-text-nodes/if/light/index.js', 'slot-not-at-top-level/with-adjacent-text-nodes/if-as-sibling/light/index.js', - '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', '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', diff --git a/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts b/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts index 2591b1d328..34012923be 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts @@ -8,15 +8,11 @@ import { parse as pathParse } from 'node:path'; import { is, builders as b } from 'estree-toolkit'; import { esTemplate } from '../estemplate'; -import { isIdentOrRenderCall } from '../estree/validators'; import { bImportDeclaration } from '../estree/builders'; import { bWireAdaptersPlumbing } from './wire'; import type { Program, - SimpleCallExpression, - Identifier, - MemberExpression, Statement, ExpressionStatement, IfStatement, @@ -24,11 +20,6 @@ import type { } from 'estree'; import type { ComponentMetaState } from './types'; -/** Node representing `.render()`. */ -type RenderCallExpression = SimpleCallExpression & { - callee: MemberExpression & { property: Identifier & { name: 'render' } }; -}; - const bGenerateMarkup = esTemplate` async function* generateMarkup( tagName, @@ -62,7 +53,10 @@ const bGenerateMarkup = esTemplate` instance.connectedCallback(); __mutationTracker.disable(instance); } - const tmplFn = ${isIdentOrRenderCall} ?? ${/*component class*/ 3}[__SYMBOL__DEFAULT_TEMPLATE] ?? __fallbackTmpl; + // If a render() function is defined on the class or any of its superclasses, then that takes priority. + // Next, if the class or any of its superclasses has an implicitly-associated template, then that takes + // second priority (e.g. a foo.html file alongside a foo.js file). Finally, there is a fallback empty template. + const tmplFn = instance.render?.() ?? ${/*component class*/ 3}[__SYMBOL__DEFAULT_TEMPLATE] ?? __fallbackTmpl; yield \`<\${tagName}\`; const hostHasScopedStylesheets = @@ -109,7 +103,7 @@ export function addGenerateMarkupFunction( tagName: string, filename: string ) { - const { hasRenderMethod, privateFields, publicFields, tmplExplicitImports } = state; + const { privateFields, publicFields, tmplExplicitImports } = state; // The default tag name represents the component name that's passed to the transformer. // This is needed to generate markup for dynamic components which are invoked through @@ -117,17 +111,11 @@ export function addGenerateMarkupFunction( // At the time of generation, the invoker does not have reference to its tag name to pass as an argument. const defaultTagName = b.literal(tagName); const classIdentifier = b.identifier(state.lwcClassName!); - const tmplVar = b.identifier('tmpl'); - const renderCall = hasRenderMethod - ? (b.callExpression( - b.memberExpression(b.identifier('instance'), b.identifier('render')), - [] - ) as RenderCallExpression) - : tmplVar; let exposeTemplateBlock: IfStatement | null = null; if (!tmplExplicitImports) { const defaultTmplPath = `./${pathParse(filename).name}.html`; + const tmplVar = b.identifier('tmpl'); program.body.unshift(bImportDeclaration({ default: tmplVar.name }, defaultTmplPath)); program.body.unshift( bImportDeclaration({ SYMBOL__DEFAULT_TEMPLATE: '__SYMBOL__DEFAULT_TEMPLATE' }) @@ -160,8 +148,7 @@ export function addGenerateMarkupFunction( b.arrayExpression(publicFields.map(b.literal)), b.arrayExpression(privateFields.map(b.literal)), classIdentifier, - connectWireAdapterCode, - renderCall + connectWireAdapterCode ) ); diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts index 047ac3146c..3f2bd8694c 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/index.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -165,9 +165,6 @@ const visitors: Visitors = { case 'connectedCallback': state.hasConnectedCallback = true; break; - case 'render': - state.hasRenderMethod = true; - break; case 'renderedCallback': state.hadRenderedCallback = true; path.remove(); @@ -221,7 +218,6 @@ export default function compileJS( isLWC: false, hasConstructor: false, hasConnectedCallback: false, - hasRenderMethod: false, hadRenderedCallback: false, hadDisconnectedCallback: false, hadErrorCallback: false, diff --git a/packages/@lwc/ssr-compiler/src/compile-js/types.ts b/packages/@lwc/ssr-compiler/src/compile-js/types.ts index 7e1e7f2c1b..6ff7723f08 100644 --- a/packages/@lwc/ssr-compiler/src/compile-js/types.ts +++ b/packages/@lwc/ssr-compiler/src/compile-js/types.ts @@ -32,8 +32,6 @@ export interface ComponentMetaState { hasConstructor: boolean; // indicates whether the subclass has a connectedCallback method hasConnectedCallback: boolean; - // indicates whether the subclass has a render method - hasRenderMethod: boolean; // indicates whether the subclass has a renderedCallback method hadRenderedCallback: boolean; // indicates whether the subclass has a disconnectedCallback method diff --git a/packages/@lwc/ssr-compiler/src/estree/validators.ts b/packages/@lwc/ssr-compiler/src/estree/validators.ts index f66d6e0f33..69fd7c813c 100644 --- a/packages/@lwc/ssr-compiler/src/estree/validators.ts +++ b/packages/@lwc/ssr-compiler/src/estree/validators.ts @@ -7,32 +7,9 @@ import { is } from 'estree-toolkit'; import { entries } from '@lwc/shared'; -import type { CallExpression, Identifier, MemberExpression } from 'estree'; import type { Checker } from 'estree-toolkit/dist/generated/is-type'; import type { Node } from 'estree-toolkit/dist/helpers'; // estree's `Node` is not compatible? -/** Node representing an identifier named "render". */ -type RenderIdentifier = Identifier & { name: 'render' }; -/** Node representing a member expression `.render`. */ -type RenderMemberExpression = MemberExpression & { property: RenderIdentifier }; -/** Node representing a method call `.render()`. */ -type RenderCall = CallExpression & { callee: RenderMemberExpression }; - -/** Returns `true` if the node is an identifier or `.render()`. */ -export const isIdentOrRenderCall = ( - node: Node | null | undefined -): node is Identifier | RenderCall => { - return ( - is.identifier(node) || - (is.callExpression(node) && - is.memberExpression(node.callee) && - is.identifier(node.callee.property) && - node.callee.property.name === 'render') - ); -}; - -isIdentOrRenderCall.__debugName = 'identifier or .render() call'; - /** A validator that returns `true` if the node is `null`. */ type NullableChecker = (node: Node | null | undefined) => node is T | null;