Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "minor",
"updateInternalDependencies": "patch",
"ignore": []
}
6 changes: 6 additions & 0 deletions .changeset/long-points-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@lit-labs/ssr': patch
'lit-html': patch
---

Fix a memory leak when patching directive constructors for SSR.
36 changes: 11 additions & 25 deletions packages/labs/ssr/src/lib/render-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
*/

import type {TemplateResult, ChildPart, CompiledTemplateResult} from 'lit';
import type {
Directive,
DirectiveClass,
DirectiveResult,
} from 'lit/directive.js';
import type {Directive} from 'lit/directive.js';

import {nothing, noChange} from 'lit';
import {PartType} from 'lit/directive.js';
Expand All @@ -29,8 +25,7 @@ const {
marker,
markerMatch,
boundAttributeSuffix,
overrideDirectiveResolve,
setDirectiveClass,
patchDirectiveResolve,
getAttributePartCommittedValue,
resolveDirective,
AttributePart,
Expand Down Expand Up @@ -67,37 +62,28 @@ import {reflectedAttributeName} from './reflected-attributes.js';

import type {RenderResult} from './render-result.js';
import {isHydratable} from './server-template.js';
import type {Part} from 'lit-html';

declare module 'parse5/dist/tree-adapters/default.js' {
interface Element {
isDefinedCustomElement?: boolean;
}
}

const patchedDirectiveCache = new WeakMap<DirectiveClass, DirectiveClass>();
function ssrResolve(this: Directive, _part: Part, values: unknown[]) {
// Since the return value may also be a directive result in the case of nested
// directives, we may need to patch that as well.
return patchIfDirective(this.render(...values));
}

/**
* Looks for values of type `DirectiveResult` and replaces its Directive class
* with a subclass that calls `render` rather than `update`
* Looks for values of type `DirectiveResult` and patches its Directive class
* such that it calls `render` rather than `update`.
*/
const patchIfDirective = (value: unknown) => {
// This property needs to remain unminified.
const directiveCtor = getDirectiveClass(value);
if (directiveCtor !== undefined) {
let patchedCtor = patchedDirectiveCache.get(directiveCtor);
if (patchedCtor === undefined) {
patchedCtor = overrideDirectiveResolve(
directiveCtor,
(directive: Directive, values: unknown[]) => {
// Since the return value may also be a directive result in the case of
// nested directives, we may need to patch that as well
return patchIfDirective(directive.render(...values));
}
);
patchedDirectiveCache.set(directiveCtor, patchedCtor);
}
// This property needs to remain unminified.
setDirectiveClass(value as DirectiveResult, patchedCtor);
patchDirectiveResolve(directiveCtor, ssrResolve);
}
return value;
};
Expand Down
37 changes: 37 additions & 0 deletions packages/lit-html/src/private-ssr-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import type {
TemplateInstance,
} from './lit-html.js';

// Contains either the minified or unminified `_$resolve` Directive method name.
let resolveMethodName: Extract<keyof Directive, '_$resolve'> | null = null;

/**
* END USERS SHOULD NOT RELY ON THIS OBJECT.
*
Expand Down Expand Up @@ -57,6 +60,40 @@ export const _$LH = {
return resolveOverrideFn(this, values);
}
},
patchDirectiveResolve: (
directiveClass: typeof Directive,
resolveOverrideFn: (
this: Directive,
_part: Part,
values: unknown[]
) => unknown
) => {
if (directiveClass.prototype._$resolve !== resolveOverrideFn) {
resolveMethodName ??= directiveClass.prototype._$resolve
.name as NonNullable<typeof resolveMethodName>;
for (
let proto = directiveClass.prototype;
proto !== Object.prototype;
proto = Object.getPrototypeOf(proto)
) {
if (proto.hasOwnProperty(resolveMethodName)) {
proto[resolveMethodName] = resolveOverrideFn;
return;
}
}
// Nothing was patched which indicates an error. The most likely error is
// that somehow both minified and unminified lit code passed through this
// codepath. This is possible as lit-labs/ssr contains its own lit-html
// module as a dependency for server rendering client Lit code. If a
// client contains multiple duplicate Lit modules with minified and
// unminified exports, we currently cannot handle both.
throw new Error(
`Internal error: It is possible that both dev mode and production mode` +
` Lit was mixed together during SSR. Please comment on the issue: ` +
`https://github.com/lit/lit/issues/4527`
);
}
},
setDirectiveClass(value: DirectiveResult, directiveClass: DirectiveClass) {
// This property needs to remain unminified.
value['_$litDirective$'] = directiveClass;
Expand Down