/
experimental-hydrate-support.ts
122 lines (112 loc) · 4.11 KB
/
experimental-hydrate-support.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* LitElement support for hydration of content rendered using lit-ssr.
*
* @packageDocumentation
*
* @deprecated Moved to `@lit-labs/ssr-client/lit-element-hydrate-support.js`.
*/
import type {PropertyValues} from '@lit/reactive-element';
import {render, RenderOptions} from 'lit-html';
import {hydrate} from 'lit-html/experimental-hydrate.js';
import {HYDRATE_INTERNALS_ATTR_PREFIX} from '@lit-labs/ssr-dom-shim';
interface PatchableLitElement extends HTMLElement {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-misused-new
new (...args: any[]): PatchableLitElement;
enableUpdating(requestedUpdate?: boolean): void;
createRenderRoot(): Element | ShadowRoot;
renderRoot: HTMLElement | DocumentFragment;
render(): unknown;
renderOptions: RenderOptions;
_$needsHydration: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
globalThis.litElementHydrateSupport = ({
LitElement,
}: {
LitElement: PatchableLitElement;
}) => {
const observedAttributes = Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(LitElement),
'observedAttributes'
)!.get!;
// Add `defer-hydration` to observedAttributes
Object.defineProperty(LitElement, 'observedAttributes', {
get() {
return [...observedAttributes.call(this), 'defer-hydration'];
},
});
// Enable element when 'defer-hydration' attribute is removed by calling the
// super.connectedCallback()
const attributeChangedCallback =
LitElement.prototype.attributeChangedCallback;
LitElement.prototype.attributeChangedCallback = function (
name: string,
old: string | null,
value: string | null
) {
if (name === 'defer-hydration' && value === null) {
connectedCallback.call(this);
}
attributeChangedCallback.call(this, name, old, value);
};
// Override `connectedCallback` to capture whether we need hydration, and
// defer `super.connectedCallback()` if the 'defer-hydration' attribute is set
const connectedCallback = LitElement.prototype.connectedCallback;
LitElement.prototype.connectedCallback = function (
this: PatchableLitElement
) {
// If the outer scope of this element has not yet been hydrated, wait until
// 'defer-hydration' attribute has been removed to enable
if (!this.hasAttribute('defer-hydration')) {
connectedCallback.call(this);
}
};
// If we've been server-side rendered, just return `this.shadowRoot`, don't
// call the base implementation, which would also adopt styles (for now)
const createRenderRoot = LitElement.prototype.createRenderRoot;
LitElement.prototype.createRenderRoot = function (this: PatchableLitElement) {
if (this.shadowRoot) {
this._$needsHydration = true;
return this.shadowRoot;
} else {
return createRenderRoot.call(this);
}
};
// Hydrate on first update when needed
const update = Object.getPrototypeOf(LitElement.prototype).update;
LitElement.prototype.update = function (
this: PatchableLitElement,
changedProperties: PropertyValues
) {
const value = this.render();
// Since this is a patch, we can't call super.update(), so we capture
// it off the proto chain and call it instead
update.call(this, changedProperties);
if (this._$needsHydration) {
this._$needsHydration = false;
// Remove aria attributes added by internals shim during SSR
for (let i = 0; i < this.attributes.length; i++) {
const attr = this.attributes[i];
if (attr.name.startsWith(HYDRATE_INTERNALS_ATTR_PREFIX)) {
const ariaAttr = attr.name.slice(
HYDRATE_INTERNALS_ATTR_PREFIX.length
);
this.removeAttribute(ariaAttr);
this.removeAttribute(attr.name);
}
}
hydrate(value, this.renderRoot, this.renderOptions);
} else {
render(value, this.renderRoot, this.renderOptions);
}
};
};
console.warn(
'Import from `lit-element/experimental-hydrate-support.js` is deprecated.' +
'Import `@lit-labs/ssr-client/lit-element-hydrate-support.js` instead.'
);