@lwrjs/client-modules 0.20.0 → 0.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{BOOTSTRAP_END as e,INIT as o,INIT_MODULE as
|
|
1
|
+
import{BOOTSTRAP_END as e,INIT as o,INIT_MODULE as t}from"lwr/metrics";import{logOperationStart as r,logOperationEnd as n}from"lwr/profiler";import{hydrateComponent as i,createElement as s}from"lwc";const c="lwr:hydrate",l="visible";function a(e,o,t){i(e,o,t)}function d(e,o,t){const r=f||(f=new IntersectionObserver(((e,o)=>{e.forEach((e=>{if(e.isIntersecting){const t=e.target;o.unobserve(t);const r=p.get(t);if(r){p.delete(t);const{ctor:e,props:o}=r;a(t,e,o)}}}))}),{root:null,rootMargin:"100px"}),f);p.set(e,{ctor:o,props:t}),r.observe(e)}const p=new Map;let f;function u(e){return"IntersectionObserver"in globalThis&&e.getAttribute(c)===l}const m=(()=>{const e=globalThis,{SSREnabled:o}=e.LWR&&e.LWR.env||{};if(!globalThis.performance||!o)return()=>!1;let t=globalThis.performance.now();return()=>{const e=globalThis.performance.now();return e-t>50&&(t=e,!0)}})();function b(e,o){return s(e,{is:o})}function h(e){return e.replace(/\/v\/[a-zA-Z0-9-_.]+$/,"").replace("/","-").replace(/([A-Z])/g,(e=>`-${e.toLowerCase()}`))}const g=/-([a-z])/g;function w(e){return e.replace(g,(e=>e[1].toUpperCase()))}function v(i,s={}){void 0!==globalThis.customElements&&void 0!==globalThis.document?(r({id:o}),(async()=>{let e=0;const o=globalThis.document;for(const[c,l]of i){m()&&await y();const i=++e,p=h(c);if(!o.body.querySelector(p)){r({id:t,specifier:c,specifierIndex:i});const e=b(p,l),s=o.querySelector("[lwr-root]");s?s.appendChild(e):o.body.appendChild(e),n({id:t,specifier:c,specifierIndex:i,metadata:{renderMode:"spa"}});continue}const f=o.querySelectorAll(p);for(const e of f){r({id:t,specifier:c,specifierIndex:i});const o=e.dataset.lwrPropsId;if(o){u(e)?d(e,l,s[o]||{}):a(e,l,s[o]||{}),n({id:t,specifier:c,specifierIndex:i,metadata:{renderMode:"ssr"}});continue}const f=b(p,l);for(const{name:o,value:t}of e.attributes){f.setAttribute(o,t);const e=w(o);e in f&&(f[e]=t)}for(;e.childNodes.length>0;)f.appendChild(e.childNodes[0]);const m=e.parentElement;m&&m.replaceChild(f,e),n({id:t,specifier:c,specifierIndex:i,metadata:{renderMode:"csr"}})}}})(),n({id:o}),r({id:e})):r({id:e})}async function y(){const e=globalThis.scheduler;return function(e){let o,t=e[0],r=1;for(;r<e.length;){const n=e[r],i=e[r+1];if(r+=2,("optionalAccess"===n||"optionalCall"===n)&&null==t)return;"access"===n||"optionalAccess"===n?(o=t,t=i(t)):"call"!==n&&"optionalCall"!==n||(t=i(((...e)=>t.call(o,...e))),o=void 0)}return t}([e,"optionalAccess",e=>e.yield])?e.yield():new Promise((e=>setTimeout(e,0)))}export{c as HYDRATE_DIRECTIVE,l as HYDRATE_VISIBLE_VALUE,w as getPropFromAttrName,v as init,h as toKebabCase};
|
|
2
2
|
//# sourceMappingURL=init.js.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { LightningElement } from 'lwc';
|
|
2
2
|
import type { ServerData } from '@lwrjs/types';
|
|
3
|
+
export declare const HYDRATE_DIRECTIVE = "lwr:hydrate";
|
|
4
|
+
export declare const HYDRATE_VISIBLE_VALUE = "visible";
|
|
3
5
|
/**
|
|
4
6
|
* Convert a module specifier into a valid CustomElement registry name:
|
|
5
7
|
* - remove any version linking
|
|
@@ -7,10 +7,76 @@ import { createElement } from 'lwc';
|
|
|
7
7
|
// Note: a build step uses these comments to strip the code for core.
|
|
8
8
|
// eslint-disable-next-line lwr/only-allowed-imports
|
|
9
9
|
import { hydrateComponent } from 'lwc';
|
|
10
|
+
// hydration directive + value constants
|
|
11
|
+
// must align with the constants in @lwrjs/shared-utils/src/html-meta.ts
|
|
12
|
+
export const HYDRATE_DIRECTIVE = 'lwr:hydrate';
|
|
13
|
+
export const HYDRATE_VISIBLE_VALUE = 'visible';
|
|
10
14
|
function hydrateComponentProxy(customElement, Ctor, props) {
|
|
11
15
|
hydrateComponent(customElement, Ctor, props);
|
|
12
16
|
}
|
|
13
17
|
// </hydrateComponentProxy>
|
|
18
|
+
/**
|
|
19
|
+
* Hydrate the custom element only when it is visible.
|
|
20
|
+
* @param customElement - The custom element to hydrate
|
|
21
|
+
* @param ctor - The constructor of the custom element
|
|
22
|
+
* @param props - The properties of the custom element
|
|
23
|
+
*/
|
|
24
|
+
function hydrateComponentOnVisible(customElement, ctor, props) {
|
|
25
|
+
// Use IntersectionObserver for visibility-based hydration
|
|
26
|
+
const observer = createVisibilityObserver();
|
|
27
|
+
// add the element to the pending hydrations and observe it
|
|
28
|
+
pendingHydrations.set(customElement, { ctor, props });
|
|
29
|
+
observer.observe(customElement);
|
|
30
|
+
}
|
|
31
|
+
// store component metadata for pending hydrations
|
|
32
|
+
const pendingHydrations = new Map();
|
|
33
|
+
// store the visibility observer so that we don't create a new one each time
|
|
34
|
+
let visibilityObserver;
|
|
35
|
+
/**
|
|
36
|
+
* Determines if a component should be hydrated when it becomes visible in the viewport.
|
|
37
|
+
* This requires IntersectionObserver to be available and the hydrate directive to be set to 'visible'.
|
|
38
|
+
* @param element - The element to check for visibility-based hydration
|
|
39
|
+
* @returns True if the component should be hydrated when visible, false otherwise
|
|
40
|
+
*/
|
|
41
|
+
function shouldHydrateComponentWhenVisible(element) {
|
|
42
|
+
return ('IntersectionObserver' in globalThis &&
|
|
43
|
+
element.getAttribute(HYDRATE_DIRECTIVE) === HYDRATE_VISIBLE_VALUE);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create an intersection observer for hydrating islands when visible, if one doesn't already exist.
|
|
47
|
+
* @returns An intersection observer that will hydrate the island when visible
|
|
48
|
+
*/
|
|
49
|
+
function createVisibilityObserver() {
|
|
50
|
+
// return the existing observer if it already exists
|
|
51
|
+
if (visibilityObserver)
|
|
52
|
+
return visibilityObserver;
|
|
53
|
+
// create a new observer if it doesn't already exist
|
|
54
|
+
visibilityObserver = new IntersectionObserver((entries, observer) => {
|
|
55
|
+
// for each observed element, check if it is intersecting with the viewport
|
|
56
|
+
entries.forEach((entry) => {
|
|
57
|
+
// if intersecting, hydrate the island
|
|
58
|
+
if (entry.isIntersecting) {
|
|
59
|
+
const element = entry.target;
|
|
60
|
+
// stop observing the element
|
|
61
|
+
observer.unobserve(element);
|
|
62
|
+
// get the hydration data for the element
|
|
63
|
+
const hydrationData = pendingHydrations.get(element);
|
|
64
|
+
if (hydrationData) {
|
|
65
|
+
// delete the hydration data for the element
|
|
66
|
+
pendingHydrations.delete(element);
|
|
67
|
+
const { ctor, props } = hydrationData;
|
|
68
|
+
// hydrate the island
|
|
69
|
+
hydrateComponentProxy(element, ctor, props);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}, {
|
|
74
|
+
root: null,
|
|
75
|
+
// adds a buffer to the intersection observer to hydrate the island slightly before it enters the viewport
|
|
76
|
+
rootMargin: '100px',
|
|
77
|
+
});
|
|
78
|
+
return visibilityObserver;
|
|
79
|
+
}
|
|
14
80
|
const shouldYield = (() => {
|
|
15
81
|
const globalThisLWR = globalThis;
|
|
16
82
|
const { SSREnabled } = (globalThisLWR.LWR && globalThisLWR.LWR.env) || {};
|
|
@@ -112,7 +178,15 @@ export function init(rootModules, serverData = {}) {
|
|
|
112
178
|
const propsId = element.dataset.lwrPropsId;
|
|
113
179
|
// hydrate SSR'd components
|
|
114
180
|
if (propsId) {
|
|
115
|
-
|
|
181
|
+
// check if the element is a hydration visible island
|
|
182
|
+
if (shouldHydrateComponentWhenVisible(element)) {
|
|
183
|
+
// hydrate the island when visible
|
|
184
|
+
hydrateComponentOnVisible(element, ctor, serverData[propsId] || {});
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// hydrate the island immediately
|
|
188
|
+
hydrateComponentProxy(element, ctor, serverData[propsId] || {});
|
|
189
|
+
}
|
|
116
190
|
logOperationEnd({
|
|
117
191
|
id: INIT_MODULE,
|
|
118
192
|
specifier,
|
|
@@ -13,11 +13,90 @@ import { createElement } from 'lwc';
|
|
|
13
13
|
// Note: a build step uses these comments to strip the code for core.
|
|
14
14
|
// eslint-disable-next-line lwr/only-allowed-imports
|
|
15
15
|
import { hydrateComponent } from 'lwc';
|
|
16
|
+
|
|
17
|
+
// hydration directive + value constants
|
|
18
|
+
// must align with the constants in @lwrjs/shared-utils/src/html-meta.ts
|
|
19
|
+
export const HYDRATE_DIRECTIVE = 'lwr:hydrate';
|
|
20
|
+
export const HYDRATE_VISIBLE_VALUE = 'visible';
|
|
16
21
|
function hydrateComponentProxy(customElement, Ctor, props) {
|
|
17
22
|
hydrateComponent(customElement, Ctor, props);
|
|
18
23
|
}
|
|
19
24
|
// </hydrateComponentProxy>
|
|
20
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Hydrate the custom element only when it is visible.
|
|
28
|
+
* @param customElement - The custom element to hydrate
|
|
29
|
+
* @param ctor - The constructor of the custom element
|
|
30
|
+
* @param props - The properties of the custom element
|
|
31
|
+
*/
|
|
32
|
+
function hydrateComponentOnVisible(customElement, ctor, props) {
|
|
33
|
+
// Use IntersectionObserver for visibility-based hydration
|
|
34
|
+
const observer = createVisibilityObserver();
|
|
35
|
+
|
|
36
|
+
// add the element to the pending hydrations and observe it
|
|
37
|
+
pendingHydrations.set(customElement, {
|
|
38
|
+
ctor,
|
|
39
|
+
props
|
|
40
|
+
});
|
|
41
|
+
observer.observe(customElement);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// store component metadata for pending hydrations
|
|
45
|
+
const pendingHydrations = new Map();
|
|
46
|
+
|
|
47
|
+
// store the visibility observer so that we don't create a new one each time
|
|
48
|
+
let visibilityObserver;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Determines if a component should be hydrated when it becomes visible in the viewport.
|
|
52
|
+
* This requires IntersectionObserver to be available and the hydrate directive to be set to 'visible'.
|
|
53
|
+
* @param element - The element to check for visibility-based hydration
|
|
54
|
+
* @returns True if the component should be hydrated when visible, false otherwise
|
|
55
|
+
*/
|
|
56
|
+
function shouldHydrateComponentWhenVisible(element) {
|
|
57
|
+
return 'IntersectionObserver' in globalThis && element.getAttribute(HYDRATE_DIRECTIVE) === HYDRATE_VISIBLE_VALUE;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create an intersection observer for hydrating islands when visible, if one doesn't already exist.
|
|
62
|
+
* @returns An intersection observer that will hydrate the island when visible
|
|
63
|
+
*/
|
|
64
|
+
function createVisibilityObserver() {
|
|
65
|
+
// return the existing observer if it already exists
|
|
66
|
+
if (visibilityObserver) return visibilityObserver;
|
|
67
|
+
|
|
68
|
+
// create a new observer if it doesn't already exist
|
|
69
|
+
visibilityObserver = new IntersectionObserver((entries, observer) => {
|
|
70
|
+
// for each observed element, check if it is intersecting with the viewport
|
|
71
|
+
entries.forEach(entry => {
|
|
72
|
+
// if intersecting, hydrate the island
|
|
73
|
+
if (entry.isIntersecting) {
|
|
74
|
+
const element = entry.target;
|
|
75
|
+
|
|
76
|
+
// stop observing the element
|
|
77
|
+
observer.unobserve(element);
|
|
78
|
+
|
|
79
|
+
// get the hydration data for the element
|
|
80
|
+
const hydrationData = pendingHydrations.get(element);
|
|
81
|
+
if (hydrationData) {
|
|
82
|
+
// delete the hydration data for the element
|
|
83
|
+
pendingHydrations.delete(element);
|
|
84
|
+
const {
|
|
85
|
+
ctor,
|
|
86
|
+
props
|
|
87
|
+
} = hydrationData;
|
|
88
|
+
// hydrate the island
|
|
89
|
+
hydrateComponentProxy(element, ctor, props);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}, {
|
|
94
|
+
root: null,
|
|
95
|
+
// adds a buffer to the intersection observer to hydrate the island slightly before it enters the viewport
|
|
96
|
+
rootMargin: '100px'
|
|
97
|
+
});
|
|
98
|
+
return visibilityObserver;
|
|
99
|
+
}
|
|
21
100
|
const shouldYield = (() => {
|
|
22
101
|
const globalThisLWR = globalThis;
|
|
23
102
|
const {
|
|
@@ -143,7 +222,14 @@ export function init(rootModules, serverData = {}) {
|
|
|
143
222
|
|
|
144
223
|
// hydrate SSR'd components
|
|
145
224
|
if (propsId) {
|
|
146
|
-
|
|
225
|
+
// check if the element is a hydration visible island
|
|
226
|
+
if (shouldHydrateComponentWhenVisible(element)) {
|
|
227
|
+
// hydrate the island when visible
|
|
228
|
+
hydrateComponentOnVisible(element, ctor, serverData[propsId] || {});
|
|
229
|
+
} else {
|
|
230
|
+
// hydrate the island immediately
|
|
231
|
+
hydrateComponentProxy(element, ctor, serverData[propsId] || {});
|
|
232
|
+
}
|
|
147
233
|
logOperationEnd({
|
|
148
234
|
id: INIT_MODULE,
|
|
149
235
|
specifier,
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.20.
|
|
7
|
+
"version": "0.20.2",
|
|
8
8
|
"homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@locker/sandbox": "0.25.3",
|
|
37
|
-
"@lwrjs/shared-utils": "0.20.
|
|
37
|
+
"@lwrjs/shared-utils": "0.20.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@lwrjs/types": "0.20.
|
|
40
|
+
"@lwrjs/types": "0.20.2",
|
|
41
41
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
42
42
|
"@rollup/plugin-sucrase": "^5.0.2",
|
|
43
43
|
"@rollup/plugin-terser": "^0.4.4",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"volta": {
|
|
71
71
|
"extends": "../../../package.json"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "be82aca54f9a3b6cd18a4aac86f2f96c0184a930"
|
|
74
74
|
}
|